<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[ruby - Kiprosh Blogs]]></title><description><![CDATA[On our blog, we share our culture, processes, insights on scaling web & mobile apps, our learnings, etc. Our aim is to share and contribute back to the community.]]></description><link>https://blog.kiprosh.com/</link><image><url>https://blog.kiprosh.com/favicon.png</url><title>ruby - Kiprosh Blogs</title><link>https://blog.kiprosh.com/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Tue, 21 Apr 2026 18:08:05 GMT</lastBuildDate><atom:link href="https://blog.kiprosh.com/tag/ruby/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Type-checking in Ruby 3 using RBS]]></title><description><![CDATA[RBS is a new syntax language for dynamic typing introduced in Ruby 3 which can be used to describe the data types in a Ruby class. It can be used with a type-checker like Steep to do type checking.]]></description><link>https://blog.kiprosh.com/type-checking-in-ruby-3-using-rbs/</link><guid isPermaLink="false">63e7b26a66f7f232230623fb</guid><category><![CDATA[ruby3]]></category><category><![CDATA[rbs]]></category><category><![CDATA[ruby]]></category><category><![CDATA[rails]]></category><category><![CDATA[steep]]></category><category><![CDATA[type-checking]]></category><category><![CDATA[dynamic-typing]]></category><dc:creator><![CDATA[Navaneeth Krishnan P]]></dc:creator><pubDate>Wed, 05 Jul 2023 07:17:29 GMT</pubDate><content:encoded><![CDATA[<p>Ruby 3 has introduced a new syntax language for dynamic typing called RBS. In short, it is a language that we can use to describe the data types used in a Ruby class. We can define the data type of variables and the return type of methods used in a class using RBS.</p><!--kg-card-begin: markdown--><h2 id="why-do-we-need-type-checking">Why do we need type-checking?</h2>
<!--kg-card-end: markdown--><p>Since Ruby is a dynamically typed language, we don&apos;t need to define the data type of the variables we are using. Ruby automatically assigns a type based on the variable&apos;s value at runtime. Let&apos;s take the below class as an example.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby"># basic_math.rb

class BasicMath
  def initialize(num1, num2)
    @num1 = num1
    @num2 = num2
  end

  def first_less_than_second?
    @num1 &lt; @num2
  end

  def add
    @num1 + @num2
  end
end
</code></pre>
<!--kg-card-end: markdown--><p>If we run the below code it will return <code>false</code> as expected since integer <code>1000</code> is not less than integer <code>2</code>.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">puts BasicMath.new(1000, 2).first_less_than_second?
 
=&gt; false
</code></pre>
<!--kg-card-end: markdown--><p>We did not define that <code>num1</code> and <code>num2</code> are integers. Ruby interpreter automatically assigned <code>integer</code> type to them at runtime. And the method <code>first_less_than_second?</code> checks if its first argument is less than the second one. But let&apos;s call the same method with strings as parameters.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">puts BasicMath.new(&apos;1000&apos;, &apos;2&apos;).first_less_than_second?
 
=&gt; true
</code></pre>
<!--kg-card-end: markdown--><p>Here the output is <code>true</code> because <code>num1</code> and <code>num2</code> are strings. But for this method we expected the output to be <code>false</code>. This error happened because we missed doing a string-to-integer conversion on the parameters. A type-checking system will help us identify such errors.</p><!--kg-card-begin: markdown--><h2 id="how-to-write-an-rbs-file">How to write an RBS file?</h2>
<!--kg-card-end: markdown--><p>Let&apos;s write an RBS file for the above <code>BasicMath</code> class.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby"># basic_math.rbs

class BasicMath
  @num1: Integer
  @num2: Integer

  def initialize: (Integer num1, Integer num2) -&gt; void
  def first_less_than_second?: -&gt; bool
  def add: -&gt; Integer
end
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li>For the variables <code>@num1</code> and <code>@num2</code>, we define its type using the <code>:</code> symbol.</li>
<li>For methods, we write the method name followed by a <code>:</code> symbol and then specify the types of each method argument inside the <code>()</code> bracket. Then we define the return type using a <code>-&gt;</code> symbol.</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="how-to-run-an-rbs-file">How to run an RBS file?</h2>
<!--kg-card-end: markdown--><p>We only defined the structure of the <code>BasicMath</code> class in our RBS file. We still need to run it against a type checker to verify if the types are correct in our class. RBS is only a language that defines the structure of a class. It cannot do type-checking on its own. Let&apos;s use a popular type checker called <code>steep</code> to test our class against the RBS file.</p><!--kg-card-begin: markdown--><h2 id="setup-steep-gem">Setup steep gem</h2>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li>Install steep gem using <code>gem install steep</code>.</li>
<li>Run <code>steep init</code> to generate the configuration file for steep.</li>
<li>This will generate a configuration file called <code>Steepfile</code>.</li>
<li>Add the following code to the <code>Steepfile</code>.</li>
</ul>
<pre><code class="language-ruby">target :app do
  check &quot;lib&quot;
  signature &quot;sig&quot;

  library &quot;set&quot;, &quot;pathname&quot;
end
</code></pre>
<ul>
<li>Add the directory of the <code>.rb</code> files in <code>check</code> option and directory of <code>.rbs</code> files in <code>signature</code> option.</li>
<li>In this example, I am adding <code>.rb</code> files in <code>lib</code> directory and <code>.rbs</code> files in <code>sig</code> directory.</li>
<li>Make sure the file names of the respective <code>.rb</code> and <code>.rbs</code> files are the same.</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="type-checking-using-steep">Type-checking using Steep</h2>
<!--kg-card-end: markdown--><p>Run steep using the command <code>steep check</code>. This will perform type-checking on all the <code>Ruby</code> files in <code>lib</code> directory, using their respective <code>RBS</code> files in <code>sig</code> directory.<br>If the type-checking passes it will return the below message.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">steep check

# Type checking files:

....................................................................................

No type error detected. &#x1FAD6;
</code></pre>
<!--kg-card-end: markdown--><p>If there is any mismatch in the types, it will throw an error message. For example, in <code>BasicMath</code> class, let&apos;s change the return value of <code>first_less_than_second?</code> to a <code>String</code>. In our RBS file, we have defined that the output of this method will be a <code>Boolean</code>.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby"># lib/basic_math.rb

class BasicMath
  def initialize(num1, num2)
    @num1 = num1
    @num2 = num2
  end

  def first_less_than_second?
    @num1 &lt; @num2
    &apos;yes&apos;
  end

  def add
    @num1 + @num2
  end
end
</code></pre>
<!--kg-card-end: markdown--><p>Now if we run <code>steep check</code>, it will return an error message. This is because according to the RBS definition <code>first_less_than_second?</code> method is expected to return a <code>Boolean</code> value.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">steep check

# Type checking files:

.......................................................................F............

lib/rbs_test.rb:7:6: [error] Cannot allow method body have type `::String` because declared as type `bool`
&#x2502;   ::String &lt;: bool
&#x2502;     ::String &lt;: (true | false)
&#x2502;       ::String &lt;: true
&#x2502;
&#x2502; Diagnostic ID: Ruby::MethodBodyTypeMismatch
&#x2502;
&#x2514;   def first_less_than_second?
        ~~~~~~~~~~~~~~~~~~~~~~~

Detected 1 problem from 1 file
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="typeprof">TypeProf</h2>
<!--kg-card-end: markdown--><p>TypeProf is a type analysis tool that comes built-in with Ruby 3. It can automatically detect data types in a class and return a rough RBS file for it.<br>Just run the command <code>typeprof &lt;filename&gt;</code> and it will return a rough RBS file. Let&apos;s try running the <code>typeprof</code> command on the above <code>BasicMath</code> class.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">typeprof lib/basic_math.rb
</code></pre>
<!--kg-card-end: markdown--><p>This will return the following result.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby"># TypeProf 0.21.3

# Classes
class BasicMath
  @num1: untyped
  @num2: untyped

  def initialize: (untyped num1, untyped num2) -&gt; void
  def first_less_than_second?: -&gt; untyped
  def add: -&gt; untyped
end
</code></pre>
<!--kg-card-end: markdown--><p>If we check the returned RBS code, we can see that TypeProf assigned <code>untyped</code> as the data type in some places. This is because TypeProf couldn&apos;t determine the correct data type at those places. But it did return a skeletal structure for the RBS file which we can edit according to the way we want. TypeProf is still an experimental tool, so it doesn&apos;t always return an accurate RBS file.</p><!--kg-card-begin: markdown--><h2 id="conclusion">Conclusion</h2>
<!--kg-card-end: markdown--><p>We saw how we can write an RBS file for a class and do type-checking using <code>Steep</code>. We also saw how we can use <code>TypeProf</code> to generate a rough RBS file for a class. Since RBS allows us to add type definitions, it can help in catching type errors in our code. So it is a good practice to write RBS for our Ruby code.</p><!--kg-card-begin: markdown--><h2 id="references">References</h2>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li><a href="https://www.honeybadger.io/blog/ruby-rbs-type-annotation/">honeybadger - Understanding RBS, Ruby&apos;s new Type Annotation System</a></li>
<li><a href="https://blog.appsignal.com/2021/01/27/rbs-the-new-ruby-3-typing-language-in-action.html">appsignal - RBS: A New Ruby 3 Typing Language in Action</a></li>
<li><a href="https://medium.com/whitespectre/an-exploration-of-rbs-by-ruby-is-it-production-ready-c1eb86530154">Medium - An Exploration of RBS by Ruby: Is it Production-Ready?</a></li>
<li><a href="https://github.com/soutaro/steep">steep gem</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[All about "Data" Simple Immutable Value Objects in Ruby 3.2]]></title><description><![CDATA[Ruby 3.2 adds Data to define simple immutable value objects, which are designed to be small, self-contained, and represent a single concept.]]></description><link>https://blog.kiprosh.com/ruby-3-2-data-simple-immutable-value-objects/</link><guid isPermaLink="false">640496e666f7f232230627d6</guid><category><![CDATA[ruby3]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Data]]></category><category><![CDATA[Value Object]]></category><category><![CDATA[ruby]]></category><dc:creator><![CDATA[Vishal Jain]]></dc:creator><pubDate>Fri, 24 Mar 2023 07:30:50 GMT</pubDate><content:encoded><![CDATA[<p>In Ruby 3.2, a new class <code>Data</code> was introduced as a way to define <a href="https://github.com/ruby/ruby/pull/6353">simple immutable value objects</a>. A <a href="https://martinfowler.com/bliki/ValueObject.html">value object</a> is a type of object that represents a value in a program, such as a point in 2D space or a date. The main advantage of value objects is that they are easy to understand, simple to use, and can improve the readability and maintainability of code. The proposal to add <code>Data</code> class was accepted by Matz on the <a href="https://bugs.ruby-lang.org/issues/16122#note-49">Ruby forum here</a>.</p><h3 id="how-does-it-work">How does it work?</h3><p>Using the newly defined class <code>Data</code> we can create a simple immutable object. These objects are designed to be small, self-contained, and represent a single concept in your application. <br>The class definition includes the name of the class, as well as a list of instance variables that the object will contain. Here&apos;s an example:</p><pre><code class="language-ruby">class Point &lt; Data.define(:x, :y)
end

# Can be initialized using Positional or Keyword arguments
point = Point.new(3, 4) OR Point.new(x: 3, y: 4) OR Point[3, 4] OR Point[x: 3, y: 4]
=&gt; #&lt;data Point x=3, y=4&gt;

# But using both Positional and Keyword arguments together will not work
Point.new(2, y: 3)
=&gt; `new`: wrong number of arguments (given 2, expected 0) (ArgumentError)</code></pre><p>Once an instance of the object is created, its instance variables cannot be changed. This makes it easy to understand about the object&apos;s state and prevents bugs that can arise from unexpected changes to the object.</p><p>However, if we need to change any one instance variable by keeping the other variable same we can do that using <code>with</code> method. </p><pre><code class="language-ruby">point2 = point.with(x: 10)
=&gt; #&lt;data Point x=10, y=4&gt;</code></pre><p>Since <code>Data</code> objects are immutable, <code>with</code> method creates a copy of the object to update the arguments. <br><sub>Note: If the <code>with</code> method is called with no arguments, the receiver is returned as-is and no new copy is created.</sub></p><p><code>Data.define</code> also accepts an optional block that can be used to define custom methods for the immutable object. These methods can provide additional functionality to the object without compromising its immutability.</p><pre><code class="language-ruby">class Point &lt; Data.define(:x, :y)
  def distance_from_origin
    Math.sqrt(x**2 + y**2)
  end
end

point = Point.new(3, 4)
=&gt; #&lt;data Point x=3, y=4&gt;

point.distance_from_origin
=&gt; 5.0
</code></pre><h3 id="why-not-struct">Why not Struct?</h3><p>While <code><a href="https://ruby-doc.org/3.2.1/Struct.html">Struct</a></code> can also be used to define objects, there are some reasons why <code>Data</code> might be a better choice in certain situations.</p><p>First, <code>Data</code> provides some safety checks that <code>Struct</code> does not. For example, <code>Data</code> prevents an object from being created with a missing argument, while <code>Struct</code> allows this. This can help to prevent bugs and improve code safety.</p><p>Here&apos;s an example:</p><pre><code class="language-ruby">Measure = Struct.new(:amount, :unit)

measure = Measure.new(30) # This works, but will fail with Data
=&gt; #&lt;struct Measure amount=30, unit=nil&gt;</code></pre><pre><code class="language-ruby">Point.new(x: 3) # Missing argument will raise error for Data
=&gt; `initialize&apos;: missing keyword: :y (ArgumentError)</code></pre><p>In this example, we&apos;ve defined a <code>Measure</code> struct that contains two instance variables: <code>amount</code> and <code>unit</code>. However, when we create a new instance of the object, we only provide one argument. This is allowed by <code>Struct</code>, but it can lead to bugs if the code assumes that all the instance variables are present.</p><p>Second, <code>Data</code> is a more explicit way of defining simple immutable objects. The <code>Data</code> class makes it clear that the object is intended to be immutable, while with <code>Struct</code> it&apos;s less clear.</p><h3 id="final-thoughts">Final Thoughts</h3><p><code>Data</code> is a powerful tool for defining simple immutable objects in Ruby 3.2. While it may not be suitable for all situations, its safety, performance, and clarity benefits make it a strong choice in many cases. By understanding how <code>Data</code> works and its tradeoffs compared to other techniques, such as <code>Struct</code>, you can make an informed decision about when to use it in your own code.</p><h3 id="references">References</h3><ol><li><a href="https://github.com/ruby/ruby/pull/6353">Add Data class implementation: Simple immutable value object PR#6353</a></li><li><a href="https://github.com/ruby/ruby/pull/6766">Add copy with changes functionality for Data objects PR#6766</a></li><li><a href="https://docs.ruby-lang.org/en/3.2/Data.html">https://docs.ruby-lang.org/en/3.2/Data.html</a></li><li><a href="https://bugs.ruby-lang.org/issues/16122">https://bugs.ruby-lang.org/issues/16122</a></li></ol>]]></content:encoded></item><item><title><![CDATA[Ruby 3.2.0 enhances Regexp performance and security with ReDoS protections]]></title><description><![CDATA[The release of Ruby 3.2.0 brings significant improvements to Regexp performance and security, including enhanced protection against ReDoS attacks.]]></description><link>https://blog.kiprosh.com/ruby-3-2-0-introduce/</link><guid isPermaLink="false">6379fd193cff3c20e60974d8</guid><category><![CDATA[ruby 3.2.0]]></category><category><![CDATA[performance]]></category><category><![CDATA[security]]></category><category><![CDATA[regex]]></category><category><![CDATA[ruby]]></category><dc:creator><![CDATA[Krishna Singh]]></dc:creator><pubDate>Wed, 01 Mar 2023 08:45:54 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h2 id="what-is-redos">What is ReDoS?</h2>
<p>Regular expression Denial of Service (ReDoS) is a security vulnerability that can occur in a regular expression (regex) when the regex is applied to a long string. This attack is designed to make a system or network unavailable to its intended users.</p>
<h3 id="an-example-occurrence-of-a-redos">An example occurrence of a ReDoS</h3>
<ol>
<li>
<p>Imagine that a website has a form that accepts user input and uses a regex to validate the input. The regex is designed to only allow alphanumeric characters in the input, so it looks like this: <code>/^[a-zA-Z0-9]+$/</code>.</p>
</li>
<li>
<p>An attacker could potentially craft a string of input that consists of a very long sequence of characters, such as this:<br>
<code>&apos;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&apos;</code>.</p>
</li>
<li>
<p>When this string is passed through the regex, it will take a very long time to validate, potentially causing the system to become unresponsive or crash. This would prevent legitimate users from accessing the website, effectively denying them service.</p>
</li>
<li>
<p>To prevent this attack, it is important to use regexes that are designed to be efficient and not susceptible to ReDoS attacks. This can involve using certain regex constructs and patterns that are known to be efficient, and avoiding certain constructs and patterns that can cause regexes to be slow or vulnerable to ReDoS attacks. It is also important to test regexes for efficiency and security before deploying them to the production environment.</p>
</li>
</ol>
<h2 id="how-to-prevent-redos-in-ruby-320">How to prevent ReDoS in Ruby &lt; 3.2.0 ?</h2>
<p>To prevent ReDoS attacks in a Ruby on Rails application, there are several steps that you can take:</p>
<ol>
<li>
<p>Avoid using the <code>match</code> method in your regexes, as this method can be slow and can lead to ReDoS attacks. Instead, use the <code>scan</code> or <code>grep</code> methods, which are faster and more efficient.</p>
</li>
<li>
<p>Avoid using regexes with nested quantifiers such as <code>(a+)*</code> or with unbounded repetitions such as <code>a*</code> or <code>a+</code> in your Rails application. These types of regexes can be very slow and can lead to ReDoS attacks.</p>
</li>
<li>
<p>Use the <code>/o</code> option in your regexes to prevent them from being compiled multiple times. This can improve the efficiency of your regexes and can prevent ReDoS attacks.</p>
</li>
<li>
<p>Test your regexes for efficiency and security before deploying them in a production environment. This can help you identify any potential ReDoS vulnerabilities and can allow you to fix them before they can be exploited by an attacker.</p>
</li>
</ol>
<h2 id="regexp-improvements-introduced-in-ruby-320-to-prevent-redos">Regexp improvements introduced in Ruby 3.2.0 to prevent ReDoS.</h2>
<p>Ruby 3.2.0 introduced two improvements that significantly mitigate ReDoS.</p>
<h4 id="improved-regexp-matching-algorithm">Improved Regexp matching algorithm</h4>
<p>Since Ruby 3.2, the matching algorithm for Regexp has been significantly enhanced by using a memoization technique:</p>
<ol>
<li>
<p>This technique improves the performance of regexp matching by allowing most regexp matches to be completed in linear time.</p>
</li>
<li>
<p>This means that the time it takes to complete a regexp match will be directly proportional to the length of the input string, and will not increase exponentially as the input string gets longer.</p>
</li>
<li>
<p>This can help prevent ReDoS attacks, which are a type of security vulnerability that can occur when a regexp is applied to a long string of input.</p>
</li>
<li>
<p>Also, this optimization may consume memory proportional to the input length for each match. This means that the amount of memory used by the optimization will increase as the length of the input string increases.</p>
</li>
<li>
<p>This should not cause any practical problems because the memory allocation is usually delayed, and a normal regexp match should consume at most 10 times as much memory as the input length.</p>
</li>
</ol>
<p><strong>Example:-</strong></p>
<ul>
<li><strong>With <code>3.1.0</code></strong></li>
</ul>
<pre><code class="language-ruby">  :001 &gt; require &apos;benchmark&apos;
  =&gt; true
  :002 &gt; Benchmark.realtime { /^a*b?a*$/ =~ &quot;a&quot; * 50000 + &quot;x&quot; }
  =&gt; 24.171763999998802
</code></pre>
<ul>
<li><strong>With <code>3.2.0</code></strong></li>
</ul>
<pre><code class="language-ruby">  :001 &gt; require &apos;benchmark&apos;
  =&gt; true
  :002 &gt; Benchmark.realtime { /^a*b?a*$/ =~ &quot;a&quot; * 50000 + &quot;x&quot; }
  =&gt; 0.007867999986046925
</code></pre>
<h4 id="preventing-redos-attacks-with-regexp-timeouts">Preventing ReDoS attacks with regexp timeouts</h4>
<p>This allows you to specify a timeout for regexp matching. Two different APIs that can be used to set a timeout for regexp matching are:</p>
<ol>
<li>
<h4 id="regexptimeout"><code>Regexp.timeout=</code></h4>
<p>This is the process-global configuration of timeout for regexp matching. It allows you to specify a timeout that will apply to all regexp matches in your Ruby application. For example, you can use it like this:</p>
<pre><code class="language-ruby">  # Set a timeout of 1 second for regexp matching
  Regexp.timeout = 1.0

  regexp = Regexp.new(&quot;^[a-zA-Z0-9]+$&quot;)

  # Perform a regexp match
  regexp.scan(string)
</code></pre>
<p>In this example, the <code>Regexp.timeout</code> global configuration is set to 1 second. This means that any regexp match performed in the application will have a timeout of 1 second. If the match takes longer than 1 second to complete, it will raise a <code>Regexp::TimeoutError</code>.</p>
</li>
<li>
<h4 id="timeout-keyword-of-regexpnew"><code>timeout</code> keyword of <code>Regexp.new</code></h4>
<p>This API allows you to specify a timeout for a specific regexp object. This is useful when you want to use different timeout settings for different regexps in your application. For example, you can use it like this:</p>
<pre><code class="language-ruby">  # Create a regexp with a timeout of 1 second
  regexp = Regexp.new(&quot;^[a-zA-Z0-9]+$&quot;, timeout: 1.0)

  # Perform a regexp match
  regexp.scan(string)
</code></pre>
<p>In this example, the <code>timeout</code> keyword is used with the <code>Regexp.new</code> method to specify a timeout of 1 second for the regexp object. This means that any match performed with this regexp object will have a timeout of 1 second. If the match takes longer than 1 second to complete, it will raise a <code>Regexp::TimeoutError</code>.</p>
</li>
</ol>
<hr>
<h2 id="references">References</h2>
<ul>
<li><a href="https://www.ruby-lang.org/en/news/2022/12/25/ruby-3-2-0-released/">Ruby 3.2.0 Released</a></li>
<li><a href="https://blog.saeloun.com/2022/08/09/ruby-introduces-regexp-timeout.html">Ruby introduces Regexp.timeout</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Rails 7.1 supports infinite ranges for Active Record Validators]]></title><description><![CDATA[Rails 7.1 supports infinite ranges that can be used with Active Record length and inclusivity validators.]]></description><link>https://blog.kiprosh.com/rails-7-supports-infinite-ranges-for-active-record-validators/</link><guid isPermaLink="false">63898dd23cff3c20e6097973</guid><category><![CDATA[rails_7]]></category><category><![CDATA[rails]]></category><category><![CDATA[rails 7.1]]></category><category><![CDATA[active-record]]></category><category><![CDATA[ruby]]></category><category><![CDATA[Activerecord Validation]]></category><category><![CDATA[infinte-ranges]]></category><dc:creator><![CDATA[Athira Kadampatta]]></dc:creator><pubDate>Wed, 04 Jan 2023 07:30:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1638414154639-0fbc5bceb80f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGluZmluaXR5fGVufDB8fHx8MTY3MjAzMzg2Nw&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1638414154639-0fbc5bceb80f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fGluZmluaXR5fGVufDB8fHx8MTY3MjAzMzg2Nw&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Rails 7.1 supports infinite ranges for Active Record Validators"><p></p><!--kg-card-begin: markdown--><p>Ruby ranges are very handy and efficient when you want to work with a sequence of numbers or characters bound by start and end values. But we could come across situations where we want ranges that either have no end value or start value. Ruby 2.6 introduced the concept of an endless range and later Ruby 2.7 brought in the beginless range.</p>
<h3 id="infinite-ranges-in-ruby">Infinite Ranges in Ruby</h3>
<p>As mentioned in the <a href="https://ruby-doc.org/2.7.7/Range.html#class-Range-label-Beginless-2FEndless+Ranges">official Ruby docs</a>, a <code>beginless range</code> and <code>endless range</code> represents a semi-infinite range.<br>
A beginless range would look like <code>(..3)</code> Or <code>(...3)</code> while endless range looks like <code>(3..)</code> Or <code>(3...)</code> and effectively defines the maximum or minimum boundary of the range.</p>
<h3 id="infinite-ranges-in-rails">Infinite Ranges in Rails</h3>
<p>Infinite ranges are supported in Rails while fetching records from the database as seen below:</p>
<pre><code class="language-ruby">Loading development environment (Rails 7.1.0.alpha)
irb(main):013:0&gt; Event.where(date:  (..Time.now))
  Event Load (0.5ms)  SELECT &quot;events&quot;.* FROM &quot;events&quot; WHERE &quot;events&quot;.&quot;date&quot; &lt;= ? /* loading for pp */ LIMIT ?  [[&quot;date&quot;, &quot;2022-12-11 12:56:31.866828&quot;], [&quot;LIMIT&quot;, 11]]
=&gt;
[#&lt;Event:0x000055a3f20b22f0
  id: 1,                                                              
  date: Sun, 11 Dec 2022 12:32:57.255815000 UTC +00:00,               
  name: &quot;The December Concert&quot;,                                       
  description: &quot;The ultimate concert to end the year&quot;,                
  created_at: Sun, 11 Dec 2022 12:32:57.256643000 UTC +00:00,         
  updated_at: Sun, 11 Dec 2022 12:32:57.256643000 UTC +00:00&gt;,
 #&lt;Event:0x000055a3f20b2188
  id: 5,
  date: Mon, 31 Oct 2022 18:30:00.000000000 UTC +00:00,
  name: &quot;The Halloween Festival&quot;,
  description: &quot;The eve of All Saints Day&quot;,
  created_at: Sun, 11 Dec 2022 12:55:33.831731000 UTC +00:00,
  updated_at: Sun, 11 Dec 2022 12:56:21.981937000 UTC +00:00&gt;]
</code></pre>
<p>The above query fetched all the past event records, i.e the ones with the <code>date</code> value less than the current time.</p>
<pre><code class="language-ruby">Loading development environment (Rails 7.1.0.alpha)
irb(main):014:0&gt; Event.where(date:  (Time.now..))
  Event Load (0.2ms)  SELECT &quot;events&quot;.* FROM &quot;events&quot; WHERE &quot;events&quot;.&quot;date&quot; &gt;= ? /* loading for pp */ LIMIT ?  [[&quot;date&quot;, &quot;2022-12-11 12:56:43.438138&quot;], [&quot;LIMIT&quot;, 11]]
=&gt;
[#&lt;Event:0x000055a3f26616b0
  id: 2,
  date: Sat, 31 Dec 2022 18:30:00.000000000 UTC +00:00,
  name: &quot;The New Year Event&quot;,
  description: &quot;The first event of the year&quot;,
  created_at: Sun, 11 Dec 2022 12:34:23.292070000 UTC +00:00,
  updated_at: Sun, 11 Dec 2022 12:34:23.292070000 UTC +00:00&gt;,
 #&lt;Event:0x000055a3f26615e8
  id: 3,
  date: Thu, 12 Jan 2023 18:30:00.000000000 UTC +00:00,
  name: &quot;The Lohri Festival&quot;,
  description: &quot;The celebration of harvest festival&quot;,
  created_at: Sun, 11 Dec 2022 12:36:13.234757000 UTC +00:00,
  updated_at: Sun, 11 Dec 2022 12:37:13.423191000 UTC +00:00&gt;]
</code></pre>
<p>The above query fetched all the future event records, i.e the ones with the <code>date</code> value greater than the current time.</p>
<p>In addition to using ranges for fetching records, we generally use them in Rails with the Active Record length and inclusion validators.</p>
<h4 id="with-rails-710-alpha">With Rails &lt; 7.1.0-alpha</h4>
<p>If we use infinite range in a validator like:</p>
<pre><code class="language-ruby">class Event &lt; ApplicationRecord
  ...
  validates_length_of :name, in: (..30)
  ...
end
</code></pre>
<p>it throws an error as follows:</p>
<pre><code class="language-ruby">Loading development environment (Rails 7.0.1)
irb(main):001:0&gt; Event
Traceback (most recent call last):
        3: from (irb):1
        2: from app/models/event.rb:4:in `&lt;main&gt;&apos;
        1: from app/models/event.rb:7:in `&lt;class:Event&gt;&apos;
RangeError (cannot get the minimum of beginless range)
</code></pre>
<p>The only way to validate is by passing <code>maximum</code> or <code>minimum</code> value to the <code>validates</code> method as follows:</p>
<pre><code class="language-ruby">validates_length_of :name, maximum: 30, allow_blank: true
</code></pre>
<h4 id="with-rails-710-alpha">With Rails &gt;= 7.1.0-alpha</h4>
<p>With the two PRs that were merged recently - <a href="https://github.com/rails/rails/pull/45123">PR#45123</a> and <a href="https://github.com/rails/rails/pull/45138">PR#45138</a>, it is possible to pass beginless/endless ranges to the length and inclusivity validators with the <code>in/within</code> options.</p>
<pre><code class="language-ruby">  # Allows date to be either today&apos;s or any future date but not a past date
  validates_inclusion_of :date, in: -&gt; { (Date.today..) } 
</code></pre>
<pre><code class="language-ruby">  # Allows name to be nil or of a max length of 30 characters
  validates_length_of :name, in: (..30)

  # Mandates description to be of minimum 10 characters with no max limit
  validates_length_of :description, in: (10..)
</code></pre>
<p><em>Please note: These changes are available in the alpha phase of Rails 7.1.0 at the time of writing this article. To get the 7.1.0-alpha version, you have to specify the rails main branch in Gemfile -</em></p>
<pre><code class="language-ruby">    # Gemfile
    gem &apos;rails&apos;, github: &apos;rails/rails&apos;, branch: &apos;main&apos;
</code></pre>
<h3 id="references">References</h3>
<ul>
<li><a href="https://github.com/rails/rails/pull/45138">Support infinite ranges for LengthValidators :in/:within options</a></li>
<li><a href="https://github.com/rails/rails/pull/45123">Add beginless range support to clusivity</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Introduction to Ractors in Ruby 3]]></title><description><![CDATA[Ruby 3 has introduced an experimental feature called Ractors.  In this article, let's learn more about Ractor with example usage. ]]></description><link>https://blog.kiprosh.com/ruby-3-introduction-to-ractors/</link><guid isPermaLink="false">635aa2b13cff3c20e6096712</guid><category><![CDATA[ruby3]]></category><category><![CDATA[concurrency]]></category><category><![CDATA[rails]]></category><category><![CDATA[ruby]]></category><dc:creator><![CDATA[Navaneeth Krishnan P]]></dc:creator><pubDate>Wed, 30 Nov 2022 07:39:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1631375937044-6dd5beac01d2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDEzfHxjaGlwfGVufDB8fHx8MTY2Nzc5Nzc4OQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1631375937044-6dd5beac01d2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDEzfHxjaGlwfGVufDB8fHx8MTY2Nzc5Nzc4OQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Introduction to Ractors in Ruby 3"><p><br>Ruby 3 has introduced an experimental feature called Ractors(previously known as Guilds). Ractor is an actor model abstraction for Ruby. It allows us to execute many code blocks parallelly. Before going into Ractors, let&apos;s discuss what an actor model is.</p><!--kg-card-begin: markdown--><h3 id="what-is-an-actor-model">What is an actor model?</h3>
<!--kg-card-end: markdown--><p>In the <a href="https://www.developer.com/design/down-and-dirty-understanding-the-actor-model/">actor model</a>, everything is an actor, like everything is an object in the object-oriented model. Each actor is isolated from other actors. So an actor cannot access the state of another actor, which prevents multiple processes from modifying the same data in concurrent systems. Actors interact with the outside environment using messages. Due to these reasons, the actor model is preferred in concurrent computational systems. Ractor is an implementation of the actor model in ruby.</p><figure class="kg-card kg-image-card"><img src="https://blog.kiprosh.com/content/images/2022/11/actor_and_object_oriented_models.png" class="kg-image" alt="Introduction to Ractors in Ruby 3" loading="lazy" width="1876" height="714" srcset="https://blog.kiprosh.com/content/images/size/w600/2022/11/actor_and_object_oriented_models.png 600w, https://blog.kiprosh.com/content/images/size/w1000/2022/11/actor_and_object_oriented_models.png 1000w, https://blog.kiprosh.com/content/images/size/w1600/2022/11/actor_and_object_oriented_models.png 1600w, https://blog.kiprosh.com/content/images/2022/11/actor_and_object_oriented_models.png 1876w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><h3 id="creating-a-ractor">Creating a Ractor</h3>
<!--kg-card-end: markdown--><p>A Ractor can be created using <code>Ractor.new { }</code>.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; first_ractor = Ractor.new { puts &apos;Hello world! I am a ractor&apos; }
&lt;internal:ractor&gt;:267: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
Hello world! I am a ractor
 =&gt; #&lt;Ractor:#2 (irb):1 terminated&gt;
</code></pre>
<!--kg-card-end: markdown--><p>A Ractor can be also created using a name.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; first_ractor = Ractor.new(name: &apos;ractor_1&apos;) { nil }
 =&gt; #&lt;Ractor:#2 ractor_1 (irb):1 terminated&gt; 
3.0.0 :002 &gt; first_ractor.name
 =&gt; &quot;ractor_1&quot; 
</code></pre>
<!--kg-card-end: markdown--><p>Ractors cannot access objects created outside their scope.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; animal = &apos;Elephant&apos;
 =&gt; &quot;Elephant&quot; 
3.0.0 :002 &gt; Ractor.new { puts &quot;#{animal} is a mammal&quot; }
Traceback (most recent call last):
        5: from /Users/user_name/.rvm/rubies/ruby-3.0.0/bin/irb:23:in `&lt;main&gt;&apos;
        4: from /Users/user_name/.rvm/rubies/ruby-3.0.0/bin/irb:23:in `load&apos;
        3: from /Users/user_name/.rvm/rubies/ruby-3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `&lt;top (required)&gt;&apos;
        2: from (irb):2:in `&lt;main&gt;&apos;
        1: from &lt;internal:ractor&gt;:267:in `new&apos;
ArgumentError (can not isolate a Proc because it accesses outer variables (animal).)
</code></pre>
<!--kg-card-end: markdown--><p>But we can pass outside objects as parameters to a ractor.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; animal = &apos;Elephant&apos;
 =&gt; &quot;Elephant&quot; 
3.0.0 :002 &gt; Ractor.new animal do |a|
3.0.0 :003 &gt;   puts &quot;#{a} is a mammal&quot;
3.0.0 :004 &gt; end
Elephant is a mammal
 =&gt; #&lt;Ractor:#2 (irb):2 terminated&gt; 
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="messaging-in-ractors">Messaging in Ractors</h3>
<!--kg-card-end: markdown--><p>Ractors communicate using a messaging system.<br>There are two types of messaging:</p><!--kg-card-begin: markdown--><h4 id="1-push-type-messaging">1. Push-type messaging</h4>
<ul>
<li>In this type, the sender knows the receiver of the message. But the receiver does not know the sender.</li>
<li>This is done using <code>send()</code> method to send the message and <code>receive</code> method to receive the message.</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; receiver = Ractor.new do
3.0.0 :002 &gt;   message = Ractor.receive
3.0.0 :003 &gt;   puts &quot;received message is #{message}&quot;
3.0.0 :004 &gt; end
 =&gt; #&lt;Ractor:#2 (irb):1 blocking&gt; 
3.0.0 :005 &gt; message = &apos;Hi!&apos;
 =&gt; &quot;Hi!&quot; 
3.0.0 :006 &gt; receiver.send(message)
received message is Hi!
 =&gt; #&lt;Ractor:#2 (irb):1 terminated&gt; 
</code></pre>
<!--kg-card-end: markdown--><p>Here the receiver has an active incoming port and it will remain active until it receives a message.<br></p><!--kg-card-begin: markdown--><h4 id="2-pull-type-messaging">2. Pull-type messaging</h4>
<ul>
<li>In this type, the sender does not know the receiver. But the receiver knows from which sender it will receive the message.</li>
<li>This is done using <code>yield()</code> method to send the message and <code>take</code> method to receive the message.</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; sender = Ractor.new do
3.0.0 :002 &gt;   message = &apos;Hi!&apos;
3.0.0 :003 &gt;   Ractor.yield(message)
3.0.0 :004 &gt; end
 =&gt; #&lt;Ractor:#2 (irb):1 blocking&gt; 
3.0.0 :005 &gt; message = sender.take
 =&gt; &quot;Hi!&quot; 
3.0.0 :006 &gt; puts &quot;received message is #{message}&quot;
received message is Hi!
 =&gt; nil
</code></pre>
<!--kg-card-end: markdown--><p>Here the sender has an active outgoing port and it will remain active until the message is received. If <code>yield</code> method is not specified, then the last return object is returned.</p><!--kg-card-begin: markdown--><h3 id="object-sharing">Object sharing</h3>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>For thread safety, only immutable objects are regarded as shareable objects in ractors. We can check if an object is shareable or not using <code>Ractor.shareable?()</code> method. Some examples of shareable objects are <code>numbers</code>, <code>boolean</code>, <code>modules</code>, <code>classes</code>, <code>other ractors</code>.<br>
<code>Strings</code> are unsharable since they are mutable. <code>.freeze</code> method can be called on the string to make it immutable and thereby shareable.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; Ractor.shareable?(&apos;I am a string&apos;)
 =&gt; false 
3.0.0 :002 &gt; Ractor.shareable?(&apos;I am a string&apos;.freeze)
 =&gt; true 
</code></pre>
<!--kg-card-end: markdown--><p>We can also make an object shareable using <code>Ractor.make_shareable()</code> method.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; mutable_object = &apos;I am unshareable&apos;
 =&gt; &quot;I am unshareable&quot; 
3.0.0 :002 &gt; Ractor.shareable?(mutable_object)
 =&gt; false 
3.0.0 :003 &gt; Ractor.make_shareable(mutable_object)
 =&gt; &quot;I am unshareable&quot; 
3.0.0 :004 &gt; Ractor.shareable?(mutable_object)
 =&gt; true 
</code></pre>
<!--kg-card-end: markdown--><p>If unshareable objects are sent via ractors, a deep copy of the object is first created and then this deep copy is sent.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; receiver = Ractor.new do
3.0.0 :002 &gt;   message = receive
3.0.0 :003 &gt;   puts message.object_id
3.0.0 :004 &gt; end
 =&gt; #&lt;Ractor:#2 (irb):1 blocking&gt; 
3.0.0 :005 &gt; mutable_object = &apos;I am unshareable&apos;
 =&gt; &quot;I am unshareable&quot; 
3.0.0 :006 &gt; puts mutable_object.object_id
260
 =&gt; nil 
3.0.0 :007 &gt; receiver.send(mutable_object)
 =&gt; #&lt;Ractor:#2 (irb):1 blocking&gt; 
280
</code></pre>
<!--kg-card-end: markdown--><p>In the above example, <code>object_id</code> of the <code>mutable_object</code> changed at the receiver. It is because a deep copy of the <code>mutable_object</code> was created and sent. We can prevent sending deep copies by directly moving an unshareable object to a ractor. This is done by adding <code>move: true</code> parameter in <code>send()</code> method. But the object becomes inaccessible outside the ractor.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; receiver = Ractor.new do
3.0.0 :002 &gt;   message = receive
3.0.0 :003 &gt;   puts message
3.0.0 :004 &gt; end
 =&gt; #&lt;Ractor:#2 (irb):1 blocking&gt; 
3.0.0 :005 &gt; mutable_object = &apos;I am unshareable&apos;
 =&gt; &quot;I am unshareable&quot; 
3.0.0 :006 &gt; puts mutable_object.inspect
&quot;I am unshareable&quot;
 =&gt; nil 
3.0.0 :007 &gt; receiver.send(mutable_object, move: true)
I am unshareable
 =&gt; #&lt;Ractor:#2 (irb):1 terminated&gt; 
3.0.0 :008 &gt; puts mutable_object.inspect
Traceback (most recent call last):
        5: from /Users/user_name/.rvm/rubies/ruby-3.0.0/bin/irb:23:in `&lt;main&gt;&apos;
        4: from /Users/user_name/.rvm/rubies/ruby-3.0.0/bin/irb:23:in `load&apos;
        3: from /Users/user_name/.rvm/rubies/ruby-3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `&lt;top (required)&gt;&apos;
        2: from (irb):8:in `&lt;main&gt;&apos;
        1: from (irb):8:in `method_missing&apos;
Ractor::MovedError (can not send any methods to a moved object)
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="examplemovie-seat-booking-using-ractors">Example - Movie seat booking using ractors</h3>
<!--kg-card-end: markdown--><p>Let us assume that there are 50 seats for a movie. We can define a ractor <code>seats</code> that will provide movie seats on a first come first serve basis.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :001 &gt; seats = Ractor.new {
3.0.0 :002 &gt;   seat_no = 1
3.0.0 :003 &gt;   50.times do
3.0.0 :004 &gt;     Ractor.yield &quot;Your seat number is #{seat_no}&quot;
3.0.0 :005 &gt;     seat_no += 1
3.0.0 :006 &gt;   end
3.0.0 :007 &gt;   &apos;No more seats&apos;
3.0.0 :008 &gt; }
 =&gt; #&lt;Ractor:#2 (irb):1 blocking&gt; 
</code></pre>
<!--kg-card-end: markdown--><p>Now if a person needs a ticket he can call the <code>take</code> method in <code>seats</code> ractor and it will provide a seat.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :009 &gt; ticket1 = Ractor.new seats do |seats|
3.0.0 :010 &gt;   seat = seats.take
3.0.0 :011 &gt;   puts seat
3.0.0 :012 &gt; end
Your seat number is 1
 =&gt; #&lt;Ractor:#3 (irb):9 terminated&gt; 
3.0.0 :013 &gt; ticket2 = Ractor.new seats do |seats|
3.0.0 :014 &gt;   seat = seats.take
3.0.0 :015 &gt;   puts seat
3.0.0 :016 &gt; end
Your seat number is 2
 =&gt; #&lt;Ractor:#4 (irb):13 terminated&gt; 
3.0.0 :017 &gt; ticket3 = Ractor.new seats do |seats|
3.0.0 :018 &gt;   seat = seats.take
3.0.0 :019 &gt;   puts seat
3.0.0 :020 &gt; end
Your seat number is 3
 =&gt; #&lt;Ractor:#5 (irb):17 terminated&gt; 
</code></pre>
<!--kg-card-end: markdown--><p>This way many users can book seats concurrently using ractors. An outgoing port is created in the <code>seats</code> ractor using <code>yield</code> method for each seat. The last return value in the block <code>&apos;No more seats&apos;</code> is returned if <code>take</code> method is called after all the 50 tickets are sold. If the take method is called again we get a <code>port closed</code> error.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">3.0.0 :xxx &gt; ticket51 = Ractor.new seats do |seats|
3.0.0 :xxx &gt;     seat = seats.take
3.0.0 :xxx &gt;     puts seat
3.0.0 :xxx &gt; end
No more seats
 =&gt; #&lt;Ractor:#x (irb):xx terminated&gt; 
3.0.0 :xxx &gt; ticket52 = Ractor.new seats do |seats|
3.0.0 :xxx &gt;     seat = seats.take
3.0.0 :xxx &gt;     puts seat
3.0.0 :xxx &gt; end
#&lt;Thread:0x0000000120059850 run&gt; terminated with exception (report_on_exception is true):
&lt;internal:ractor&gt;:694:in `take&apos;: The outgoing-port is already closed (Ractor::ClosedError)
        from (irb):xx:in `block in &lt;main&gt;&apos;
 =&gt; #&lt;Ractor:#x (irb):xx terminated&gt; 
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="conclusion">Conclusion</h3>
<!--kg-card-end: markdown--><p>Ractors can be very useful if we want to add concurrency to our code. Each ractor maintains a private state and they use message sharing for communication. So it helps us to avoid concurrency issues like race conditions. But ractors are still an experimental feature in Ruby 3. So it&apos;s best to not use them for large-scale concurrent applications. Future versions of ruby might polish this feature more and make it a permanent feature. Until then we can use ractors to write small-scale concurrent code.</p><!--kg-card-begin: markdown--><h3 id="references">References</h3>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ul>
<li><a href="https://ruby-doc.org/core-3.0.0/Ractor.html">ruby-doc.org - Ractor</a></li>
<li><a href="https://blog.appsignal.com/2022/08/24/an-introduction-to-ractors-in-ruby.html">AppSignal - An Introduction to Ractors in Ruby</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Selenium 4 CDP Integration With Capybara]]></title><description><![CDATA[Let's explore how we can use Chrome Dev Tools Protocols(CDP) with Capybara. We will look at headers, cookies, and cache manipulation using CDP]]></description><link>https://blog.kiprosh.com/selenium-4-cdp-integration-with-capybara/</link><guid isPermaLink="false">62fb32133cff3c20e6094c73</guid><category><![CDATA[selenium]]></category><category><![CDATA[selenium4 cdp]]></category><category><![CDATA[ruby]]></category><category><![CDATA[capybara]]></category><dc:creator><![CDATA[Darshan Shah]]></dc:creator><pubDate>Tue, 04 Oct 2022 07:30:15 GMT</pubDate><content:encoded><![CDATA[<p>The <a href="https://chromedevtools.github.io/devtools-protocol/">Chrome DevTools Protocol (CDP)</a> is a tool for inspecting and debugging Chrome-based browsers. CDP APIs offer abilities to alter network requests, get cookies, delete cache, and do many other operations. These APIs are also useful for automation testing when we need to perform <code>DevTools</code> related operations.</p><p>The Selenium CDP <code>network</code> object offers methods to monitor the page&apos;s network activity. In this article, we&apos;ll look at some operations that can be performed on headers, cookies, and browser cache using <a href="https://www.selenium.dev/documentation/webdriver/bidirectional/chrome_devtools/">Selenium 4 CDP</a> with Capybara and Ruby.</p><p>To get started, we will install the <a href="https://rubygems.org/gems/selenium-webdriver">selenium-webdriver</a> and the <a href="https://rubygems.org/gems/selenium-devtools">selenium-devtools</a> gems. The version of these gems must match the version of the Chrome browser.</p><p>To configure <code>Selenium 4 CDP</code> with <code>Capybara</code>, we add the following steps:</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">require &apos;capybara/rspec&apos;
require &apos;selenium-webdriver&apos;
require &apos;selenium/devtools&apos;

Selenium::WebDriver::Chrome::Service.driver_path = &apos;C:\Users\chromedriver.exe&apos;
Capybara.default_driver = :selenium
Capybara.register_driver :selenium do |app|
  Capybara::Selenium::Driver.new(
    app,
    browser: :chrome
  )
end
</code></pre>
<!--kg-card-end: markdown--><h3 id="headers">Headers</h3><p>Network tracking must be enabled in order to carry out operations related to the network. This can be done by using the network object&apos;s <code>enable</code> method. After this, we can attach the header to the HTTP request by passing the header values to the <code>set_extra_http_headers</code> method provided by the <code>network</code> object. The code would look like:</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">it &apos;adds header to HTTP request&apos; do
  # Activate network settings
  page.driver.browser.devtools.network.enable

  # Attach headers to the request using setExtraHTTPHeaders
  page.driver.browser.devtools.network.set_extra_http_headers(headers: {
    &apos;header_key_1&apos; =&gt; &apos;header_value_1&apos;,
    &apos;header_key_2&apos; =&gt; &apos;header_value_2&apos;
  })

  # Visit the desired page
  visit(&apos;https://desired-page-url.com&apos;)
end
</code></pre>
<!--kg-card-end: markdown--><h3 id="cookies">Cookies</h3><p>To attach the cookie to the website, visit the required page and pass the cookie parameters using <code>set_cookies</code> method. The code snippet would explain how to attach multiple cookies:</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">it &apos;adds cookie to a page&apos; do
  # Visit the desired page
  visit(&apos;https://desired-page-url.com&apos;)

  # Attach the cookie to the page
  page.driver.browser.devtools.network.set_cookies(cookies: [
    {
       name: &apos;cookie_1_name&apos;,
       value: &apos;cookie_1_value&apos;,
       domain: &apos;page domain&apos;
    },
    {
       name: &apos;cookie_2_name&apos;,
       value: &apos;cookie_2_value&apos;,
       domain: &apos;page domain&apos;
    }
  ])
end 
</code></pre>
<!--kg-card-end: markdown--><p><code>get_all_cookies</code> method is used to retrieve all the browser cookies.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">it &apos;gets all cookies present on the browser&apos; do
  # Visit the page
  visit(&apos;https://desired-page-url.com&apos;)
  # Retrieve all the browser cookies
  # page.driver.browser.devtools.network.get_all_cookies = &gt; {&quot;id&quot; =&gt; 5, &quot;result&quot; =&gt; {&quot;cookies&quot;=&gt;[{&quot;name&quot;=&gt;&quot;cookie_name&quot;, ...}]}}
  cookie_array = page.driver.browser.devtools.network.get_all_cookies[&apos;result&apos;][&apos;cookies&apos;]
end
</code></pre>
<!--kg-card-end: markdown--><p>To retrieve browser cookies for a specific page, <code>get_cookies</code> method can be used by passing the page URL.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">page.driver.browser.devtools.network.get_cookies(urls: [&apos;https://desired-page-url.com&apos;])
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="cache">Cache</h3>
<!--kg-card-end: markdown--><p>Before running an automation test it&apos;s always a good idea to clear the browser cache. This stops the browser from utilizing cached versions of websites while performing the tests. <code>Selenium 4 CDP</code> offers the option to <code>clear the browser cache</code> and <code>disable the browser cache</code> to prevent the browser from using the cached copy of the page.</p><p> To delete cache for a webpage the <code>clear_browser_cache</code> method is used.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">it &apos;clears browser cache&apos; do
  page.driver.browser.devtools.network.clear_browser_cache
end
</code></pre>
<!--kg-card-end: markdown--><p>The <code>set_cache_disabled</code> method is used to prevent the browser from using cache for a webpage. To use this method, we first enable the network setting similar to the header section. Then we call the <code>set_cache_disabled</code> method by passing the <code>cache_disabled</code> flag as true.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">it &apos;Disable Browser Cache&apos; do
  # Activate network settings
  page.driver.browser.devtools.network.enable
  # Disable broswer cache
  page.driver.browser.devtools.network.set_cache_disabled(cache_disabled: true)
  # Visit the page
  visit(&apos;https://desired-page-url.com&apos;)
end
</code></pre>
<!--kg-card-end: markdown--><p>This was just a quick rundown of how to use <code>DevTools</code> with <code>Capybara</code> and <code>Selenium Driver</code>. In our project, we intended to add a header to access the web-page, verify scenarios involving cookie presence, and carry out cache-related tasks. <code>Selenium 4 CDP</code> was helpful to us in achieving our objective, and we hope it will be for you as well.</p><!--kg-card-begin: markdown--><h3 id="references">References</h3>
<ul>
<li><a href="https://chromedevtools.github.io/devtools-protocol/tot/Network/">Chrome Dev Tools Protocol</a></li>
<li><a href="https://www.selenium.dev/documentation/webdriver/bidirectional/chrome_devtools/">Selenium 4 CDP</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Embedding of Scenario Outline with Data Table in Cucumber]]></title><description><![CDATA[Let's look at how to reduce the complexity of the automation script by combining the cucumber Data table and the scenario outline.]]></description><link>https://blog.kiprosh.com/parameterization-of-scenario-outline-with-data-table/</link><guid isPermaLink="false">6299984057483d085981c343</guid><category><![CDATA[cucumber]]></category><category><![CDATA[ruby]]></category><category><![CDATA[capybara]]></category><dc:creator><![CDATA[Darshan Shah]]></dc:creator><pubDate>Fri, 01 Jul 2022 07:30:25 GMT</pubDate><media:content url="https://blog.kiprosh.com/content/images/2022/06/Cucumber-Blog-image.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.kiprosh.com/content/images/2022/06/Cucumber-Blog-image.jpg" alt="Embedding of Scenario Outline with Data Table in Cucumber"><p>In one of our automation projects using <strong>Cucumber</strong> and <strong>Capybara</strong>, there was a use case where we needed to automate the process of submitting a multi-field form and checking the provided form details on the next page. We considered parameterization using <a href="https://www.tutorialspoint.com/cucumber/cucumber_data_tables.htm"><strong>Data Table</strong></a> and <a href="https://www.tutorialspoint.com/cucumber/cucumber_scenario_outline.htm"><strong>Scenario Outline</strong></a><strong> </strong>provided by Cucumber. In this article, we&apos;ll take a look at why and how we combined Scenario Outline with Data Table to optimize the feature file.<br><br>We began with <code>Scenario Outline</code> because we only had four fields: <code>First Name</code>, <code>Last Name</code>, <code>Password</code>, <code>Email</code>, and we wanted to run the same scenario with different datasets. However, we were later given a new requirement that included additional fields such as <code>Location</code>, <code>Age</code>, and <code>Phone Number</code>, increasing the number of input parameters. As a result, the scenario and step definitions became more complex. This is how we can write a test case, which can be easily extended for new field requirements. (For reference we have included only the first four fields in the examples)</p><figure class="kg-card kg-code-card"><pre><code class="language-gherkin">Scenario Outline: Validate User Registration Form
  Given I am on the registration page
  When I submit the form with the following details &quot;&lt;FirstName&gt;&quot;, &quot;&lt;LastName&gt;&quot;, &quot;&lt;Password&gt;&quot;, &quot;&lt;Email&gt;&quot;
  Then the details should be displayed on my profile page

  Examples:
    |FirstName |LastName |Password  |Email             |
    |John      |Doe      |testPass1 |jdoe@email.com    |
    |Anne      |Smith    |testPass2 |asmith@email.com  |
    |Mike      |Stewart  |testPass3 |mstewart@email.com|</code></pre><figcaption>Feature File</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-ruby">Given(&apos;I am on the registration page&apos;) do
  log &apos;I am on registration page&apos;
end

When(&apos;I submit the form with the following details {string}, {string}, {string}, {string}&apos;) do |first_name, last_name, password, email|
  # Storing the input parameters in instance variable to access it in subsequent steps
  @first_name = first_name
  @last_name = last_name
  @email = email

  # Fill the registration form
  fill_in(&apos;First Name&apos;, with: @first_name)
  fill_in(&apos;Last Name&apos;, with: @last_name)
  fill_in(&apos;Password&apos;, with: password)
  fill_in(&apos;Email&apos;, with: @email)
end

Then(&apos;the details should be displayed on my profile page&apos;) do
  expect(find(&apos;#firstname&apos;).value).to eq(@first_name)
  expect(find(&apos;#lastname&apos;).value).to eq(@last_name)
  expect(find(&apos;#email&apos;).value).to eq(@email)
end
</code></pre><figcaption>Step Definition</figcaption></figure><p>We considered using <code>Data Table</code> because we needed to send multiple parameters for a scenario. In contrast to <code>Scenario Outline</code>, <code>Data Table</code> passes all datasets in a single step, and to retrieve dataset details we had to loop through the data map. As an alternative to iterating through the data map, we could repeat the scenario as many times as the dataset.</p><figure class="kg-card kg-code-card"><pre><code class="language-gherkin">Scenario: Validate User Registration Form for User 1
  Given I am on the registration page
  When I submit the form with the following details
    |FirstName |LastName |Password  |Email         |
    |John      |Doe      |testPass1 |jdoe@email.com|
  Then the details should be displayed on my profile page

Scenario: Validate User Registration Form for User 2
  Given I am on the registration page
  When I submit the form with the following details
    |FirstName |LastName |Password  |Email           |
    |Anne      |Smith    |testPass2 |asmith@email.com|
  Then the details should be displayed on my profile page

Scenario: Validate User Registration Form for User 3
  Given I am on the registration page
  When I submit the form with the following details
    |FirstName |LastName |Password  |Email             |
    |Mike      |Stewart  |testPass3 |mstewart@email.com|
  Then the details should be displayed on my profile page
</code></pre><figcaption>Feature File</figcaption></figure><p>To avoid the repetition of the <code>Scenarios</code> we passed the <code>Reference Header</code> of the <code>Example Table</code> as a parameter to the <code>Data Table</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-gherkin">Scenario Outline: Validate User Registration Form
  Given I am on the registration page
  When I submit the form with the following details
  |FirstName  |LastName  |Password  |Email  |
  |&lt;FirstName&gt;|&lt;LastName&gt;|&lt;Password&gt;|&lt;Email&gt;|
  Then the details should be displayed on my profile page

  Examples:
    |FirstName |LastName |Password  |Email             |
    |John      |Doe      |testPass1 |jdoe@email.com    |
    |Anne      |Smith    |testPass2 |asmith@email.com  |
    |Mike      |Stewart  |testPass3 |mstewart@email.com|</code></pre><figcaption>Feature File</figcaption></figure><p>In the step definition, we retrieved the input parameter provided as the first row of the Data Map. We used the same hash in the subsequent steps by assigning it to an instance variable.</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">Given(&apos;I am on the registration page&apos;) do
  log &apos;I am on registration page&apos;
end

When(&apos;I submit the form with the following details&apos;) do |user_details_map|
  # Fetch the user detail passed as the first argument of the data map
  # user_details_map = { hashes: [{&quot;FirstName&quot;=&gt;&quot;John&quot;, &quot;LastName&quot;=&gt;&quot;Doe&quot;,
  # &quot;Password&quot;=&gt;&quot;testPass1&quot;, &quot;Email&quot;=&gt;&quot;jdoe@email.com&quot;}]}
  @user_detail = user_details_map.hashes[0]
  # Fill the registration form
  fill_in(&apos;First Name&apos;, with: @user_detail[&apos;FirstName&apos;])
  fill_in(&apos;Last Name&apos;, with: @user_detail[&apos;LastName&apos;])
  fill_in(&apos;Password&apos;, with: @user_detail[&apos;Password&apos;])
  fill_in(&apos;Email&apos;, with: @user_detail[&apos;Email&apos;])
end

Then(&apos;the details should be displayed on my profile page&apos;) do
  expect(find(&apos;#firstname&apos;).value).to eq(@user_detail[&apos;FirstName&apos;])
  expect(find(&apos;#lastname&apos;).value).to eq(@user_detail[&apos;LastName&apos;])
  expect(find(&apos;#email&apos;).value).to eq(@user_detail[&apos;email&apos;])
end</code></pre><figcaption>Step Definition</figcaption></figure><p>In the above example, the scenario will run three times as we have three rows in the Example Table. </p><p>By combining the scenario outline with the data table, we were able to DRY our code and make it more readable at the same time.</p><!--kg-card-begin: markdown--><h3 id="references">References</h3>
<ul>
<li><a href="https://cucumber.io/">Cucumber</a></li>
<li><a href="https://www.tutorialspoint.com/cucumber/cucumber_scenario_outline.htm">Cucumber - Scenario Outline</a></li>
<li><a href="https://www.tutorialspoint.com/cucumber/cucumber_data_tables.htm">Cucumber - Data Table</a></li>
<li><a href="https://javapointers.com/automation/cucumber/cucumber-data-tables-example-in-java">Cucumber Data Table Example In Java</a></li>
<li><a href="https://github.com/teamcapybara/capybara">Capybara</a></li>
<li><a href="https://www.pexels.com/photo/close-up-photo-of-programming-of-codes-546819/">Cover Image from Pexels by luis gomes</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Advanced features provided by the new debug gem in Ruby]]></title><description><![CDATA[Decoding features like record & replay debugging, postmortem debugging, and VSCode integrations are provided by the new debug gem.]]></description><link>https://blog.kiprosh.com/advanced-features-provided-by-new-debug-gem/</link><guid isPermaLink="false">62113c853861a521824e4401</guid><category><![CDATA[rails_7]]></category><category><![CDATA[debugging]]></category><category><![CDATA[ruby]]></category><category><![CDATA[ruby-on-rails]]></category><dc:creator><![CDATA[Manoj Saun]]></dc:creator><pubDate>Thu, 14 Apr 2022 09:54:08 GMT</pubDate><content:encoded><![CDATA[<p><code><a href="https://github.com/ruby/debug">debug</a></code> is Ruby&apos;s new default debugger included in Ruby 3.1. This new debugger has replaced <code><a href="https://rubygems.org/gems/byebug">byebug</a></code> in Rails 7. Not only does <code>debug</code> provide us with a wide range of functionality, but it also provides some advanced features.</p><p>In this article, we will explore and understand a few advanced features of <code>debug</code>.</p><p><strong>1. Seamless integration with VSCode</strong></p><p>Below steps can be followed to integrate <code>debug</code> gem in <a href="https://code.visualstudio.com/">VSCode</a>.</p><ul><li>Install extension <a href="https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg">VSCode rdbg Ruby Debugger - Visual Studio Marketplace</a> in your VSCode.</li><li>Open the file you want to debug in the VSCode.</li><li>Register the breakpoint by clicking on the line you want to set the breakpoint and press the <code>F9</code> key.</li><li>Start the debugging by pressing the <code>F5</code> key or by selecting <code>Start debugging</code> in the <code>Run</code> menu.</li></ul><p><strong>2. Postmortem debugging to debug the dead Ruby process</strong></p><p>When you debug a program and an exception is raised, the debugger will exit immediately. To avoid this, we can make use of the postmortem feature.</p><p>Let&apos;s say we have a simple program to calculate the percentage.</p><pre><code class="language-ruby">  # computation.rb
  def percentage(numerator, denominator)
    (numerator / denominator) * 100.0
  end

  # call the above function by passing the denominator as 0
  percentage(10, 0)</code></pre><p>When you run the program with the debugger and continue the debugging process, the debugger will suspend immediately throwing <code>ZeroDivisionError</code>.</p><pre><code class="language-bash">  # start the debugging using `rdbg` command
  19:22:35:~/Users/Examples  &gt;% rdbg computation.rb
  [1, 6] in computation.rb
  =&gt;   1| def percentage(numerator, denominator)
       2|   (numerator / denominator) * 100.0
       3| end
       4|
       5|
       6| percentage(10, 0)
  (rdbg)

  # continue the debugging process
  (rdbg) continue
  Traceback (most recent call last):
    2: from computation.rb:6:in `&lt;main&gt;&apos;
    1: from computation.rb:2:in `percentage&apos;
  computation.rb:2:in `/&apos;: divided by 0 (ZeroDivisionError)

  # debugging process gets suspended
  19:54:15:~/Users/Examples</code></pre><p>To remain in the debugging mode even after the exception is raised, we can use the postmortem feature. Run the program with the debugger, then set the configuration with the command <code>config set postmortem true</code>.</p><p>Start the program in debugging mode.</p><pre><code class="language-bash">  # start the debugging using `rdbg` command
  20:02:38:~/Users/Examples  &gt;% rdbg computation.rb
  [1, 6] in computation.rb
  =&gt;   1| def def percentage(numerator, denominator)
       2|   (numerator / denominator) * 100.0
       3| end
       4|
       5|
       6| percentage(10, 0)
 (rdbg)</code></pre><p>Enable the postmortem feature.</p><pre><code class="language-bash">  # command to enable postmortem
  (rdbg) config set postmortem true
  postmortem = true</code></pre><p>Continue the program and the exception will be raised, but the debugger won&apos;t be suspended.</p><pre><code class="language-bash">  # continue the debugging process
  (rdbg) continue    # continue command
  Enter postmortem mode with #&lt;ZeroDivisionError: divided by 0&gt;
    computation.rb:2:in `/&apos;
    computation.rb:2:in `percentage&apos;
    computation.rb:6:in `&lt;main&gt;&apos;

  # debugger is still active
    (rdbg:postmortem)</code></pre><p>Backtrace the issue with <code>bt</code> command.</p><pre><code class="language-bash">  # command to backtrace
  (rdbg:postmortem) bt
  =&gt;#0  [C] Integer#/ at computation.rb:2
    #1  Object#percentage(numerator=10, denominator=0) at computation.rb:2
    #2  &lt;main&gt; at computation.rb:6
  (rdbg:postmortem)</code></pre><p><strong>3. Supports record &amp; replay debugging</strong></p><p>This feature allows recording the execution information. Using this we can go back to the execution again by using <code>step back</code> command. This will help us to check the last state of the program before the breakpoint.</p><p>Let&apos;s take a simple example that calculates simple interest. Add a breakpoint at the start of the function.</p><pre><code class="language-ruby">  # computation.rb
  def calculate_simple_interest
    debugger

    puts &apos;Enter investment:&apos;
    investment = Integer(gets.chomp)

    puts &apos;Enter interest rate:&apos;
    interest_rate = Integer(gets.chomp)

    puts &apos;Enter number of years:&apos;
    time = Integer(gets.chomp)

    interest = (investment * interest_rate * time) / 100
    puts &quot;Interest amount is: #{interest}&quot;
  end

  # call to the above function
  calculate_simple_interest</code></pre><p>Run the program and start the debugging.</p><pre><code class="language-bash">  # start the debugging using `rdbg` command
  18:09:24:/Users/Examples  &gt;% rdbg computation.rb
  [1, 10] in computation.rb
  =&gt;   1| def calculate_simple_interest
       2|   debugger
       3|
       4|   puts &apos;Enter investment:&apos;
       5|   investment = Integer(gets.chomp)
       6|
       7|   puts &apos;Enter interest rate:&apos;
       8|   interest_rate = Integer(gets.chomp)
       9|
      10|   puts &apos;Enter number of years:&apos;
  =&gt;#0  &lt;main&gt; at computation.rb:1
  (rdbg)</code></pre><p>To start the recording, execute the command <code>record on</code>.</p><pre><code class="language-bash"># command to start the record
(rdbg) record on
Recorder for #&lt;Thread:0x00007fef0c85fb58 run&gt;: on (0 records)
(rdbg) </code></pre><p>Using <code>next</code> command go to the end of the program and check the values of all the variables using <code>info</code> command. You will be able to see the values of all four variables.</p><pre><code class="language-bash">  # command to move the breakpoint to the new line
  (rdbg) next
  [9, 17] in computation.rb
       9|
      10|   puts &apos;Enter number of years:&apos;
      11|   time = Integer(gets.chomp)
      12|
      13|   interest = (investment * interest_rate * time) / 100
  =&gt;  14|   puts &quot;Interest amount is: #{interest}&quot;
      15| end
      16|

  # command to list the values of the variables
  (rdbg) info
  %self = main
  investment = 12
  interest_rate = 12
  time = 12
  interest = 17
  (rdbg)</code></pre><p>Now, to go back to the last state, enter the command <code>step back</code>. Now you will only be able to see values of <code>investment</code>, <code>interest_rate</code> &amp; <code>time</code>.</p><pre><code class="language-bash">  # command to move to the previous state
  (rdbg) step back
  [replay] [8, 17] in computation.rb
  [replay]      8|   interest_rate = Integer(gets.chomp)
  [replay]      9|
  [replay]     10|   puts &apos;Enter number of years:&apos;
  [replay]     11|   time = Integer(gets.chomp)
  [replay]     12|
  [replay] =&gt;  13|   interest = (investment * interest_rate * time) / 100
  [replay]     14|   puts &quot;Interest amount is: #{interest}&quot;
  [replay]     15| end
  [replay]     16|
  [replay]     17| calculate_simple_interest

  # command to list the values of the variables
  (rdbg) info
  [replay] %self = main
  [replay] investment = 12
  [replay] interest_rate = 12
  [replay] time = 12
  [replay] interest = nil
  (rdbg)</code></pre><p>You can move back again to the previous state by entering the command <code>step back</code>. Now you will only be able to see the value of <code>investment</code> &amp; <code>interest_rate</code>.</p><pre><code class="language-bash">  # command to move to the previous state
  (rdbg) step back
  [replay] [6, 15] in computation.rb
  [replay]      6|
  [replay]      7|   puts &apos;Enter interest rate:&apos;
  [replay]      8|   interest_rate = Integer(gets.chomp)
  [replay]      9|
  [replay]     10|   puts &apos;Enter number of years:&apos;
  [replay] =&gt;  11|   time = Integer(gets.chomp)
  [replay]     12|
  [replay]     13|   interest = (investment * interest_rate * time) / 100
  [replay]     14|   puts &quot;Interest amount is: #{interest}&quot;
  [replay]     15| end

  # command to list the values of the variables
  (rdbg) info
  [replay] %self = main
  [replay] investment = 12
  [replay] interest_rate = 12
  [replay] time = nil
  [replay] interest = nil
  (rdbg)</code></pre><h2 id="comparing-the-performance-of-the-debug-gem-with-the-other-debuggers">Comparing the performance of the <code>debug</code> gem with the other debuggers</h2><p>We already have existing debuggers like <code>lib/debug.rb</code>, <code><a href="https://rubygems.org/gems/byebug">byebug</a></code>, <code><a href="https://rubygems.org/gems/debase">debase</a></code>, etc. The reason to use the new debugger is its performance.</p><p>Let&apos;s have a simple function to find the Fibonacci series.</p><pre><code class="language-ruby">def fibonacci(n)
  if n &lt; 0
    raise # breakpoint
  elsif n &lt; 2
    n
  else
    fibonacci(n - 1) + fibonacci(n - 2)
  end
end

# running above method in the irb
irb(main):010:0&gt; require &apos;benchmark&apos;
irb(main):011:0&gt; Benchmark.bm { |x| x.report{ fibonacci(35) } }</code></pre><p>Here are the benchmark scores in <code>seconds</code> for various debuggers for the above example.</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th style="text-align: left"></th>
<th style="text-align: center">Without Breakpoint (sec)</th>
<th style="text-align: center">With Breakpoint (sec)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">rdbg(debug.gem)</td>
<td style="text-align: center">0.92</td>
<td style="text-align: center">0.92</td>
</tr>
<tr>
<td style="text-align: left">Ruby &lt; 3.1</td>
<td style="text-align: center">0.93</td>
<td style="text-align: center">N/A</td>
</tr>
<tr>
<td style="text-align: left">RubyMine</td>
<td style="text-align: center">0.97</td>
<td style="text-align: center">22.6</td>
</tr>
<tr>
<td style="text-align: left">Byebug</td>
<td style="text-align: center">1.23</td>
<td style="text-align: center">75.15</td>
</tr>
<tr>
<td style="text-align: left">old lib/debug.rb</td>
<td style="text-align: center">221.88</td>
<td style="text-align: center">285.99</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>Looking at the comparison in the above benchmarks, we can clearly notice that the new debugger is much faster than the rest of the debuggers.</p><hr><h2 id="references"><strong>References</strong></h2><ul><li><a href="https://www.youtube.com/watch?v=XeWHrsp6nwo">Ruby&apos;s debug.gem functionality by Koichi Sasada</a></li></ul>]]></content:encoded></item><item><title><![CDATA[Rails 7 supports tracking of belongs_to association]]></title><description><![CDATA[Rails 7 is introducing two methods association_changed? and association_previously_changed? to track changes for the `belongs_to` association.]]></description><link>https://blog.kiprosh.com/rails-7-supports-tracking-of-belongs_to-association/</link><guid isPermaLink="false">61194bfee082a5667d04eb06</guid><category><![CDATA[rails_7]]></category><category><![CDATA[active-record]]></category><category><![CDATA[association]]></category><category><![CDATA[change-tracking-methods]]></category><category><![CDATA[ruby-on-rails]]></category><category><![CDATA[ruby]]></category><dc:creator><![CDATA[Supriya Laxman Medankar]]></dc:creator><pubDate>Wed, 06 Oct 2021 07:30:33 GMT</pubDate><content:encoded><![CDATA[<p>One of the most convenient features of <strong>Rails</strong> is the ability to track attribute changes. <strong>Rails 7</strong> is releasing soon and bringing in a lot of new features as a treat for developers. One of the many features that <strong>Rails 7</strong> is introducing, is that we can also track the associated object.</p><p>This <a href="https://github.com/rails/rails/pull/42751">Rails ActiveRecord specific PR</a> has added two methods for tracking the changes for the <code>belongs_to</code> association. In this article, we will discuss these two new methods with the help of examples.</p><h2 id="1-association_changed">1. <code>association_changed?</code></h2><blockquote>The <code>association_changed?</code> method tells if a different associated object has been assigned and the foreign key will be updated in the next save.</blockquote><p>Let&apos;s take an example of the following <code>Event</code> and <code>Organizer</code> model structure:</p><pre><code class="language-ruby">class Event
  belongs_to :organizer
end

class Organizer
  has_many :events
end</code></pre><p>Before <strong>Rails 7</strong>, one could only track the change in the target of a <code>belongs_to</code> association by keeping a track of its foreign key. Here&apos;s an example:</p><pre><code class="language-ruby">class Event
  belongs_to :organizer
  before_save :track_change
    
  private
    
  def track_change
    if organizer_id_changed?
      #track something
    end
  end
end</code></pre><p>The above example tracks a change in the foreign key (<code>organizer_id</code>), change in the foreign key accordingly means a change in the target of the <code>belongs_to</code> association.</p><p>In <strong>Rails 7</strong> with the <code>association_changed?</code> method, we can directly check if the associated object is updated or not.</p><pre><code class="language-ruby">class Event
  belongs_to :organizer
  before_save :track_change
    
  private
    
  def track_change
    if organizer_changed?
      #track something
    end
  end
end</code></pre><h2 id="2-association_previously_changed">2. <strong><code>association_previously_changed?</code></strong></h2><blockquote>The <code>association_previously_changed?</code> method tells if the previous save updated the association to reference a different associated object.</blockquote><p>So for the above example, we can also check if the <code>organizer</code> association had previously changed using the <code>organizer_previously_changed?</code> method which will return <code>true</code> if the <code>organizer</code> association was changed in the last save.</p><pre><code class="language-ruby">&gt; event.organizer
=&gt; #&lt;Organizer id: 1, name: &quot;Organization 1&quot;&gt;

&gt; event.organizer = Organizer.second
=&gt; #&lt;Organizer id: 2, name: &quot;Organization 2&quot;&gt;

&gt; event.organizer_changed?
=&gt; true

&gt; event.organizer_previously_changed?
=&gt; false

&gt; event.save!
=&gt; true

&gt; event.organizer_changed?
=&gt; false

&gt; event.organizer_previously_changed?
=&gt; true</code></pre><p>Similarly, we can track changes on removing the association:</p><pre><code class="language-ruby">&gt; event.organizer
=&gt; #&lt;Organizer id: 2, name: &quot;Organization 2&quot;&gt;

&gt; event.organizer = nil
=&gt; true

&gt; event.organizer_changed?
=&gt; true

&gt; event.save!
=&gt; true

&gt; event.organizer_previously_changed?
=&gt; true</code></pre><p>Hope these examples help you to understand the new tracking methods <strong>Rails 7</strong> is introducing. We can now track the actual <code>association</code> change instead of tracking the <code>id</code> of the associated object. </p><p>Thank you for reading! &#x2764;&#xFE0F;</p><h2 id="references-">References:</h2><p><br>1. <a href="https://edgeguides.rubyonrails.org/7_0_release_notes.html">Rails 7 release notes</a></p><p>2. <a href="https://github.com/rails/rails/pull/42751">PR - Add change tracking methods for <code>belongs_to</code> associations</a></p>]]></content:encoded></item><item><title><![CDATA[Side effects of Active Record's new feature #invert_where in Rails 7]]></title><description><![CDATA[The potential side effect of using the new Rails 7 method `invert_where` that inverts all scope conditions.]]></description><link>https://blog.kiprosh.com/side-effects-of-activerecords-new-feature-invert_where-in-rails-7/</link><guid isPermaLink="false">6119705ae082a5667d04ecc2</guid><category><![CDATA[rails_7]]></category><category><![CDATA[active-record]]></category><category><![CDATA[invert-where]]></category><category><![CDATA[ruby]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[scopes]]></category><dc:creator><![CDATA[Manoj Saun]]></dc:creator><pubDate>Wed, 22 Sep 2021 07:59:26 GMT</pubDate><media:content url="https://blog.kiprosh.com/content/images/2021/09/invert-where.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.kiprosh.com/content/images/2021/09/invert-where.png" alt="Side effects of Active Record&apos;s new feature #invert_where in Rails 7"><p>Rails 7 is introducing a new method <code>invert_where</code> that will invert all scope conditions. It allows us to invert an entire where clause instead of manually applying conditions.</p><p>We can either chain <code>invert_where</code> to a scope or to a <code>where</code> condition.</p><!--kg-card-begin: markdown--><pre><code class="language-ruby">class Account
  scope :active, -&gt; { where(active: true) }
end
</code></pre>
<!--kg-card-end: markdown--><pre><code class="language-ruby">Account.active.invert_where
=&gt; &quot;SELECT \&quot;accounts\&quot;.* FROM \&quot;accounts\&quot; WHERE \&quot;accounts\&quot;.\&quot;active\&quot; != 1&quot;</code></pre><pre><code class="language-ruby">Account.where(active: true).invert_where
=&gt; &quot;SELECT \&quot;accounts\&quot;.* FROM \&quot;accounts\&quot; WHERE \&quot;accounts\&quot;.\&quot;active\&quot; != 1&quot;</code></pre><h2 id="what-are-the-various-side-effects-of-using-invert_where">What are the various side effects of using <code>invert_where</code>?</h2><p><strong>1. The <code>invert_where</code> method inverts all the where clauses present in the query</strong></p><p>Let&apos;s say we create a scope to fetch inactive accounts using <code>invert_where</code>.</p><pre><code class="language-ruby">class Account
  scope :active, -&gt; { where(active: true) }
  scope :inactive, -&gt; { active.invert_where }
end</code></pre><p>Now, somewhere in the controller, we want to fetch all the inactive admin accounts so we may end up with a query as follows:</p><pre><code class="language-ruby">Account.where(role: &apos;admin&apos;).inactive</code></pre><p>We may think that the above query will return all the inactive admin accounts. Let&apos;s look at the SQL equivalent of the above code.</p><pre><code class="language-sql">=&gt; &quot;SELECT \&quot;accounts\&quot;.* FROM \&quot;accounts\&quot; WHERE NOT (\&quot;accounts\&quot;.\&quot;role\&quot; = &apos;admin&apos; AND \&quot;accounts\&quot;.\&quot;active\&quot; = 1)&quot;</code></pre><p>But instead, we received all the inactive non-admin accounts. The <code>invert_where</code> also inverted the clause <code>where(role: &apos;admin&apos;)</code> internally.</p><p><strong>2. The <code>invert_where</code> affects the default scope as well</strong></p><p>Continuing the above example, let us add a default scope in the Account model to filter archived accounts (soft-deleted accounts).</p><pre><code class="language-ruby">class Account
  default_scope { where(archived: false) }
  scope :active, -&gt; { where(active: true) }
end</code></pre><p>Now let us write a query to fetch inactive accounts using <code>invert_where</code>.</p><pre><code class="language-ruby">Account.active.invert_where</code></pre><p>The SQL equivalent of the above will be:</p><pre><code class="language-sql">=&gt; &quot;SELECT \&quot;accounts\&quot;.* FROM \&quot;accounts\&quot; WHERE NOT (\&quot;accounts\&quot;.\&quot;archived\&quot; = 0 AND \&quot;accounts\&quot;.\&quot;active\&quot; = 1)&quot;</code></pre><p>We can see that <code>invert_where</code> has also inverted the <code>default_scope</code>. We wanted to fetch inactive accounts which are not archived but instead received inactive accounts which are archived.</p><p>Using <code>default_scope</code> is quite dangerous in the model in the first place, using <code>invert_where</code> will increase the chances of bugs on top of that.</p><p><strong>3. Challenges we will face on chaining two or more scopes that contain <code>invert_where</code></strong></p><p>Let&apos;s say we have two scopes that contain <code>invert_where</code> as follows:</p><pre><code class="language-ruby">class Account
  scope :inactive, -&gt; { active.invert_where }
  scope :non_admin, -&gt; { admin.invert_where }
end</code></pre><p>Now we want to fetch the inactive non-admin accounts, so we chain the above two scopes together and write the query as follows:</p><pre><code class="language-ruby">Account.non_admin.inactive</code></pre><p>The SQL equivalent of the above is:</p><pre><code class="language-sql">=&gt; &quot;SELECT \&quot;accounts\&quot;.* FROM \&quot;accounts\&quot; WHERE NOT (\&quot;accounts\&quot;.\&quot;role\&quot; != &apos;admin&apos; AND \&quot;accounts\&quot;.\&quot;active\&quot; = 1)&quot;</code></pre><p>We can see that the above query will unexpectedly return all the inactive admin accounts.</p><h2 id="substitute-for-invert_where">Substitute for <code>invert_where</code></h2><p>Rails 7 is still in the alpha phase, <code>invert_where</code> &apos;s behavior might be changed once Rails 7 is officially released. For now, we can make use of the <code>NOT</code> condition to invert the individual <code>WHERE</code> clause.</p><p>Let us take the example from the first point and create a scope to fetch inactive accounts using the <code>NOT</code> clause.</p><pre><code class="language-ruby">class Account
  scope :active, -&gt; { where(active: true) }
  scope :inactive, -&gt; { where.not(active: true) }
end</code></pre><p>Now if we want to fetch all the inactive admin accounts, we can write the query as follows:</p><pre><code class="language-ruby">Account.where(role: &apos;admin&apos;).inactive</code></pre><p>The SQL equivalent of the above will be:</p><pre><code class="language-sql">=&gt; &quot;SELECT \&quot;accounts\&quot;.* FROM \&quot;accounts\&quot; WHERE \&quot;accounts\&quot;.\&quot;role\&quot; = &apos;admin&apos; AND \&quot;accounts\&quot;.\&quot;active\&quot; != 1&quot;</code></pre><p>The above query will return all the inactive admin accounts. This is the expected behavior we wanted and we achieved it using the <code>NOT</code> clause.</p><h2 id="conclusion">Conclusion</h2><p>One should cautiously use this proposed method <code>invert_where</code> otherwise we may end up with bugs that may be difficult to debug. What do you think? Do you agree or have any other suggestions?</p><p></p><p>Thank you for reading.</p><hr><p></p><h2 id="references"><strong>References</strong></h2><ul><li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-invert_where">ActiveRecord::QueryMethods#invert_where</a></li><li><a href="https://github.com/rails/rails/blob/main/activerecord/CHANGELOG.md">Ruby on Rails 7.0 Release Notes CHANGELOG.md</a></li></ul>]]></content:encoded></item><item><title><![CDATA[Speeding up Rails 7's Controller Actions using ActiveRecord's #load_async]]></title><description><![CDATA[load_async initiates a new thread to execute the query, resulting in reduced wait time. Loading starts even before an instance is referenced in the view.]]></description><link>https://blog.kiprosh.com/rails-7-activerecord-relation-load_async/</link><guid isPermaLink="false">60c5fd775e6fb60795614dec</guid><category><![CDATA[rails_7]]></category><category><![CDATA[active-record]]></category><category><![CDATA[ruby]]></category><category><![CDATA[controllers]]></category><category><![CDATA[performance]]></category><category><![CDATA[Ruby on Rails]]></category><dc:creator><![CDATA[Krishna Singh]]></dc:creator><pubDate>Tue, 22 Jun 2021 11:42:01 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1588127727253-e5f2faf4f541?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDN8fHJhaWx8ZW58MHx8fHwxNjI0MjcwMDUw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1588127727253-e5f2faf4f541?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDN8fHJhaWx8ZW58MHx8fHwxNjI0MjcwMDUw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Speeding up Rails 7&apos;s Controller Actions using ActiveRecord&apos;s #load_async"><p>Most of the time in a web application, a single API request consists of multiple database queries. For example:</p>
<pre><code class="language-ruby">class DashboardsController &lt; ApplicationController
  def dashboard
    @users = User.some_complex_scope.load_async
    @products = Product.some_complex_scope.load_async
    @library_files = LibraryFile.some_complex_scope.load_async
  end
end
</code></pre>
<p>Quoting snippet from <a href="https://github.com/rails/rails/pull/40037">load_async</a> PR description from rails repository.</p>
<blockquote>
<p>The queries are executed synchronously, which mostly isn&#x2019;t a huge concern. But, as the database grows larger in size, the response time of requests is getting longer. A significant part of the query time is often just I/O waits. Since these two queries are totally independent, ideally you could execute them in parallel, so that assuming that each take 50ms, the total query time would be 50ms rather than 100ms. In the above scenario, the queries can be executed asynchronously, and the time saved on the I/O waits can be reutilized for the next query execution.</p>
</blockquote>
<p>The <code>load_async</code> method will initiate a new thread to execute the query, which will reduce the wait time. If you use the usual lazy loading way in the controller (without the <code>load_async</code> method), the loading will not begin until the view references it. But in the case of <code>load_async</code>, loading will start before it is referenced in the view.</p>
<h2 id="configurationforload_async">Configuration for <code>load_async</code></h2>
<p>We need to set the value for <code>async_query_executor</code> in the environment configuration for <a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-load_async">load_async</a> to work. By default, Rails initializes <code>async_query_executor</code> to <code>nil</code>. The query runs in the foreground if <code>load_async</code> is called and executor is not defined. <code>async_query_executor</code> comes with two configuration options.</p>
<ol>
<li>
<p><code>:global_thread_pool</code> is used to create a common thread pool for all database connections.</p>
<pre><code class="language-ruby"># config/environments/development.rb

config.active_record.async_query_executor = :global_thread_pool
</code></pre>
</li>
<li>
<p><code>:multi_thread_pool</code> is used to create a unique thread pool for each database connection.</p>
<pre><code class="language-ruby"># config/environments/development.rb

config.active_record.async_query_executor = :multi_thread_pool
</code></pre>
</li>
</ol>
<hr>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/rails/rails/pull/41372">Implement Relation#load_async to schedule the query on the background thread pool</a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-load_async">ActiveRecord::Relation#load_async</a></li>
</ul>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[Everything You Need to know about Serialization in Ruby on Rails - Part III]]></title><description><![CDATA[We had previously talked about the Serialization formats and How Serialization is implemented for storing objects in the relational database in the first two parts of the blog series. This article focuses on the various Serializers that prepare and construct API transferable data in Ruby on Rails.]]></description><link>https://blog.kiprosh.com/serialization_in_ruby_on_rails_part_three/</link><guid isPermaLink="false">5f685b206a8b4e63f74d7048</guid><category><![CDATA[serialization-in-ruby-on-rails]]></category><category><![CDATA[serialization]]></category><category><![CDATA[rails]]></category><category><![CDATA[ruby]]></category><category><![CDATA[ruby-on-rails]]></category><category><![CDATA[api]]></category><category><![CDATA[json]]></category><category><![CDATA[serializers]]></category><category><![CDATA[api-serializers]]></category><dc:creator><![CDATA[Athira Kadampatta]]></dc:creator><pubDate>Fri, 18 Jun 2021 06:07:26 GMT</pubDate><media:content url="https://blog.kiprosh.com/content/images/2021/06/sayan-nath-cBmHIO-m_AU-unsplash-1--2-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.kiprosh.com/content/images/2021/06/sayan-nath-cBmHIO-m_AU-unsplash-1--2-.jpg" alt="Everything You Need to know about Serialization in Ruby on Rails - Part III"><p>We had previously talked about the <a href="https://blog.kiprosh.com/serialization_in_ruby_on_rails_part_one/">Serialization formats</a> and <a href="https://blog.kiprosh.com/serialization_in_ruby_on_rails_part_two/">How Serialization is implemented for storing objects in the relational database</a> in the first two parts of the blog series. This article focuses on the various Serializers that prepare and construct API transferable data in Ruby on Rails.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.kiprosh.com/serialization_in_ruby_on_rails_part_one/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Everything You Need to know about Serialization in Rails: Part I</div><div class="kg-bookmark-description">It was the day we were moving. I was observing how the &#x201C;Packers and Movers&#x201D;professionals packed our furniture. For example, the King size bed shown belowhad to be accommodated within a space of about 6-7 inches inside a van. While Ikept wondering how they&#x2019;d manage this, they dismantled the bed. A&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.kiprosh.com/favicon.png" alt="Everything You Need to know about Serialization in Ruby on Rails - Part III"><span class="kg-bookmark-author">Kiprosh Blogs</span><span class="kg-bookmark-publisher">Athira Kadampatta</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.kiprosh.com/content/images/2020/12/image-7-.png" alt="Everything You Need to know about Serialization in Ruby on Rails - Part III"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.kiprosh.com/serialization_in_ruby_on_rails_part_two"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Serialization in Ruby on Rails for Storage: Part II</div><div class="kg-bookmark-description">Rails framework allows complex objects to be stored in a DB column via the ActiveRecord::Serialization module.This article explains when and how to do it</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.kiprosh.com/favicon.png" alt="Everything You Need to know about Serialization in Ruby on Rails - Part III"><span class="kg-bookmark-author">Kiprosh Blogs</span><span class="kg-bookmark-publisher">Athira Kadampatta</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.kiprosh.com/content/images/2021/03/Serialization-image-last-3-.jpg" alt="Everything You Need to know about Serialization in Ruby on Rails - Part III"></div></a></figure><!--kg-card-begin: markdown--><h2 id="serializationinrailsforapis">Serialization in Rails for APIs</h2>
<!--kg-card-end: markdown--><p>What is so special about the data rendered by the APIs? Do we really need to serialize it? The short answer is &quot;YES&quot;, we need to send a standardized and formatted response to the API consumers in a way they would be able to parse. Otherwise, it would be difficult for the API consumers to make sense of the transferred data. Since most of the API consumers are JavaScript frameworks, the preferred serialization format for APIs in Rails is JSON.</p><h3 id="how-to-send-json-response-from-rails-apis">How to send JSON response from Rails APIs?</h3><p>For simple use cases, we could use <code>render json: object</code> in the API controller action. Rails internally applies <code>to_json</code> to render the response. Alternatively, we could apply the <code>JSON.dump</code> or <code>to_json</code> methods on our objects and send it in the response.</p><p>Let&apos;s say we are building a basic e-learning site. So a user has many courses, which in turn has many topics and each topic has many assignments. </p><figure class="kg-card kg-image-card"><img src="https://blog.kiprosh.com/content/images/2021/06/image--26-.png" class="kg-image" alt="Everything You Need to know about Serialization in Ruby on Rails - Part III" loading="lazy" width="434" height="503"></figure><p>To send all courses of the authenticated user, our <code>courses_controller</code>&apos;s <code>index</code> action looks somewhat like this:</p><pre><code class="language-ruby">class Api::V1::CoursesController &lt; ApplicationController
  def index
    render json: current_user.courses
  end
end</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-JSON">[
    {
        &quot;id&quot;: 24,
        &quot;title&quot;: &quot;Javascript Training&quot;,
        &quot;description&quot;: &quot;Master JavaScript With The Most Complete Course On The Market.&quot;,
        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.856Z&quot;,
        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.856Z&quot;
    },
    {
        &quot;id&quot;: 25,
        &quot;title&quot;: &quot;Ruby on Rails&quot;,
        &quot;description&quot;: &quot; Learn to make innovative web apps with Ruby on Rails and unleash your creativity&quot;,
        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.886Z&quot;,
        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.886Z&quot;
    }
]</code></pre><figcaption>Response for courses#index.json</figcaption></figure><p>This is so easy, isn&apos;t it? Or is it? Just think of it, would the API consumer expect us to simply send the title and description of the course? What about the topics, or assignments?</p><p>Let us use the <code>include</code> option for the <code>to_json</code> method to render the nested objects.</p><pre><code class="language-ruby">class Api::V1::CoursesController &lt; ApplicationController
  def index
    render json: current_user.courses.to_json(include: { topics: { include: :assignments } })
  end
end</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-JSON">[
    {
        &quot;id&quot;: 24,
        &quot;title&quot;: &quot;Javascript Training&quot;,
        &quot;description&quot;: &quot;Master JavaScript With The Most Complete Course On The Market.&quot;,
        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.856Z&quot;,
        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.856Z&quot;,
        &quot;topics&quot;: [
            {
                &quot;id&quot;: 159,
                &quot;name&quot;: &quot;Variables and flow control&quot;,
                &quot;description&quot;: &quot;You will learn the fundamentals of JavaScript&quot;,
                &quot;course_id&quot;: 24,
                &quot;created_at&quot;: &quot;2021-05-22T17:31:59.908Z&quot;,
                &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.908Z&quot;,
                &quot;assignments&quot;: [
                    {
                        &quot;id&quot;: 157,
                        &quot;description&quot;: &quot;Declare a variable called myName and initialize it with a value, on the same line.&quot;,
                        &quot;topic_id&quot;: 159,
                        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.962Z&quot;,
                        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.962Z&quot;
                    }
                ]
            }
        ]
    },
    {
        &quot;id&quot;: 25,
        &quot;title&quot;: &quot;Ruby on Rails&quot;,
        &quot;description&quot;: &quot; Learn to make innovative web apps with Ruby on Rails and unleash your creativity&quot;,
        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.886Z&quot;,
        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.886Z&quot;,
        &quot;topics&quot;: [
            {
                &quot;id&quot;: 160,
                &quot;name&quot;: &quot;Serializers in Rails&quot;,
                &quot;description&quot;: &quot;You will learn all the API Serializers in Ruby on Rails&quot;,
                &quot;course_id&quot;: 25,
                &quot;created_at&quot;: &quot;2021-05-22T17:31:59.920Z&quot;,
                &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.920Z&quot;,
                &quot;assignments&quot;: [
                    {
                        &quot;id&quot;: 158,
                        &quot;description&quot;: &quot;Render a basic json response from an action controller&quot;,
                        &quot;topic_id&quot;: 160,
                        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.975Z&quot;,
                        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.975Z&quot;
                    }
                ]
            }
        ]
    }
]</code></pre><figcaption>Response for courses#index.json</figcaption></figure><p>There it is!! We got all the required data in JSON format.<br>Observe the response. Don&apos;t you think sending the <code>created_at</code>, <code>updated_at</code>, <code>user_id</code> etc. in each array were perhaps not needed? Okay, maybe that&apos;s not a big deal - we can limit the response attributes by passing &#xA0;<code>only</code> / <code>except</code> options to the <code>to_json</code> method.</p><h3 id="why-do-we-need-json-serializers">Why do we need JSON serializers?</h3><p>Well, the real world is not so straightforward as you see. The API consumer would now expect conditional responses, like <strong>not</strong> sending the assignments data until the corresponding topic has been completed.</p><p>We&apos;d then probably create a hash of the course details and render the hash as a JSON object.</p><pre><code class="language-ruby">class Api::V1::CoursesController &lt; ApplicationController
  def index
    array_of_courses = current_user.courses.map(&amp;:attributes)
    current_user.courses.each_with_index do |course, index|
      array_of_topics = course.topics.map(&amp;:attributes)
      course.topics.each_with_index do |topic, index|
        array_of_topics[index][&quot;is_completed&quot;] = topic.is_completed?(current_user)

        array_of_topics[index][&quot;assignments&quot;] = topic.assignments if topic.is_completed?(current_user)
      end
      array_of_courses[index][&quot;topics&quot;] = array_of_topics
    end
    render json: array_of_courses
  end
end</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-JSON">[
    {
        &quot;id&quot;: 24,
        &quot;title&quot;: &quot;Javascript Training&quot;,
        &quot;description&quot;: &quot;Master JavaScript With The Most Complete Course On The Market.&quot;,
        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.856Z&quot;,
        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.856Z&quot;,
        &quot;topics&quot;: [
            {
                &quot;id&quot;: 159,
                &quot;name&quot;: &quot;Variables and flow control&quot;,
                &quot;description&quot;: &quot;You will learn the fundamentals of JavaScript&quot;,
                &quot;course_id&quot;: 24,
                &quot;created_at&quot;: &quot;2021-05-22T17:31:59.908Z&quot;,
                &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.908Z&quot;,
                &quot;is_completed&quot;: true,
                &quot;assignments&quot;: [
                    {
                        &quot;id&quot;: 157,
                        &quot;description&quot;: &quot;Declare a variable called myName and initialize it with a value, on the same line.&quot;,
                        &quot;topic_id&quot;: 159,
                        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.962Z&quot;,
                        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.962Z&quot;
                    }
                ]
            }
        ]
    },
    {
        &quot;id&quot;: 25,
        &quot;title&quot;: &quot;Ruby on Rails&quot;,
        &quot;description&quot;: &quot; Learn to make innovative web apps with Ruby on Rails and unleash your creativity&quot;,
        &quot;created_at&quot;: &quot;2021-05-22T17:31:59.886Z&quot;,
        &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.886Z&quot;,
        &quot;topics&quot;: [
            {
                &quot;id&quot;: 160,
                &quot;name&quot;: &quot;Serializers in Rails&quot;,
                &quot;description&quot;: &quot;You will learn all the API Serializers in Ruby on Rails&quot;,
                &quot;course_id&quot;: 25,
                &quot;created_at&quot;: &quot;2021-05-22T17:31:59.920Z&quot;,
                &quot;updated_at&quot;: &quot;2021-05-22T17:31:59.920Z&quot;,
                &quot;is_completed&quot;: false
            }
        ]
    }
]</code></pre><figcaption>Response for api/v1/courses#index</figcaption></figure><p>Doesn&apos;t this look very messy? Even with refactoring, the above code is difficult to maintain and has cluttered our controller. For such scenarios instead of manipulating data manually, how about we choose among the various serializers available in the form of gems. These serializers make it easier to build serialized JSON data. They provide wrapper methods that can help us keep the code well-structured and enable customization.</p><p>There are different serializer gems available for us to choose from. Out of them we shall explore the four most popular ones and compare them.</p><ul><li><a href="https://github.com/rails/jbuilder">Jbuilder</a></li><li><a href="https://github.com/rails-api/active_model_serializers" rel="noopener noreferrer">Active Model Serializers</a></li><li><a href="https://github.com/Netflix/fast_jsonapi">Fast JSON API</a> (<a href="https://github.com/jsonapi-serializer/jsonapi-serializer">jsonapi-serializer</a>)</li><li><a href="http://jsonapi-rb.org/">JSONAPI-RB</a> (<a href="https://github.com/jsonapi-rb/jsonapi-rails">jsonapi-rails</a>)</li></ul><h3 id="jbuilder">Jbuilder</h3><p>Jbuilder is provided by Rails(&gt;= 5) as the default serializer and is based on the approach that construction of JSON data belongs to the View layer. This gem provides a DSL to customize the responses. The files are placed under the <code>app/views</code> directory with the extension of <code>.json.jbuilder</code>. &#xA0;Jbuilder is quite intuitive and helpful when you need to send complex responses with a lot of data manipulations.</p><pre><code class="language-ruby">class Api::V1::CoursesController &lt; ApplicationController
  helper_method :current_user
  
  def index
    @courses = current_user.courses
  end
end</code></pre><pre><code class="language-ruby"># /app/views/api/v1/courses/index.json.jbuilder

json.array! @courses do |course|
  json.extract! course, :id, :title, :description
    json.topics course.topics, partial: &apos;api/v1/courses/topic&apos;, as: :topic
  end
end</code></pre><pre><code class="language-ruby"># /app/views/api/v1/courses/_topic.json.jbuilder

json.extract! topic, :id, :name, :description
if topic.is_completed?(current_user)
  json.assignments topic.assignments, partial: &apos;api/v1/courses/assignment&apos;, as: :assignment
  end
end</code></pre><pre><code class="language-ruby"># /app/views/api/v1/courses/_assignment.json.jbuilder

json.extract! assignment, :id, :description
</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-JSON">[
    {
        &quot;id&quot;: 24,
        &quot;title&quot;: &quot;Javascript Training&quot;,
        &quot;description&quot;: &quot;Master JavaScript With The Most Complete Course On The Market.&quot;,
        &quot;topics&quot;: [
            {
                &quot;id&quot;: 159,
                &quot;name&quot;: &quot;Variables and flow control&quot;,
                &quot;description&quot;: &quot;You will learn the fundamentals of JavaScript&quot;,
                &quot;is_completed&quot;: true,
                &quot;assignments&quot;: [
                    {
                        &quot;id&quot;: 157,
                        &quot;description&quot;: &quot;Declare a variable called myName and initialize it with a value, on the same line.&quot;
                    }
                ]
            }
        ]
    },
    {
        &quot;id&quot;: 25,
        &quot;title&quot;: &quot;Ruby on Rails&quot;,
        &quot;description&quot;: &quot; Learn to make innovative web apps with Ruby on Rails and unleash your creativity&quot;,
        &quot;topics&quot;: [
            {
                &quot;id&quot;: 160,
                &quot;name&quot;: &quot;Serializers in Rails&quot;,
                &quot;description&quot;: &quot;You will learn all the API Serializers in Ruby on Rails&quot;,
                &quot;is_completed&quot;: false
            }
        ]
    }
]</code></pre><figcaption>Response for api/v1/courses#index</figcaption></figure><p>Ruby Benchmark Report</p><pre><code class="language-ruby">    user     system      total        real 
0.875491   0.035895   0.911386 	(1.000073)</code></pre><h3 id="activemodel-serializers-">ActiveModel::Serializers:</h3><p>If you do not wish to use a DSL and are more comfortable with treating serializers like an ActiveRecord object, then you may go with the Active Model Serializers. It is based on Rails conventions for generating data and extracts the serialization logic into a separate folder <code>/app/serializers</code>.</p><pre><code class="language-ruby"># /app/serializers/course_serializer.rb

class CourseSerializer &lt; ActiveModel::Serializer
  attributes :id, :title, :description

  has_many :topics
  has_many :user_courses
end
</code></pre><pre><code class="language-ruby"># /app/serializers/topic_serializer.rb

class TopicSerializer &lt; ActiveModel::Serializer
  attributes :id, :name, :description

  belongs_to :course
  has_many :assignments, if: -&gt; { object.is_completed?(current_user) }
  
  attribute :is_completed do
    object.is_completed?(current_user)
  end
end
</code></pre><pre><code class="language-ruby"># /app/serializers/assignment_serializer.rb

class AssignmentSerializer &lt; ActiveModel::Serializer
  attributes :id, :description

  belongs_to :topic
end
</code></pre><p>Now that we have defined the serializers for our models and their associations, all we have to do in the controller action is this: </p><pre><code class="language-ruby">class Api::V1::CoursesController &lt; ApplicationController
  def index
    render json: current_user.courses, include: &quot;topics.assignments&quot;
  end
end</code></pre><p>Please refer to <a href="https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/adapters.md#include-option">this</a> documentation to know more about how we can include double nested associations.</p><p>The result would be something like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-JSON">[
    {
        &quot;id&quot;: 24,
        &quot;title&quot;: &quot;Javascript Training&quot;,
        &quot;description&quot;: &quot;Master JavaScript With The Most Complete Course On The Market.&quot;,
        &quot;topics&quot;: [
            {
                &quot;id&quot;: 159,
                &quot;name&quot;: &quot;Variables and flow control&quot;,
                &quot;description&quot;: &quot;You will learn the fundamentals of JavaScript&quot;,
                &quot;is_completed&quot;: true,
                &quot;assignments&quot;: [
                    {
                        &quot;id&quot;: 157,
                        &quot;description&quot;: &quot;Declare a variable called myName and initialize it with a value, on the same line.&quot;
                    }
                ]
            }
        ]
    },
    {
        &quot;id&quot;: 25,
        &quot;title&quot;: &quot;Ruby on Rails&quot;,
        &quot;description&quot;: &quot; Learn to make innovative web apps with Ruby on Rails and unleash your creativity&quot;,
        &quot;topics&quot;: [
            {
                &quot;id&quot;: 160,
                &quot;name&quot;: &quot;Serializers in Rails&quot;,
                &quot;description&quot;: &quot;You will learn all the API Serializers in Ruby on Rails&quot;,
                &quot;is_completed&quot;: false
            }
        ]
    }
]</code></pre><figcaption>Response for courses#index.json</figcaption></figure><p>NEAT, isn&apos;t it?</p><p>Ruby Benchmark Report</p><pre><code class="language-ruby">    user     system      total        real  
0.340994   0.008847   0.349841 	(0.483862)
</code></pre><!--kg-card-begin: markdown--><h3 id="fastjsonapi">Fast JSON API</h3>
<!--kg-card-end: markdown--><p>Fast JSON API is similar to AMS but is much faster. While AMS lets you configure the <a href="https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/adapters.md">adapter</a> to be used, Fast JSON API follows the <a href="https://jsonapi.org/format/">JSON API specifications</a>. <br>This serializer is not maintained anymore, but we can use its forked version, i.e the &#xA0;<a href="https://github.com/jsonapi-serializer/jsonapi-serializer">jsonapi-serializer</a> as mentioned in their <a href="https://github.com/Netflix/fast_jsonapi">Github page</a>. I have used <code>FastJsonapi::ObjectSerializer</code> in the examples below, but we can replace it with <code>JSONAPI::Serializer</code> too. &#xA0; <br>The JSON structure may be a bit uncomprehensible specifically for nested relations, but that is how the specifications demand it to be.</p><pre><code class="language-ruby"># /app/serializers/course_serializer.rb

class CourseSerializer
  include FastJsonapi::ObjectSerializer

  attributes :id, :title, :description
  attribute :topics do |object, params|
    TopicSerializer.new(object.topics, { params: { current_user: params[:current_user] } })
  end
end</code></pre><pre><code class="language-ruby"># /app/serializers/topic_serializer.rb

class TopicSerializer
  include FastJsonapi::ObjectSerializer
  attributes :id, :name, :description

  has_many :assignments, if: Proc.new { |topic, params| topic.is_completed?(params[:current_user]) }

  attribute :is_completed do |topic, params|
    topic.is_completed?(params[:current_user])
  end
end
</code></pre><pre><code class="language-ruby"># /app/serializers/assignment_serializer.rb

class AssignmentSerializer
  include FastJsonapi::ObjectSerializer

  attributes :id, :description
end
</code></pre><pre><code class="language-ruby">class Api::V1::CoursesController &lt; ApplicationController
  def index
    render json: CourseSerializer.new(current_user.courses, { params: { current_user: current_user }, include: [:&apos;topics.assignments&apos;] })
  end
end</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-JSON">{
    &quot;data&quot;: [
        {
            &quot;id&quot;: &quot;24&quot;,
            &quot;type&quot;: &quot;course&quot;,
            &quot;attributes&quot;: {
                &quot;id&quot;: 24,
                &quot;title&quot;: &quot;Javascript Training&quot;,
                &quot;description&quot;: &quot;Master JavaScript With The Most Complete Course On The Market.&quot;
            },
            &quot;relationships&quot;: {
                &quot;topics&quot;: {
                    &quot;data&quot;: [
                        {
                            &quot;id&quot;: &quot;159&quot;,
                            &quot;type&quot;: &quot;topic&quot;
                        }
                    ]
                }
            }
        },
        {
            &quot;id&quot;: &quot;25&quot;,
            &quot;type&quot;: &quot;course&quot;,
            &quot;attributes&quot;: {
                &quot;id&quot;: 25,
                &quot;title&quot;: &quot;Ruby on Rails&quot;,
                &quot;description&quot;: &quot; Learn to make innovative web apps with Ruby on Rails and unleash your creativity&quot;
            },
            &quot;relationships&quot;: {
                &quot;topics&quot;: {
                    &quot;data&quot;: [
                        {
                            &quot;id&quot;: &quot;160&quot;,
                            &quot;type&quot;: &quot;topic&quot;
                        }
                    ]
                }
            }
        }
    ],
    &quot;included&quot;: [
        {
            &quot;id&quot;: &quot;157&quot;,
            &quot;type&quot;: &quot;assignment&quot;,
            &quot;attributes&quot;: {
                &quot;id&quot;: 157,
                &quot;description&quot;: &quot;Declare a variable called myName and initialize it with a value, on the same line.&quot;
            }
        },
        {
            &quot;id&quot;: &quot;159&quot;,
            &quot;type&quot;: &quot;topic&quot;,
            &quot;attributes&quot;: {
                &quot;id&quot;: 159,
                &quot;name&quot;: &quot;Variables and flow control&quot;,
                &quot;description&quot;: &quot;You will learn the fundamentals of JavaScript&quot;,
                &quot;is_completed&quot;: true
            },
            &quot;relationships&quot;: {
                &quot;assignments&quot;: {
                    &quot;data&quot;: [
                        {
                            &quot;id&quot;: &quot;157&quot;,
                            &quot;type&quot;: &quot;assignment&quot;
                        }
                    ]
                }
            }
        },
        {
            &quot;id&quot;: &quot;160&quot;,
            &quot;type&quot;: &quot;topic&quot;,
            &quot;attributes&quot;: {
                &quot;id&quot;: 160,
                &quot;name&quot;: &quot;Serializers in Rails&quot;,
                &quot;description&quot;: &quot;You will learn all the API Serializers in Ruby on Rails&quot;,
                &quot;is_completed&quot;: false
            },
            &quot;relationships&quot;: {}
        }
    ]
}</code></pre><figcaption>Response for courses#index.json</figcaption></figure><p>Ruby Benchmark Report</p><pre><code class="language-ruby">    user     system      total        real  
0.468393   0.018134   0.486527 	(0.530162)</code></pre><!--kg-card-begin: markdown--><h3 id="jsonapirb">JSONAPI-RB</h3>
<!--kg-card-end: markdown--><p>Similar to Fast JSON, JSON API offers a bunch of lightweight modules and wrappers to make Ruby applications abide by the<a href="https://jsonapi.org/format/"> JSON-API Specifications</a></p><p>We will use <code>jsonapi-rails </code>, a gem that provides the required helpers to generate JSON-API conformant responses.</p><pre><code class="language-ruby"># /app/resources/serializable_course.rb

class SerializableCourse &lt; JSONAPI::Serializable::Resource
  type &apos;courses&apos;

  attributes :title, :description

  belongs_to :user
  has_many :topics
end</code></pre><pre><code class="language-ruby"># /app/resources/serializable_topic.rb

class SerializableTopic &lt; JSONAPI::Serializable::Resource
  extend JSONAPI::Serializable::Resource::ConditionalFields
  type &apos;topics&apos;
  
  attributes :name, :description

  belongs_to :course
  has_many :assignments, if: -&gt; { @object.is_completed?(@user) }
  
  attribute :is_completed do ||
    @object.is_completed?(@user)
  end
end</code></pre><pre><code class="language-ruby"># /app/resources/serializable_assignment.rb

class SerializableAssignment &lt; JSONAPI::Serializable::Resource
  type &apos;assignments&apos;

  attributes :description

  belongs_to :topic
end</code></pre><pre><code class="language-ruby">class Api::V1::CoursesController &lt; ApplicationController
  def index
    render jsonapi: current_user.courses,
           expose: { user: current_user },
           include: [&quot;topics&quot;, &quot;topics.assignments&quot;],
           fields: { courses: [:title, :description, :topics],
                     topics: [:name, :description, :assignments],
                     assignments: [:description] }
  end
end</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-JSON">{
    &quot;data&quot;: [
        {
            &quot;id&quot;: &quot;24&quot;,
            &quot;type&quot;: &quot;courses&quot;,
            &quot;attributes&quot;: {
                &quot;title&quot;: &quot;Javascript Training&quot;,
                &quot;description&quot;: &quot;Master JavaScript With The Most Complete Course On The Market.&quot;
            },
            &quot;relationships&quot;: {
                &quot;topics&quot;: {
                    &quot;data&quot;: [
                        {
                            &quot;type&quot;: &quot;topics&quot;,
                            &quot;id&quot;: &quot;159&quot;
                        }
                    ]
                }
            }
        },
        {
            &quot;id&quot;: &quot;25&quot;,
            &quot;type&quot;: &quot;courses&quot;,
            &quot;attributes&quot;: {
                &quot;title&quot;: &quot;Ruby on Rails&quot;,
                &quot;description&quot;: &quot; Learn to make innovative web apps with Ruby on Rails and unleash your creativity&quot;
            },
            &quot;relationships&quot;: {
                &quot;topics&quot;: {
                    &quot;data&quot;: [
                        {
                            &quot;type&quot;: &quot;topics&quot;,
                            &quot;id&quot;: &quot;160&quot;
                        }
                    ]
                }
            }
        }
    ],
    &quot;included&quot;: [
        {
            &quot;id&quot;: &quot;159&quot;,
            &quot;type&quot;: &quot;topics&quot;,
            &quot;attributes&quot;: {
                &quot;name&quot;: &quot;Variables and flow control&quot;,
                &quot;description&quot;: &quot;You will learn the fundamentals of JavaScript&quot;,
                &quot;is_completed&quot;: true
            },
            &quot;relationships&quot;: {
                &quot;course&quot;: {
                    &quot;meta&quot;: {
                        &quot;included&quot;: false
                    }
                },
                &quot;assignments&quot;: {
                    &quot;data&quot;: [
                        {
                            &quot;type&quot;: &quot;assignments&quot;,
                            &quot;id&quot;: &quot;157&quot;
                        }
                    ]
                }
            }
        },
        {
            &quot;id&quot;: &quot;160&quot;,
            &quot;type&quot;: &quot;topics&quot;,
            &quot;attributes&quot;: {
                &quot;name&quot;: &quot;Serializers in Rails&quot;,
                &quot;description&quot;: &quot;You will learn all the API Serializers in Ruby on Rails&quot;,
                &quot;is_completed&quot;: false
            },
            &quot;relationships&quot;: {
                &quot;course&quot;: {
                    &quot;meta&quot;: {
                        &quot;included&quot;: false
                    }
                }
            }
        },
        {
            &quot;id&quot;: &quot;157&quot;,
            &quot;type&quot;: &quot;assignments&quot;,
            &quot;attributes&quot;: {
                &quot;description&quot;: &quot;Declare a variable called myName and initialize it with a value, on the same line.&quot;
            },
            &quot;relationships&quot;: {
                &quot;topic&quot;: {
                    &quot;meta&quot;: {
                        &quot;included&quot;: false
                    }
                }
            }
        },
        {
            &quot;id&quot;: &quot;158&quot;,
            &quot;type&quot;: &quot;assignments&quot;,
            &quot;attributes&quot;: {
                &quot;description&quot;: &quot;Render a basic json response from an action controller&quot;
            },
            &quot;relationships&quot;: {
                &quot;topic&quot;: {
                    &quot;meta&quot;: {
                        &quot;included&quot;: false
                    }
                }
            }
        }
    ],
    &quot;jsonapi&quot;: {
        &quot;version&quot;: &quot;1.0&quot;
    }
}</code></pre><figcaption>Response for courses#index.json</figcaption></figure><p>Ruby Benchmark Report</p><pre><code class="language-ruby">    user     system      total        real  
0.418128   0.022805   0.440933 	(0.495260)
</code></pre><!--kg-card-begin: markdown--><h3 id="whichonetochoose">Which one to choose?</h3>
<!--kg-card-end: markdown--><p>We have seen a few of the many options out there that can be used for serializing API data in Ruby on Rails applications. Many others are not covered here like RABL, Blueprinter, etc. So now the question arises which serializer should you choose for your application?<br><a href="http://www.carlosramireziii.com/which-json-serializer-to-use-for-a-new-rails-api.html">Here</a> is a good blog that shares reasons and use-cases where a specific serializer would fit best. To summarize that blog, choose Jbuilder if you need a lot of customized rendering and like the responses to remain at the view level. If you expect your responses to be consistent with the database design go with AMS. If you are a stickler and would like to follow the JSON:API specifications exactly, go with FastJSON-API/JSONAPI-RB</p><p>If you wish to see the above examples in action and play around with them, <a href="https://github.com/AthiraKadampatta/rails-api-serializers">here</a> is a link to the Github repository that I created.</p><!--kg-card-begin: markdown--><h3 id="conclusion">Conclusion</h3>
<!--kg-card-end: markdown--><p>Creating JSON responses in Ruby on Rails might look easy, but with the array of different serializers available, you might be spoilt for choice. The best way to take a decision would be to know who are the potential consumers of your APIs and analyze the expected response. I would also suggest taking a look at their documentation to see which serializer would satisfy all your needs without much overriding.</p><p>Everything that we deal with in the inter-connected world today is nothing but &quot;data&quot;. Serialization that deals with the translation/storage of this data have become all the more important with the evolution of API-driven applications. </p><p>I hope this series of articles dealing with Serialization in Ruby on Rails helps you in choosing the right serialization format and tools for storing and transferring data. </p><p>Thank you for reading!</p><!--kg-card-begin: markdown--><h3 id="references">References</h3>
<ol>
<li><a href="https://buttercms.com/blog/json-serialization-in-rails-a-complete-guide">JSON Serialization In Rails - A complete guide</a></li>
<li><a href="http://www.carlosramireziii.com/which-json-serializer-to-use-for-a-new-rails-api.html">Which JSON Serializer To Use for a new Rails API</a></li>
<li><a href="https://dev.to/caffiendkitten/wtf-is-a-serializer-anyway-1dgj">Wtf is a serializer anyway</a></li>
<li><a href="https://medium.com/@raj.b.stretz/active-model-serializer-vs-fast-json-api-serializer-8338b939f01f">Active Model serializer vs Fast-json-api serializer</a></li>
<li><a href="https://www.reddit.com/r/rails/comments/djzlnm/2019_which_json_serializer_to_use_rails_api/">Which JSON serializer to use rails api</a></li>
<li><a href="https://www.sitepoint.com/active-model-serializers-rails-and-json-oh-my/">Active Model serializers rails and JSON oh my</a></li>
<li><a href="http://vaidehijoshi.github.io/blog/2015/06/23/to-serialize-or-not-to-serialize-activemodel-serializers/">To serialize or not to serialize: activemodel serializers</a></li>
<li><a href="https://stackoverflow.com/a/26557905/5308127">jbuilder vs rails-api/active_model_serializers for JSON</a></li>
</ol>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Rails Parameters Parsing and the Case of Parameters Corruption]]></title><description><![CDATA[Rails is a developer-friendly web application framework that enables developers to do more with less code, but it isn’t always clear exactly what’s going...]]></description><link>https://blog.kiprosh.com/rails-parameters-parsing-and-the-case-of-parameters-corruption/</link><guid isPermaLink="false">6045c68d5e6fb607956121fa</guid><category><![CDATA[rails]]></category><category><![CDATA[ruby]]></category><category><![CDATA[parsing]]></category><category><![CDATA[query parameter]]></category><category><![CDATA[rack]]></category><dc:creator><![CDATA[Ayush Billore]]></dc:creator><pubDate>Fri, 09 Apr 2021 07:18:09 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1522776851755-3914469f0ca2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHJ1Ynl8ZW58MHx8fHwxNjE2NjU0MTA2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1522776851755-3914469f0ca2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHJ1Ynl8ZW58MHx8fHwxNjE2NjU0MTA2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Rails Parameters Parsing and the Case of Parameters Corruption"><p><strong>Rails</strong> is a developer-friendly web application framework that enables developers to do more with less code, but it isn&#x2019;t always clear exactly what&#x2019;s going on under the covers. One area where I&#x2019;ve had a hard time was understanding how Rails parses the Request query parameters and the Form variables.</p>
<h4 id="sowhatsaqueryparameter">So what&apos;s a query parameter?</h4>
<p>Query parameters are an optional set of key-value pairs that appear after the question mark in the URL. For example, <code>name=contra</code> is a query parameter in <code>https://example.com/over/there?name=contra</code>.</p>
<h4 id="andwhatsaformvariable">And what&apos;s a form variable?</h4>
<p>When we submit a form on the web, the form sends data to the server as form variables. For example, <code>player[game_attributes][name]</code> and <code>player[game_attributes][release_year]</code> are form variables in the snippet below.</p>
<pre><code class="language-markup">&lt;p&gt;
  Task Name: &lt;input name=&quot;player[game_attributes][name]&quot;&gt;
  Task Duration: &lt;input name=&quot;player[game_attributes][release_year]&quot;&gt;
&lt;/p&gt;
</code></pre>
<hr>
<h2 id="parametersparsing">Parameters Parsing</h2>
<p>Several server-side frameworks are designed to handle form data if the inputs are named in a certain way. For e.g., in Rails, if you have an input named <code>user[name]</code>, and is submitted as a part of a form, then the server would parse and convert it to a Hash as such: <code>{ &quot;user&quot; =&gt; { &quot;name&quot; =&gt; } }</code>. This allows the server program to access the input value <code>params[&quot;user&quot;][&quot;name&quot;]</code>, where params are the variable through which the parsed Hash is accessible.</p>
<p>Rails uses <a href="https://rack.github.com/">Rack</a> for parameters parsing, so it&#x2019;s the same even for other Ruby frameworks such as <a href="http://sinatrarb.com/">Sinatra</a> and <a href="http://padrinorb.com/">Padrino</a>.</p>
<h3 id="whatshappeningunderthecovers">What&apos;s happening under the covers?</h3>
<p>The function in Rack that&#x2019;s doing all the work in generating the <code>magical params</code> is <a href="https://github.com/rack/rack/blob/14a236b4f0899e46bc41c4f80dcff29159a59312/lib/rack/utils.rb#L98">parse_nested_query</a>. The <a href="http://apidock.com/rails/Rack/Utils/parse_nested_query">documentation</a> is not very comprehensive, so we can experiment a bit to figure out what&#x2019;s going on under the covers.</p>
<p>Here&apos;s what the method looks like:</p>
<pre><code class="language-ruby">def parse_nested_query(qs, d = &apos;&amp;;&apos;)
  params = {}
  (qs || &apos;&apos;).split(/[#{d}] */n).each do |p|
    k, v = unescape(p).split(&apos;=&apos;, 2)
    normalize_params(params, k, v)
  end
  return params
end
</code></pre>
<h3 id="experimentalsetup">Experimental Setup</h3>
<p>Since Rack is written in ruby, we can pop open the <a href="https://en.wikipedia.org/wiki/Interactive_Ruby_Shell"><em>irb</em></a> and do the initial setup.</p>
<pre><code class="language-ruby">ayush-kiproshs-MacBook ~ $ irb
2.7.0 :001 &gt; require &quot;rack&quot;
 =&gt; true
2.7.0 :002 &gt; def parse(query_string)
2.7.0 :003 &gt;   Rack::Utils.parse_nested_query(query_string)
2.7.0 :004 &gt; end
 =&gt; :parse
</code></pre>
<p>We need to require Rack and create a parse method for our ease of experimentation. Now as we are ready with the initial setup let&apos;s begin with the cases.</p>
<h4 id="case1whenthefieldsarenamedwithoutsquarebrackets">Case 1: When the fields are named without square brackets</h4>
<p>If there are no square brackets, the value directly gets assigned to the parameter.</p>
<pre><code class="language-ruby">2.6.6 :013 &gt; query_string = &quot;game_1=contra&amp;game_2=mario&quot;
2.6.6 :014 &gt; parse(query_string)
 =&gt; {&quot;game_1&quot;=&gt;&quot;contra&quot;, &quot;game_2&quot;=&gt;&quot;mario&quot;}
</code></pre>
<h4 id="case2whenusingsquarebracketstonameattributes">Case 2: When using square brackets to name attributes</h4>
<p>If the parameter is enclosed under square brackets, the value gets assigned to the parameter in a nested structure.</p>
<pre><code class="language-ruby">2.6.6 :015 &gt; query_string = &quot;player[game]=contra&quot;
2.6.6 :016 &gt; parse(query_string)
 =&gt; {&quot;player&quot;=&gt;{&quot;game&quot;=&gt;&quot;contra&quot;}}
</code></pre>
<h4 id="case3whenthefieldnameendswithemptysquarebrackets">Case 3: When the field name ends with empty square brackets</h4>
<p>If the field name is ending with empty square brackets, the parameter is treated as an empty array, and values get appended to it.</p>
<pre><code class="language-ruby">2.7.0 :017 &gt; query_string = &quot;player[games][]=contra&amp;
                             player[games][]=mario&quot;
2.7.0 :018 &gt; parse(query_string)
 =&gt; {&quot;player&quot;=&gt;{&quot;games&quot;=&gt;[&quot;contra&quot;, &quot;mario&quot;]}}
</code></pre>
<h4 id="case4whenparsingobjectscombinationoftheabovethreecases">Case 4: When parsing objects(Combination of the above three cases)</h4>
<p>On analysing the above cases we know that Rails reads the query parameters from left to right in the query-string, and creates a new object each time it sees a repeated attribute.</p>
<pre><code class="language-ruby">2.7.0 :021 &gt; query_string = &quot;player[games][][name]=contra&amp;
                             player[games][][release_year]=1987&amp;
                             player[games][][name]=mario&amp;
                             player[games][][release_year]=1983&quot;
2.7.0 :022 &gt; parse(query_string)
 =&gt; {&quot;player&quot;=&gt;{&quot;games&quot;=&gt;[{&quot;name&quot;=&gt;&quot;contra&quot;, &quot;release_year&quot;=&gt;&quot;1987&quot;}, {&quot;name&quot;=&gt;&quot;mario&quot;, &quot;release_year&quot;=&gt;&quot;1983&quot;}]}}
</code></pre>
<h4 id="case5caseofparameterscorruption">Case 5: Case of Parameters Corruption</h4>
<p>We know from the above cases that a new parameter is created each time the parser encounters a repeated attribute. But if the order of query parameters in the query string is not correct, the parser will return a corrupted params object. See examples below for better understanding.</p>
<pre><code class="language-ruby">2.7.0 :023 &gt; query_string = &quot;player[games][][name]=contra&amp;
                             player[games][][name]=mario&amp;
                             player[games][][release_year]=1987&amp;
                             player[games][][release_year]=1983&quot;
2.7.0 :024 &gt; parse(query_string)
 =&gt; {&quot;player&quot;=&gt;{&quot;games&quot;=&gt;[{&quot;name&quot;=&gt;&quot;contra&quot;}, {&quot;name&quot;=&gt;&quot;mario&quot;, &quot;release_year&quot;=&gt;&quot;1987&quot;}, {&quot;release_year&quot;=&gt;&quot;1983&quot;}]}}
</code></pre>
<p>Parameters in query parsing are strictly order-dependent. And it is guaranteed that the browsers will send the form parameters in the same order in which they appear in the source.</p>
<p>Refer <em><a href="https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1">W3C specification</a></em> to learn about browser standards.</p>
<blockquote>
<p><strong>multipart/form-data</strong> The parts are sent to the processing agent in the same order the corresponding controls appear in the document stream. Part boundaries should not occur in any of the data; how this is done lies outside the scope of this specification.</p>
</blockquote>
<hr>
<h2 id="references">References</h2>
<ul>
<li><a href="https://www.npmjs.com/package/@unobtrusive/react-form-builder">@unobtrusive/react-form-builder</a></li>
<li><a href="https://stackoverflow.com/questions/10372989/how-does-rails-group-arrays-of-hashes-during-form-submission/36334080">How does Rails group arrays of hashes during form submission?</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Rate limiting using Redis in a Rails app]]></title><description><![CDATA[The web is a weird place. You go to sleep thinking that you have a perfectly functional web application and the next day when you wake up, you might find...]]></description><link>https://blog.kiprosh.com/rate-limiting-using-redis/</link><guid isPermaLink="false">6044664d5e6fb60795611f09</guid><category><![CDATA[rails]]></category><category><![CDATA[redis]]></category><category><![CDATA[rate limiting]]></category><category><![CDATA[ruby-on-rails]]></category><category><![CDATA[ruby]]></category><category><![CDATA[middleware]]></category><category><![CDATA[rack-attack]]></category><dc:creator><![CDATA[Prayesh Shah]]></dc:creator><pubDate>Thu, 25 Mar 2021 06:17:36 GMT</pubDate><media:content url="https://blog.kiprosh.com/content/images/2021/03/markus-winkler-IrRbSND5EUc-unsplash-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.kiprosh.com/content/images/2021/03/markus-winkler-IrRbSND5EUc-unsplash-1.jpg" alt="Rate limiting using Redis in a Rails app"><p>The web is a weird place. You go to sleep thinking that you have a perfectly functional web application and the next day when you wake up, you might find yourself staring at a sudden huge spike in the number of requests. Either your app got popular overnight or you were just a victim of a <code>DOS</code> attack trying to bring your app server down. Usually, it&apos;s the latter.</p>
<hr>
<p>There are some popular gems like <a href="https://github.com/rack/rack-attack">rack-attack</a> and <a href="https://github.com/dryruby/rack-throttle">rack-throttle</a> which work quite well and provides a lot of flexibility. But if you&apos;re looking to write your custom logic with minimum dependencies, then continue reading.</p>
<p>We will create a <code>middleware</code> that intercepts and blocks any host which tries to overload our servers by firing too many requests within a short timespan. We will be using <a href="https://redis.io/">Redis</a> to store the count of requests from each IP address.</p>
<p>Let&apos;s start by writing the most basic <code>middleware</code>.</p>
<pre><code class="language-ruby"># app/lib/middlewares/custom_rate_limit.rb
class CustomRateLimit
  def initialize(app)
    @app = app
  end

  def call(env)
    @app.call(env)
  end
end
</code></pre>
<p><br>
Add the following line inside your <code>application.rb</code>:</p>
<pre><code class="language-ruby"># For Rails version &gt; 5
config.middleware.use CustomRateLimit
</code></pre>
<p><br>
If you&apos;re using Rails version lesser than 5, add the following:</p>
<pre><code class="language-ruby"># For Rails version &lt; 5
config.middleware.use &apos;CustomRateLimit&apos;
</code></pre>
<p><br>
Run the <code>bin/rails middleware</code> command and verify that our custom middleware is present in the middleware stack.<br>
In case you run into an error saying <code>uninitialized constant ::CustomRateLimit</code>, add the below line at the top of your <code>application.rb</code>:</p>
<pre><code class="language-ruby">require_relative &apos;../lib/middlewares/custom_rate_limit&apos;
</code></pre>
<p><br>
Now, let&apos;s install the <code>redis</code> gem by adding it to our <code>Gemfile</code>.</p>
<pre><code class="language-ruby">gem &apos;redis&apos;
</code></pre>
<p>Also, ensure that you have the <code>Redis</code> server installed on your local system. If not, install it by following the steps mentioned <a href="https://redis.io/topics/quickstart">here</a>.</p>
<p><strong>Note:</strong> As Redis is an in-memory database, it&apos;s state is not persistent. In other words, if your redis server were to go down due to an outage, you&apos;d lose your data. By default, Redis saves snapshots of the dataset on the disk, in a binary file called <code>dump.rdb</code>. Refer <a href="https://redis.io/topics/persistence">redis persistence</a> for more details.</p>
<p>To initialize <code>redis</code> in your app, add the following file inside the <code>config/initializers</code>:</p>
<pre><code class="language-ruby"># config/initializers/redis.rb
require &apos;redis&apos;

REDIS = Redis.new(url: ENV.fetch(&apos;REDIS_URL&apos;))
</code></pre>
<p>For development environment, the value of <code>REDIS_URL</code> will be <code>redis://localhost:6379</code>.<br>
<br>
Now, let&apos;s edit our middleware and add some logic.</p>
<pre><code class="language-ruby">def call(env)
  if should_allow?(env)
    @app.call(env)
  else
    request_quota_exceeded
  end
end
</code></pre>
<p><br>
The <code>should_allow?</code> function will look something like this:</p>
<pre><code class="language-ruby">def should_allow?(env)
  key = &quot;IP:#{env[&apos;action_dispatch.remote_ip&apos;]}&quot;

  REDIS.set(key, 0, nx: true, ex: TIME_PERIOD)
  REDIS.incr(key) &gt; LIMIT ? false : true
end
</code></pre>
<p>We will use the user&apos;s IP address as the key and store the request count as the value.<br>
The <a href="https://www.rubydoc.info/github/redis/redis-rb/Redis:set">Redis#set</a> method will set the record in the redis store. We will pass it the following arguments:</p>
<ol>
<li><code>key</code> - a unique identifier, which in this case will be the user&apos;s IP address</li>
<li><code>value</code> - sets the value against the given key</li>
<li><code>ex</code> - sets the expiry time in seconds</li>
<li><code>nx</code> - sets the key only if it doesn&apos;t already exist</li>
</ol>
<p>On every request, we increment the count using <a href="https://www.rubydoc.info/github/redis/redis-rb/Redis:incr">Redis#incr</a>. If the count exceeds the predefined limit, we return <code>false</code>.<br>
<br>
Define the constants in the same file and update the values as per your needs.</p>
<pre><code class="language-ruby">TIME_PERIOD = 60 # no. of seconds
LIMIT = 20 # no. of allowed requests per IP for unauthenticated user
</code></pre>
<p><br>
If you are using <code>Warden</code> based authentication like <code>Devise</code>, and don&apos;t want to throttle authenticated requests, add the following guard condition to the <code>should_allow?</code> function.</p>
<pre><code class="language-ruby">return true if env[&apos;rack.session&apos;][&apos;warden.user.user.key&apos;].present?
</code></pre>
<p>This will allow all authenticated requests to pass through without any rate limits.<br>
<br>
The <code>request_quota_exceeded</code> method will look something like this:</p>
<pre><code class="language-ruby">def request_quota_exceeded
  [ 429, {}, [&apos;Too many requests fired. Request quota exceeded!&apos;] ]
end
</code></pre>
<p>The HTTP status code <code>429</code> indicates <code>Too Many Requests</code> to the server in a given period.<br>
<br>
Our middleware will finally look something like this:</p>
<pre><code class="language-ruby"># lib/middlewares/custom_rate_limit.rb
class CustomRateLimit
  TIME_PERIOD = 60 # no. of seconds
  LIMIT = 20 # no. of allowed requests per IP for unauthenticated user

  def initialize(app)
    @app = app
  end

  def call(env)
    if should_allow?(env)
      @app.call(env)
    else
      request_quota_exceeded
    end
  end

  private
  def should_allow?(env)
    return true if env[&apos;rack.session&apos;][&apos;warden.user.user.key&apos;].present?

    key = &quot;IP:#{env[&apos;action_dispatch.remote_ip&apos;]}&quot;

    REDIS.set(key, 0, nx: true, ex: TIME_PERIOD)
    REDIS.incr(key) &gt; LIMIT ? false : true
  end

  def request_quota_exceeded
    [ 429, {}, [&apos;Too many requests fired. Request quota exceeded!&apos;] ]
  end
end
</code></pre>
<hr>
<p>Hope this blog was helpful.<br>
Thank you!</p>
<h2 id="usefullinks">Useful links:</h2>
<p><a href="https://redis.io/commands">Redis commands</a><br>
<a href="https://redislabs.com/redis-best-practices/basic-rate-limiting/">Basic Rate Limiting</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Sync files to a remote location using rsync linux command and Ruby]]></title><description><![CDATA[In this article, we will see how to use rsync in Ruby to sync files from local to a remote location, and how rsync is better than scp.]]></description><link>https://blog.kiprosh.com/sync-files-to-a-remote-location-using-rsync-and-ruby/</link><guid isPermaLink="false">5fb602bf6a8b4e63f74d85b2</guid><category><![CDATA[rsync]]></category><category><![CDATA[file transfer]]></category><category><![CDATA[sync files]]></category><category><![CDATA[scp]]></category><category><![CDATA[ruby]]></category><category><![CDATA[rails]]></category><category><![CDATA[linux]]></category><dc:creator><![CDATA[Supriya Laxman Medankar]]></dc:creator><pubDate>Wed, 17 Mar 2021 06:47:02 GMT</pubDate><media:content url="https://blog.kiprosh.com/content/images/2021/02/pexels-pixabay-373543.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<img src="https://blog.kiprosh.com/content/images/2021/02/pexels-pixabay-373543.jpg" alt="Sync files to a remote location using rsync linux command and Ruby"><p>Note: The entire approach highlighted in this blog about using the <code>rsync</code> command with Ruby is only applicable on Linux-based systems and won&apos;t be available on Windows.</p>
</blockquote>
<p>In this article, we will explore how to use <code>rsync</code> in Ruby to sync files from local to a remote location, how to improve its error logging and how <code>rsync</code> is better than <code>scp</code>.</p>
<h3 id="letsfirstunderstandwhatisrsync">Let&apos;s first understand what is <code>rsync</code>:</h3>
<p><code>rsync</code> is a Linux command to sync files from one location to another. We can run this command on the terminal and it will copy files from one directory to another, either locally or even on a remote location.</p>
<p><code>scp</code> is another Linux command which allows file transfer from one location to another similar to <code>rsync</code>. You can refer to this <a href="https://linuxize.com/post/how-to-use-scp-command-to-securely-transfer-files/">article</a> about <code>scp</code> to learn more about it.</p>
<h3 id="whywepreferredrsyncoverscp">Why we preferred <code>rsync</code> over <code>scp</code>?</h3>
<p>There are some advantages of using <code>rsync</code> over <code>scp</code>, these are:</p>
<ol>
<li><code>rsync</code> uses a special <a href="https://en.wikipedia.org/wiki/Delta_encoding">delta transfer algorithm</a> along with a few optimizations which makes it faster as compared to <code>scp</code> which performs a plain transfer</li>
<li><code>rsync</code> provides the option to preserve the partially downloaded files. In case if your download session gets interrupted in the middle of copying a file, you can resume it from where you left off</li>
<li><code>rsync</code> automatically detects if the file has been transferred correctly</li>
</ol>
<p>I found this <a href="https://stackoverflow.com/questions/20244585/how-does-scp-differ-from-rsync">Stack Overflow</a> post helpful while comparing <code>scp</code> to <code>rsync</code>.</p>
<h3 id="howtousersynccommand">How to use <code>rsync</code> command?</h3>
<pre><code class="language-bash">rsync [options] [source] [destination]
</code></pre>
<p>Following are the options which we can use with <code>rsync</code>:</p>
<p><code>-v</code> : verbose<br>
<code>-r</code> : recursively copies data. It doesn&#x2019;t preserve timestamps and permission while transferring data<br>
<code>-a</code> : archive mode, it allows copying files recursively and it also preserves symbolic links, file permissions, user &amp; group ownerships and timestamps<br>
<code>-z</code> : compress file data<br>
<code>-h</code> : human-readable, output numbers in a human-readable format<br>
<code>-e</code> : specify type of protocol</p>
<p>The above options (and more) can also be accessed by running <code>rsync -h</code> in the console.<br>
We can also combine multiple options like <code>rsync -avhe [source] [destination]</code> as per our need.</p>
<h3 id="howrsyncworkswithssh">How <code>rsync</code> works with <code>SSH</code>?</h3>
<p>It is important to transfer data on a secure connection to ensure that nobody can read data while it is being transferred. And we can achieve this by using <code>rsync</code> with SSH (Secure Shell) protocol.<br>
As we can see in the above options list, <code>-e</code> option is used to specify the type of protocol we are using to copy/sync the files to/from a remote location.</p>
<p>So with ssh <code>rsync</code> command will be like this:</p>
<pre><code class="language-bash">$ rsync -e ssh /local_dir user@remote-host:/remote_dir/
</code></pre>
<p>We can also change the permission of file while copying/syncing files from/to a remote location</p>
<pre><code class="language-bash">$ rsync -avhe ssh --chown=USER:GROUP /local_dir user@remote-host:/remote_dir/`
</code></pre>
<p>The above command will sync all the files present in directory <code>/local_dir</code> owned by <code>USER</code> with group <code>GROUP</code> to the <code>/remote_dir</code> directory on the remote-host.</p>
<h3 id="howtousersyncinruby">How to use <code>rsync</code> in Ruby?</h3>
<p>Till now we saw how we can run <code>rsync</code> command in <code>Linux/ Unix</code> terminal. Now let&apos;s see how to use this command in Ruby to sync files to a remote location.</p>
<p>There are multiple ways to call shell commands from Ruby, some of which are as follows:</p>
<ol>
<li>
<p><code>Kernel#`</code> i.e. backticks<br>
It returns the result of shell command</p>
<pre><code class="language-ruby">  `echo &quot;Hello World!&quot;`       #=&gt; &quot;Hello World!\n&quot;
  cmd = &quot;echo &apos;Hello World!&apos;&quot;
  value = `#{cmd}`            #=&gt; &quot;Hello World!\n&quot;
</code></pre>
</li>
<li>
<p>Built-in syntax, <code>%x( cmd )</code><br>
It also returns the result of shell command similar to backticks. In the syntax following <code>%x</code> is a delimiter, we can use any  character as a delimiter. For example (, {, [ or &lt; .  For more details refer to this <a href="https://stackoverflow.com/a/2400">Stack Overflow</a> post.</p>
<pre><code class="language-ruby">  %x( echo &apos;Hello World!&apos;)  #=&gt; &quot;Hello World!\n&quot;
</code></pre>
</li>
<li>
<p><code>Kernel#exec</code><br>
It exits the current process and executes the shell command in the new process.<br>
In the following example, <code>exec</code> exits the ruby console and executes the <code>echo</code> command.</p>
<pre><code class="language-ruby">  exec(&quot;echo &apos;Hello World!&apos;&quot;)  #=&gt; It exits ruby console and prints &quot;Hello World&quot; on terminal
</code></pre>
</li>
<li>
<p><code>Kernel#system</code><br>
Executes the given command in a sub-shell.<br>
On successful execution of command, it returns true otherwise false.</p>
<pre><code class="language-ruby">  system(&apos;invalid command&apos;)                 #=&gt; nil
  system(&apos;echo system command executed&apos;)    #=&gt; true
  system(&apos;echo new command | grep another&apos;) #=&gt; false
</code></pre>
</li>
</ol>
<p>For more information, refer to this <a href="https://www.rubyguides.com/2018/12/ruby-system/">rubyguide</a>.</p>
<p>We will use the <code>system</code> command for error tracking since only this command echoes the <code>STDOUT</code> with <code>true/false/nil</code> according to the result.</p>
<p>Let&apos;s create a method using <code>Kernel#system</code> to execute <code>rsync</code> command:</p>
<pre><code class="language-ruby">def sync_files
  cmd = &apos;rsync -e ssh /local_dir user@remote-host:/remote_dir/&apos;

  system cmd
end
</code></pre>
<p>In the above method, string <code>cmd</code> is the <code>rsync</code> command which we want to execute on the terminal. We have passed it to the <code>system</code> method. All seems to be good so far, right?</p>
<p>Wait, what happens if the <code>rsync</code> command fails due to unexpected errors, such as the server was out of service?</p>
<h3 id="howtotracksystemerrorsinruby">How to track <code>system</code> errors in Ruby?</h3>
<p>First, we will understand what the <code>system</code> method returns:<br>
The <code>system</code> returns <code>true</code> if the command gives zero exit status, <code>false</code> for non zero exit status. Returns <code>nil</code> if command execution fails.</p>
<p>We can check the error status using <code>$?</code>. This returned object of class <code>Process::Status</code> with process id and exit code. For example:</p>
<p><code>#&lt;Process::Status: pid 9167 exit 2&gt;</code></p>
<p>We can also use <code>$?.success?</code> to check the status which returns <code>true</code> if the last operation was a success.</p>
<pre><code class="language-ruby">raise &quot;rsync failed with status code #{status}&quot; unless $?.success?
</code></pre>
<p>Let&apos;s modify the above <code>sync_files</code> method to track errors</p>
<pre><code class="language-ruby">def sync_files
  system &apos;rsync -e ssh /local_dir user@remote-host:/remote_dir/&apos;

  status = $?.exitstatus

  raise &quot;rsync failed with status code #{status}&quot; unless status.zero?
end
</code></pre>
<p>Now, the above method will raise an error whenever <code>rsync</code> command fails to execute due to any issues.</p>
<p>I hope you enjoyed this article and learned how to use <code>rsync</code> in Ruby. Thank you.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>