<?xml version="1.0" encoding="utf-8"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
     <title>BigBinary Blog</title>
     <link href="https://www.bigbinary.com/feed.xml" rel="self"/>
     <link href="https://www.bigbinary.com/"/>
     <updated>2026-03-08T07:52:44+00:00</updated>
     <id>https://www.bigbinary.com/</id>
     <entry>
       <title><![CDATA[Rails 6 adds support to persist timezones of Active Job]]></title>
       <author><name>Chetan Gawai</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-add-timezone-support-in-active-job"/>
      <updated>2020-09-01T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-add-timezone-support-in-active-job</id>
      <content type="html"><![CDATA[<p>When a job is enqueued in Rails 6 using Active Job, the current timezone of ajob is <a href="https://github.com/rails/rails/pull/32085/">preserved</a> and then thispreserved timezone is restored when the job is finished executing.</p><p>Let's take an example of sale at Amazon.</p><p>Amazon would like to remind users across different timezones about its upcomingsale by sending an email. This task of sending a reminder would be processed asa background job.</p><p>&lt;b&gt;Before:&lt;/b&gt;</p><p>Before Rails 6, we had to pass timezone explicitly to the <code>perform</code> method ofthe job as shown below.</p><pre><code class="language-ruby">timezone = &quot;Eastern Time (US &amp; Canada)&quot;AmazonSaleJob.perform_later(Time.now, timezone)class AmazonSaleJob &lt; ApplicationJob  queue_as :default  def perform(time, timezone)    time = time.in_time_zone(timezone)    sale_start_time = localtime(2020, 12, 24)    if time &gt;= sale_start_time      puts &quot;Sale has started!&quot;      #Send an email stating Sale has started    else      sale_starts_in = (sale_start_time - time).div(3600)      puts &quot;Hang on! Sale will start in #{sale_starts_in} hours&quot;      #Send an email stating sales starts in sale_starts_in hours     end  end  private    def localtime(*args)      Time.zone ? Time.zone.local(*args) : Time.utc(*args)    endend</code></pre><p>&lt;b&gt;After:&lt;/b&gt;</p><p>After the changes in Rails 6, passing timezone to Job is now taken care of byRails.</p><pre><code class="language-ruby">timezone = &quot;Eastern Time (US &amp; Canada)&quot;Time.use_zone(timezone) do  AmazonSaleJob.perform_later(Time.zone.now)endclass AmazonSaleJob &lt; ApplicationJob  queue_as :default  def perform(time)    sale_start_time = localtime(2020, 12, 24)    if time &gt;= sale_start_time      puts &quot;Sale has started!&quot;      #Send an email stating Sale has started    else      sale_starts_in = (sale_start_time - time).div(3600)      puts &quot;Hang on! Sale will start in #{sale_starts_in} hours&quot;      #Send an email stating sales starts in sale_starts_in hours     end   end  private    def localtime(*args)      Time.zone ? Time.zone.local(*args) : Time.utc(*args)    endend</code></pre><p>Rails 6 also propagates timezone to all the subsequent nested jobs.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds including & excluding method on Enumerables]]></title>
       <author><name>Akhil Gautam</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-array-including-excluding-and-enumerable-including-excluding"/>
      <updated>2020-08-18T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-array-including-excluding-and-enumerable-including-excluding</id>
      <content type="html"><![CDATA[<p>Rails 6 added <code>including</code> and <code>excluding</code> on Array and Enumerable.</p><h4>Array#including and Enumerable#including</h4><p><code>including</code> can be used to extend a collection in a more object oriented way. Itdoes not mutate the original collection but returns a new collection which isthe concatenation of the given collections.</p><pre><code class="language-ruby"># multiple arguments can be passed to including&gt; &gt; [1, 2, 3].including(4, 5)&gt; &gt; =&gt; [1, 2, 3, 4, 5]# another enumerable can also be passed to including&gt; &gt; [1, 2, 3].including([4, 5])&gt; &gt; =&gt; [1, 2, 3, 4, 5]&gt; &gt; %i(apple orange).including(:banana)&gt; &gt; =&gt; [:apple, :orange, :banana]# return customers whose country_code is IN along with the prime customers&gt; &gt; Customer.where(country_code: &quot;IN&quot;).including(Customer.where(prime: true))</code></pre><h4>Array#excluding and Enumerable#excluding</h4><p>It returns a copy of the enumerable excluding the given collection.</p><pre><code class="language-ruby"># multiple arguments can be passed to including&gt; &gt; [11, 22, 33, 44].excluding([22, 33])&gt; &gt; =&gt; [11, 44]&gt; &gt; %i(ant bat cat).excluding(:bat)&gt; &gt; =&gt; [:ant, :cat]# return all prime customers except those who haven't added their phone&gt; &gt; Customer.where(prime: true).excluding(Customer.where(phone: nil))</code></pre><p><code>Array#excluding and Enumerable#excluding</code> replaces the already existing method<code>without</code> which in Rails 6 is now aliased to <code>excluding</code>.</p><pre><code class="language-ruby">&gt; &gt; [11, 22, 33, 44].without([22, 33])&gt; &gt; =&gt; [11, 44]</code></pre><p><code>excluding</code> and <code>including</code> helps to shrink or extend a collection without usingany operator.</p><p>Check out the<a href="https://github.com/rails/rails/commit/bfaa3091c3c32b5980a614ef0f7b39cbf83f6db3">commit</a>for more details on this.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 fixes after_commit callback invocation bug]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-fixes-a-bug-where-after_commit-callbacks-are-called-on-failed-update-in-a-transaction-block"/>
      <updated>2020-02-25T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-fixes-a-bug-where-after_commit-callbacks-are-called-on-failed-update-in-a-transaction-block</id>
      <content type="html"><![CDATA[<p>Rails 6 fixes a <a href="https://github.com/rails/rails/issues/29747">bug</a> where<a href="https://apidock.com/rails/ActiveRecord/Transactions/ClassMethods/after_commit">after_commit</a>callbacks are called on failed update in a transaction block.</p><p>Let's checkout the bug in Rails 5.2 and the fix in Rails 6.</p><h4>Rails 5.2</h4><p>Let's define an<a href="https://apidock.com/rails/ActiveRecord/Transactions/ClassMethods/after_commit">after_commit</a>callback in <code>User</code> model and try updating an invalid user object in atransaction block.</p><pre><code class="language-ruby">&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   validates :name, :email, presence: true&gt;&gt;&gt;&gt;   after_commit :show_success_message&gt;&gt;&gt;&gt;   private&gt;&gt;&gt;&gt;     def show_success_message&gt;&gt;       p 'User has been successfully saved into the database.'&gt;&gt;     end&gt;&gt; end=&gt; :show_success_message&gt;&gt; user = User.create(name: 'Jon Snow', email: 'jon@bigbinary.com')begin transactionUser Create (0.8ms)  INSERT INTO &quot;users&quot; (&quot;name&quot;, &quot;email&quot;, &quot;created_at&quot;, &quot;updated_at&quot;) VALUES (?, ?, ?, ?)  [[&quot;name&quot;, &quot;Jon Snow&quot;], [&quot;email&quot;, &quot;jon@bigbinary.com&quot;], [&quot;created_at&quot;, &quot;2019-07-14 15:35:33.517694&quot;], [&quot;updated_at&quot;, &quot;2019-07-14 15:35:33.517694&quot;]]commit transaction&quot;User has been successfully saved into the database.&quot;=&gt; #&lt;User id: 1, name: &quot;Jon Snow&quot;, email: &quot;jon@bigbinary.com&quot;, created_at: &quot;2019-07-14 15:35:33&quot;, updated_at: &quot;2019-07-14 15:35:33&quot;&gt;&gt;&gt; User.transaction do&gt;&gt;   user.email = nil&gt;&gt;   p user.valid?&gt;&gt;   user.save&gt;&gt; endbegin transactionfalsecommit transaction&quot;User has been successfully saved into the database.&quot;=&gt; false</code></pre><p>As we can see here, that that the after_commit callback <code>show_success_message</code>was called even if object was never saved in the transaction.</p><h4>Rails 6.0.0.rc1</h4><p>Now, let's try the same thing in Rails 6.</p><pre><code class="language-ruby">&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   validates :name, :email, presence: true&gt;&gt;&gt;&gt;   after_commit :show_success_message&gt;&gt;&gt;&gt;   private&gt;&gt;&gt;&gt;     def show_success_message&gt;&gt;       p 'User has been successfully saved into the database.'&gt;&gt;     end&gt;&gt; end=&gt; :show_success_message&gt;&gt; user = User.create(name: 'Jon Snow', email: 'jon@bigbinary.com')SELECT sqlite_version(*)begin transactionUser Create (1.0ms)  INSERT INTO &quot;users&quot; (&quot;name&quot;, &quot;email&quot;, &quot;created_at&quot;, &quot;updated_at&quot;) VALUES (?, ?, ?, ?)  [[&quot;name&quot;, &quot;Jon Snow&quot;], [&quot;email&quot;, &quot;jon@bigbinary.com&quot;], [&quot;created_at&quot;, &quot;2019-07-14 15:40:54.022045&quot;], [&quot;updated_at&quot;, &quot;2019-07-14 15:40:54.022045&quot;]]commit transaction&quot;User has been successfully saved into the database.&quot;=&gt; #&lt;User id: 1, name: &quot;Jon Snow&quot;, email: &quot;jon@bigbinary.com&quot;, created_at: &quot;2019-07-14 15:40:54&quot;, updated_at: &quot;2019-07-14 15:40:54&quot;&gt;&gt;&gt; User.transaction do&gt;&gt;   user.email = nil&gt;&gt;   p user.valid?&gt;&gt;   user.save&gt;&gt;   endfalse=&gt; false</code></pre><p>Now, we can see that after_commit callback was never called if the object wasnot saved.</p><p>Here is the relevant <a href="https://github.com/rails/rails/issues/29747">issue</a> andthe <a href="https://github.com/rails/rails/pull/32185">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds rails db:prepare to migrate or setup a database]]></title>
       <author><name>Akhil Gautam</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-rails-db-prepare-to-migrate-or-setup-a-database"/>
      <updated>2019-12-10T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-rails-db-prepare-to-migrate-or-setup-a-database</id>
      <content type="html"><![CDATA[<p>Rails 6 adds rails db:prepare to migrate or setup a database if it doesn'texist.</p><p>Before Rails 6, we had to run the following tasks to set up the database.</p><pre><code class="language-ruby"># create the databaserails db:create# run the migrationsrails db:migrate# prepopulate the database with initial/default datarails db:seed</code></pre><h4>Rails 6</h4><p>Rails 6, adds<a href="https://github.com/rails/rails/blob/98754de1412870d7dae9eba1c7bac944b9b90093/activerecord/lib/active_record/railties/databases.rake#L298">rails db:prepare</a>to get rid of running all the above tasks individually. <code>rails db:prepare</code> firstcalls the<a href="https://github.com/rails/rails/blob/98754de1412870d7dae9eba1c7bac944b9b90093/activerecord/lib/active_record/railties/databases.rake#L306"><code>migrate</code></a>to run the migrations, but if the database doesn't exist, <code>migrate</code> throws an<code>ActiveRecord::NoDatabaseError</code>. Once it is<a href="https://github.com/rails/rails/blob/98754de1412870d7dae9eba1c7bac944b9b90093/activerecord/lib/active_record/railties/databases.rake#L311">catched</a>,it performs the following operations:</p><ul><li>Creates the database.</li><li>Loads the schema.</li><li>Seeds the database.</li></ul><p>Thus, <code>rails db:prepare</code> saves a lot of time spent on running database tasksindividually while setting up an application and finishes it with just onecommand.</p><p>Here is the relevant <a href="https://github.com/rails/rails/pull/35768">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds guard against DNS Rebinding attacks]]></title>
       <author><name>Midhun Krishna</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-guard-against-dns-rebinding-attacks"/>
      <updated>2019-11-05T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-guard-against-dns-rebinding-attacks</id>
      <content type="html"><![CDATA[<p>In a DNS Rebinding attack, a malicious webpage runs client-side script when itis loaded, to attack endpoints within a given network.</p><h3>What is DNS Rebinding attack?</h3><p>DNS Rebinding can be summarized as follows.</p><ul><li>An unsuspecting victim is tricked into loading <code>rebinding.network</code> which isresolved by a DNS server controlled by a malicious entity.</li><li>Victims web browser sends a DNS query and gets the real IP address, say<code>24.56.78.99</code> of <code>http://rebinding.network</code>. This DNS server also sets a veryshort TTL value ( say 1 second ) on the response so that the client won'tcache this response for long.</li><li>The script on this webpage cannot attack services running in local network dueto CORS restrictions imposed by victims web browser. Instead it starts sendinga suspicious POST request to <code>http://rebinding.network/setup/reboot</code> with aJSON payload <code>{params: factory-reset}</code>.</li><li>First few requests are indeed sent to <code>24.56.78.99</code> (real IP address), withthe DNS info from the cache, but then the browser sends out a DNS query for<code>rebinding.network</code> when it observes that the cache has gone stale.</li><li>When the malicious DNS server gets the request for a second time, instead ofresponding with <code>24.56.78.99</code> (which is the real IP address of<code>rebinding.network</code>), it responds with <code>192.168.1.90</code>, an address at which, apoorly secured smart device runs.</li></ul><p>Using this exploit, an attacker is able to factory-reset a device which reliedon security provided by local network.</p><p>This attack is explained in much more detail<a href="https://medium.com/@brannondorsey/attacking-private-networks-from-the-internet-with-dns-rebinding-ea7098a2d325">in this blog post</a>.</p><h3>How does it affect Rails?</h3><p>Railss web console was particularly vulnerable to a Remote Code Execution (RCE)via a DNS Rebinding.</p><p><a href="http://benmmurphy.github.io/blog/2016/07/11/rails-webconsole-dns-rebinding/">In this blog post</a>,Ben Murphy goes into technical details of exploiting this vulnerability to openCalculator app (only works in OS X).</p><h3>How does Rails 6 mitigate DNS Rebinding?</h3><p>Rails mitigates DNS Rebinding attack by maintaining a whitelist of domains fromwhich it can receive requests. This is achieved with a new<a href="https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/host_authorization.rb">HostAuthorization</a>middleware. This middleware leverages the fact that HOST request header is<a href="https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name">a forbidden header</a>.</p><pre><code class="language-ruby"># taken from Rails documentation# Allow requests from subdomains like `www.product.com` and# `beta1.product.com`.Rails.application.config.hosts &lt;&lt; &quot;.*\.product\.com/&quot;</code></pre><p>In the above example, Rails would render a<a href="https://github.com/rails/rails/blob/ee0d0b1220adda0ee48f67cc4340ff4d702f6ed9/actionpack/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb">blocked host template</a>,if it receives requests from domains outside of above whitelist.</p><p>In development environment, default whitelist includes <code>0.0.0.0/0, ::0</code>(<a href="https://en.wikipedia.org/wiki/Default_route">CIDR notations for IPv4 and IPv6 default routes</a>)and <code>localhost</code>. For all other environments, config.hosts is empty and hostheader checks are not done.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveStorage::Blob#open]]></title>
       <author><name>Akhil Gautam</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activestorage-blob-open"/>
      <updated>2019-10-30T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activestorage-blob-open</id>
      <content type="html"><![CDATA[<p>Rails 6 adds<a href="https://edgeapi.rubyonrails.org/classes/ActiveStorage/Blob.html#method-i-open">ActiveStorage::Blob#open</a>which downloads a blob to a tempfile on disk and yields the tempfile.</p><pre><code class="language-ruby">&gt;&gt; blob = ActiveStorage::Blob.first=&gt; &lt;ActiveStorage::Blob id: 1, key: &quot;6qXeoibkvohP4VJiU4ytaEkH&quot;, filename: &quot;Screenshot 2019-08-26 at 10.24.40 AM.png&quot;, ..., created_at: &quot;2019-08-26 09:57:30&quot;&gt;&gt;&gt; blob.open do |tempfile|&gt;&gt;   puts tempfile.path  #do some processing&gt;&gt; end# Output: /var/folders/67/3n96myxs1rn5q_c47z7dthj80000gn/T/ActiveStorage-1-20190826-73742-mve41j.png</code></pre><h3>Processing a blob</h3><p>Let's take an example of a face detection application where the user images areuploaded. Let's assume that the images are uploaded on S3.</p><p>Before Rails 6, we will have to download the image in system's memory, processit with an image processing program and then send the processed image back tothe S3 bucket.</p><h4>The overhead</h4><p>If the processing operation is successful, the original file can be deleted fromthe system. We need to take care of a lot of uncertain events from the downloadphase till the phase when the processed image is created.</p><h4>ActiveStorage::Blob#open to the rescue</h4><p>ActiveStorage::Blob#open, abstracts away all this complications and gives us atempfile which is closed and unlinked once the block is executed.</p><p>1. <code>open</code> takes care of handling all the fanfare of getting a blob objectto a tempfile. 2. <code>open</code> takes care of the tempfile cleanup after theblock.</p><pre><code class="language-ruby">&gt; &gt; blob = ActiveStorage::Blob.first&gt; &gt; blob.open do |tempfile|&gt; &gt; tempfile #do some processing&gt; &gt; end# once the given block is executed# the tempfile is closed and unlinked=&gt; #&lt;Tempfile: (closed)&gt;</code></pre><p>By default, tempfiles are created in <code>Dir.tmpdir</code> directory, butActiveStorage::Blob#open also takes an optional argument <code>tmpdir</code> to set acustom directory for storing the tempfiles.</p><pre><code class="language-ruby">&gt; &gt; Dir.tmpdir&gt; &gt; =&gt; &quot;/var/folders/67/3n96myxs1rn5q_c47z7dthj80000gn/T&quot;&gt; &gt; blob = ActiveStorage::Blob.first&gt; &gt; blob.open(tmpdir: &quot;/desired/path/to/save&quot;) do |tempfile|&gt; &gt; puts tempfile.path #do some processing&gt; &gt; end&gt; &gt;</code></pre><p>Here is the relevant<a href="https://github.com/rails/rails/commit/9f95767979579f5761cb0d2bcccb67f3662349c5">commit</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActionMailer#email_address_with_name]]></title>
       <author><name>Taha Husain</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-actionmailer-email_address_with_name"/>
      <updated>2019-10-22T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-actionmailer-email_address_with_name</id>
      <content type="html"><![CDATA[<p>When using <code>ActionMailer::Base#mail</code>, if we want to display name and emailaddress of the user in email, we can pass a string in format<code>&quot;John Smith&quot; &lt;john@example.com&gt;</code> in <code>to</code>, <code>from</code> or <code>reply_to</code> options.</p><p>Before Rails 6, we had to join name and email address using string interpolationas mentioned in<a href="https://guides.rubyonrails.org/v5.2/action_mailer_basics.html#sending-email-with-name">Rails 5.2 Guides</a>and shown below.</p><pre><code class="language-ruby">  email_with_name = %(&quot;John Smith&quot; &lt;john@example.com&gt;)  mail(    to: email_with_name,    subject: 'Hey Rails 5.2!'  )</code></pre><p>Problem with string interpolation is it doesn't escape unexpected specialcharacters like quotes(&quot;) in the name.</p><p>Here's an example.</p><h3>Rails 5.2</h3><pre><code class="language-ruby">irb(main):001:0&gt; %(&quot;John P Smith&quot; &lt;john@example.com&gt;)=&gt; &quot;\&quot;John P Smith\&quot; &lt;john@example.com&gt;&quot;irb(main):002:0&gt; %('John &quot;P&quot; Smith' &lt;john@example.com&gt;)=&gt; &quot;'John \&quot;P\&quot; Smith' &lt;john@example.com&gt;&quot;</code></pre><p>Rails 6 adds<a href="https://github.com/rails/rails/pull/36454"><code>ActionMailer::Base#email_address_with_name</code></a>to join name and email address in the format <code>&quot;John Smith&quot; &lt;john@example.com&gt;</code>and take care of escaping special characters.</p><h3>Rails 6.1.0.alpha</h3><pre><code class="language-ruby">irb(main):001:0&gt; ActionMailer::Base.email_address_with_name(&quot;john@example.com&quot;, &quot;John P Smith&quot;)=&gt; &quot;John P Smith &lt;john@example.com&gt;&quot;irb(main):002:0&gt; ActionMailer::Base.email_address_with_name(&quot;john@example.com&quot;, 'John &quot;P&quot; Smith')=&gt; &quot;\&quot;John \\\&quot;P\\\&quot; Smith\&quot; &lt;john@example.com&gt;&quot;</code></pre><pre><code class="language-ruby">mail(to: email_address_with_name(&quot;john@example.com&quot;, &quot;John Smith&quot;),subject: 'Hey Rails 6!')</code></pre><p>Here's the relevant <a href="https://github.com/rails/rails/pull/36454">pull request</a>for this change.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 raises ArgumentError if param contains colon]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-raises-argumenterror-if-custom-param-contains-a-colon"/>
      <updated>2019-10-15T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-raises-argumenterror-if-custom-param-contains-a-colon</id>
      <content type="html"><![CDATA[<p>The<a href="https://guides.rubyonrails.org/routing.html#overriding-named-route-parameters">:param</a>option in routes is used to override default resource identifier i.e. :id.</p><p>Let's take for an example that we want product :name to be as the defaultresource identifier instead of :id while defining routes for <code>products</code>. In thiscase,<a href="https://guides.rubyonrails.org/routing.html#overriding-named-route-parameters">:param</a>option comes handy. We will see below how we can use this option.</p><p>Before Rails 6, if<a href="https://guides.rubyonrails.org/routing.html#overriding-named-route-parameters">resource custom param</a>contains a colon, Rails used to consider that as an extra param which should notbe the case because it sneaks in an extra param.</p><p>An <a href="https://github.com/rails/rails/issues/30467">issue</a> was raised in Aug, 2017which was later fixed in February this year.</p><p>So, now Rails 6 raises <code>ArgumentError</code> if a<a href="https://guides.rubyonrails.org/routing.html#overriding-named-route-parameters">resource custom param</a>contains a colon(:).</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><p>Let's create routes for <code>products</code> with custom param as <code>name/:pzn</code>.</p><pre><code class="language-ruby">&gt; &gt; Rails.application.routes.draw do&gt; &gt; resources :products, param: 'name/:pzn'&gt; &gt; end&gt; &gt;</code></pre><pre><code class="language-plaintext">\$ rake routes | grep productsproducts GET /products(.:format) products#indexPOST /products(.:format) products#createnew_product GET /products/new(.:format) products#newedit_product GET /products/:name/:pzn/edit(.:format) products#editproduct GET /products/:name/:pzn(.:format) products#showPATCH /products/:name/:pzn(.:format) products#updatePUT /products/:name/:pzn(.:format) products#updateDELETE /products/:name/:pzn(.:format) products#destroy</code></pre><p>As we can see, Rails also considers <code>:pzn</code> as a parameter.</p><p>Now let's see how it works in Rails 6.</p><h4>Rails 6.0.0.rc1</h4><pre><code class="language-ruby">&gt; &gt; Rails.application.routes.draw do&gt; &gt; resources :products, param: 'name/:pzn'&gt; &gt; end&gt; &gt;</code></pre><pre><code class="language-plaintext">\$ rake routes | grep productsrake aborted!ArgumentError: :param option can't contain colons/Users/amit/.rvm/gems/ruby-2.6.3/gems/actionpack-6.0.0.rc1/lib/action_dispatch/routing/mapper.rb:1149:in `initialize' /Users/amit/.rvm/gems/ruby-2.6.3/gems/actionpack-6.0.0.rc1/lib/action_dispatch/routing/mapper.rb:1472:in `new'/Users/amit/.rvm/gems/ruby-2.6.3/gems/actionpack-6.0.0.rc1/lib/action_dispatch/routing/mapper.rb:1472:in `block in resources'.........</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/issues/30467">issue</a> andthe <a href="https://github.com/rails/rails/pull/35236">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 introduces new code loader called Zeitwerk]]></title>
       <author><name>Midhun Krishna</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-introduces-new-code-loader-called-zeitwerk"/>
      <updated>2019-10-08T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-introduces-new-code-loader-called-zeitwerk</id>
      <content type="html"><![CDATA[<p><a href="https://github.com/fxn/zeitwerk">Zeitwerk</a> is the new code loader that<a href="https://weblog.rubyonrails.org/2019/2/22/zeitwerk-integration-in-rails-6-beta-2#autoloading-modes">comes with Rails 6 by default</a>.In addition to providing<a href="https://guides.rubyonrails.org/autoloading_and_reloading_constants.html">autoloading, eager loading, and reloading capabilities</a>,it also improves the classical code loader by being efficient and thread safe.According to the author of Zeitwerk, <a href="https://twitter.com/fxn">Xavier Noria</a>,one of the main motivations for writing Zeitwerk was to keep code DRY and toremove the brittle <code>require</code> calls.</p><p>Zeitwerk is available as a gem with no additional dependencies. It means anyregular Ruby project can use Zeitwerk.</p><h3>How to use Zeitwerk</h3><p>Zeitwerk is baked in a Rails 6 project, thanks to the<a href="https://github.com/rails/rails/blob/bfc9065d58508fb19dd1a4170406604dd3b3234a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb">Zeitwerk-Rails integration</a>.For a non-Rails project, adding the following into the project's entry pointsets up Zeitwerk.</p><pre><code class="language-ruby">loader = Zeitwerk::Loader.newloader.push_dir(...)loader.setup</code></pre><p>For gem maintainers, Zeitwerk provides the handy <code>.for_gem</code> utility method</p><p>The following example from Zeitwerk documentation illustrates the usage of<code>Zeitwerk.for_gem</code> method.</p><pre><code class="language-ruby">#lib/my_gem.rb (main file)require &quot;zeitwerk&quot;loader = Zeitwerk::Loader.for_gemloader.setupmodule MyGem  # Since the setup has been performed, at this point we are already  # able to reference project constants, in this case MyGem::MyLogger.  include MyLoggerend</code></pre><h3>How does Zeitwerk work?</h3><p>Before we look into Zeitwerk's internals, the following section provides a quickrefresher on constant-resolution in Ruby and how classical code loader of Railsworks.</p><p>Ruby's constant resolution looks for a constant in the following places.</p><ul><li>In each entry of Module.nesting</li><li>In each entry of Module.ancestors</li></ul><p>It triggers 'constant_missing' callback when it can't find the constant.</p><p>Ruby used to look for constants in Object.ancestors as well, but<a href="https://github.com/ruby/ruby/commit/44a2576f798b07139adde2d279e48fdbe71a0148">that seems not the case anymore</a>.An in-depth explanation of constant resolution can be found<a href="https://cirw.in/blog/constant-lookup.html">at Conrad Irwin's blog</a>.</p><h5>Classical Code Loader in Rails</h5><p>Classical code loader (code loader in Rails version &lt; 6.0) achieves autoloadingby overriding<a href="https://docs.ruby-lang.org/en/2.5.0/Module.html#method-i-const_missing">Module#const_missing</a>and loads the missing constant without the need for an explicit require call aslong as the code follows certain conventions.</p><ul><li>The file should be within a directory inActiveSupport::Dependencies.autoload_paths</li><li>A file should be named after the class, i.e Admin::RoutesController =&gt;admin/routes_controller.rb</li></ul><h5>Zeitwerk Mode</h5><p>Zeitwerk takes an entirely different approach in autoloading by registeringconstants to be autoloaded by Ruby.</p><p>Consider the following configuration in which Zeitwerk manages <code>lib</code> directoryand <code>lib</code> has <code>automobile.rb</code> file.</p><pre><code class="language-ruby">loader.push_dir('./lib')</code></pre><p>Zeitwerk then uses<a href="https://docs.ruby-lang.org/en/2.5.0/Module.html#method-i-autoload">Module.autoload</a>to tell Ruby that &quot;Automobile&quot; can be found in &quot;lib/automobile.rb&quot;.</p><pre><code class="language-ruby">autoload &quot;Automobile&quot;, &quot;lib/automobile.rb&quot;</code></pre><p>Unlike classical loader, Zeitwerk takes module nesting into account whileloading constants by leveraging the new Tracepoint API to go look for constantsdefined in subdirectories when a new class or module is defined.</p><p>Let us look at an example to understand this better.</p><pre><code class="language-ruby">class Automobile  # =&gt; Tracepoint hook triggers here.  # include Engineend</code></pre><p>When<a href="https://github.com/fxn/zeitwerk/blob/86064aba0c6e218c8bcc235b30c210a86c7c6ef8/lib/zeitwerk/explicit_namespace.rb#L78">the tracepoint hook</a>triggers, Zeitwerk checks for an <code>automobile</code> directory in the same level asautomobile.rb and sets up Module.autoload for that directory and all the files(in this case ./automobile/engine.rb) within that directory.</p><h3>Conclusion</h3><p>Previously in Rails, we had a code loader that was riddled with gotchas andstruggled to be thread safe. Zeitwerk does a better job by leveraging the newRuby standard API and matches Ruby's semantics for constants.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveSupport::ActionableError]]></title>
       <author><name>Taha Husain</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-active-support-actionable-error"/>
      <updated>2019-10-01T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-active-support-actionable-error</id>
      <content type="html"><![CDATA[<p>When working in a team on a Rails application, we often bump into<code>PendingMigrationError</code> or other errors that need us to run a rails command,rake task etc.</p><p>Rails introduced a way to resolve such frequent errors in development from errorpage itself.</p><p>Rails 6 added<a href="https://github.com/rails/rails/pull/34788"><code>ActiveSupport::ActionableError</code></a>module to define actions we want perform on errors, right from the error page.</p><p>For example, this is how <code>PendingMigrationError</code> page looks like in Rails 6.</p><p><img src="/blog_images/2019/rails-6-adds-active-support-actionable-error/rails-6.png" alt="How Actionable error looks like in Rails 6"></p><p>By default, a button is added on error screen that says <em>Run pendingmigrations</em>. Clicking on this button would dispatch <code>rails db:migrate</code> action.Page will reload once migrations run successfully.</p><p>We can also define custom actions to execute on errors.</p><h3>How to define actions on error?</h3><p>We need to include <code>ActiveSupport::ActionableError</code> module in our error class.We can monkey patch an existing error class or define custom error class.</p><p><code>#action</code> api is provided to define actions on error. First argument in<code>#action</code> is name of the action. This string would be displayed on the button onerror page. Second argument is a block where we can write commands or code tofix the error.</p><p>Let's take an example of seeding posts data from controller, if posts notalready present.</p><pre><code class="language-ruby"># app/controllers/posts_controller.rbclass PostsController &lt; ApplicationControllerdef index@posts = Post.allif @posts.empty?raise PostsMissingErrorendendend</code></pre><pre><code class="language-ruby"># app/errors/posts_missing_error.rbclass PostsMissingError &lt; StandardErrorinclude ActiveSupport::ActionableErroraction &quot;seed posts data&quot; doRails::Command.invoke 'posts:seed'endend</code></pre><pre><code class="language-ruby"># lib/tasks/posts.rakenamespace :posts dodesc 'posts seed task'task :seed doPost.create(title: 'First Post')endend</code></pre><pre><code class="language-ruby"># app/views/posts/index.html.erb&lt;% @posts.each do |post| %&gt;&lt;%= post.title %&gt;&lt;% end %&gt;</code></pre><p>Let's check <code>/posts</code> (<code>posts#index</code> action) when no posts are present. We wouldget an error page with an action button on it as shown below.</p><p><img src="/blog_images/2019/rails-6-adds-active-support-actionable-error/posts-missing-error.png" alt="Actionable error - seed posts data"></p><p>Clicking on <em>seed posts data</em> action button will run our rake task and createposts. Rails will automatically reload <code>/posts</code> after running rake task.</p><p><img src="/blog_images/2019/rails-6-adds-active-support-actionable-error/posts-index.png" alt="Posts index page"></p><p><a href="https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb"><code>ActionDispatch::ActionableExceptions</code></a>middleware takes care of invoking actions from error page.<code>ActionableExceptions</code> middleware dispatches action to <code>ActionableError</code> andredirects back when action block has successfully run. Action buttons are addedon error page from<a href="https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb">this middleware template</a>.</p><p>Checkout the <a href="https://github.com/rails/rails/pull/34788">pull request</a> for moreinformation on actionable error.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 add_foreign_key & remove_foreign_key SQLite3]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-add_foreign_key-and-remove_foreign_key-for-sqlite3"/>
      <updated>2019-09-24T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-add_foreign_key-and-remove_foreign_key-for-sqlite3</id>
      <content type="html"><![CDATA[<p>Rails provides<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key">add_foreign_key</a>to add foreign key constraint for a column on a table.</p><p>It also provides<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key">remove_foreign_key</a>to remove the foreign key constraint.</p><p>Before Rails 6,<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key">add_foreign_key</a>and<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key">remove_foreign_key</a>were not supported for SQLite3.</p><p>Rails 6 now adds this support. Now, we can create and remove foreign keyconstraints using<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key">add_foreign_key</a>and<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key">remove_foreign_key</a>in SQLite3.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><p>We have two tables named as <code>orders</code> and <code>users</code>. Now, let's add foreign keyconstraint of <code>users</code> in <code>orders</code> table using<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key">add_foreign_key</a>and then try removing it using<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key">remove_foreign_key</a>.</p><pre><code class="language-ruby">&gt;&gt; class AddUserReferenceToOrders &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     add_column :orders, :user_id, :integer&gt;&gt;     add_foreign_key :orders, :users&gt;&gt;   end&gt;&gt; end=&gt; :change&gt;&gt; AddUserReferenceToOrders.new.change-- add_column(:orders, :user_id, :integer)   (1.2ms)  ALTER TABLE &quot;orders&quot; ADD &quot;user_id&quot; integer   -&gt; 0.0058s-- add_foreign_key(:orders, :users)   -&gt; 0.0000s=&gt; nil&gt;&gt; class RemoveUserForeignKeyFromOrders &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     remove_foreign_key :orders, :users&gt;&gt;   end&gt;&gt; end=&gt; :change&gt;&gt; RemoveUserForeignKeyFromOrders.new.change-- remove_foreign_key(:orders, :users)   -&gt; 0.0001s=&gt; nil</code></pre><p>We can see that<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key">add_foreign_key</a>and<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key">remove_foreign_key</a>are ignored by <code>Rails 5.2</code> with SQLite3.</p><h4>Rails 6.0.0.rc1</h4><p>We have two tables named as <code>orders</code> and <code>users</code>. Now, let's add foreign keyconstraint of <code>users</code> in <code>orders</code> table using<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key">add_foreign_key</a>.</p><pre><code class="language-ruby">&gt;&gt; class AddUserReferenceToOrders &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     add_column :orders, :user_id, :integer&gt;&gt;     add_foreign_key :orders, :users&gt;&gt;   end&gt;&gt; end=&gt; :change&gt;&gt; AddUserReferenceToOrders.new.change-- add_column(:orders, :user_id, :integer)   (1.0ms)  SELECT sqlite_version(*)   (2.9ms)  ALTER TABLE &quot;orders&quot; ADD &quot;user_id&quot; integer   -&gt; 0.0091s-- add_foreign_key(:orders, :users)   (0.0ms)  begin transaction   (0.1ms)  PRAGMA foreign_keys   (0.1ms)  PRAGMA defer_foreign_keys   (0.0ms)  PRAGMA defer_foreign_keys = ON   (0.1ms)  PRAGMA foreign_keys = OFF   (0.2ms)  CREATE TEMPORARY TABLE &quot;aorders&quot; (&quot;id&quot; integer NOT NULL PRIMARY KEY, &quot;number&quot; varchar DEFAULT NULL, &quot;total&quot; decimal DEFAULT NULL, &quot;completed_at&quot; datetime DEFAULT NULL, &quot;created_at&quot; datetime(6) NOT NULL, &quot;updated_at&quot; datetime(6) NOT NULL, &quot;user_id&quot; integer DEFAULT NULL)   (0.1ms)  INSERT INTO &quot;aorders&quot; (&quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot;)                     SELECT &quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot; FROM &quot;orders&quot;   (0.3ms)  DROP TABLE &quot;orders&quot;   (0.1ms)  CREATE TABLE &quot;orders&quot; (&quot;id&quot; integer NOT NULL PRIMARY KEY, &quot;number&quot; varchar DEFAULT NULL, &quot;total&quot; decimal DEFAULT NULL, &quot;completed_at&quot; datetime DEFAULT NULL, &quot;created_at&quot; datetime(6) NOT NULL, &quot;updated_at&quot; datetime(6) NOT NULL, &quot;user_id&quot; integer DEFAULT NULL, CONSTRAINT &quot;fk_rails_f868b47f6a&quot;FOREIGN KEY (&quot;user_id&quot;)  REFERENCES &quot;users&quot; (&quot;id&quot;))   (0.1ms)  INSERT INTO &quot;orders&quot; (&quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot;)                     SELECT &quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot; FROM &quot;aorders&quot;   (0.1ms)  DROP TABLE &quot;aorders&quot;   (0.0ms)  PRAGMA defer_foreign_keys = 0   (0.0ms)  PRAGMA foreign_keys = 1   (0.6ms)  commit transaction   -&gt; 0.0083s=&gt; []&gt;&gt; class RemoveUserForeignKeyFromOrders &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     remove_foreign_key :orders, :users&gt;&gt;   end&gt;&gt; end=&gt; :change&gt;&gt; RemoveUserForeignKeyFromOrders.new.change-- remove_foreign_key(:orders, :users)   (1.4ms)  SELECT sqlite_version(*)   (0.0ms)  begin transaction   (0.0ms)  PRAGMA foreign_keys   (0.0ms)  PRAGMA defer_foreign_keys   (0.0ms)  PRAGMA defer_foreign_keys = ON   (0.0ms)  PRAGMA foreign_keys = OFF   (0.2ms)  CREATE TEMPORARY TABLE &quot;aorders&quot; (&quot;id&quot; integer NOT NULL PRIMARY KEY, &quot;number&quot; varchar DEFAULT NULL, &quot;total&quot; decimal DEFAULT NULL, &quot;completed_at&quot; datetime DEFAULT NULL, &quot;created_at&quot; datetime(6) NOT NULL, &quot;updated_at&quot; datetime(6) NOT NULL, &quot;user_id&quot; integer DEFAULT NULL)   (0.3ms)  INSERT INTO &quot;aorders&quot; (&quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot;)                     SELECT &quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot; FROM &quot;orders&quot;   (0.4ms)  DROP TABLE &quot;orders&quot;   (0.1ms)  CREATE TABLE &quot;orders&quot; (&quot;id&quot; integer NOT NULL PRIMARY KEY, &quot;number&quot; varchar DEFAULT NULL, &quot;total&quot; decimal DEFAULT NULL, &quot;completed_at&quot; datetime DEFAULT NULL, &quot;created_at&quot; datetime(6) NOT NULL, &quot;updated_at&quot; datetime(6) NOT NULL, &quot;user_id&quot; integer DEFAULT NULL)   (0.1ms)  INSERT INTO &quot;orders&quot; (&quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot;)                     SELECT &quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot; FROM &quot;aorders&quot;   (0.1ms)  DROP TABLE &quot;aorders&quot;   (0.0ms)  PRAGMA defer_foreign_keys = 0   (0.0ms)  PRAGMA foreign_keys = 1   (0.7ms)  commit transaction   -&gt; 0.0179s=&gt; []</code></pre><p>Now, let's remove foreign key constraint of <code>users</code> from <code>orders</code> table using<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key">remove_foreign_key</a>.</p><pre><code class="language-ruby">&gt;&gt; class RemoveUserForeignKeyFromOrders &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     remove_foreign_key :orders, :users&gt;&gt;   end&gt;&gt; end=&gt; :change&gt;&gt; RemoveUserForeignKeyFromOrders.new.change-- remove_foreign_key(:orders, :users)   (1.4ms)  SELECT sqlite_version(*)   (0.0ms)  begin transaction   (0.0ms)  PRAGMA foreign_keys   (0.0ms)  PRAGMA defer_foreign_keys   (0.0ms)  PRAGMA defer_foreign_keys = ON   (0.0ms)  PRAGMA foreign_keys = OFF   (0.2ms)  CREATE TEMPORARY TABLE &quot;aorders&quot; (&quot;id&quot; integer NOT NULL PRIMARY KEY, &quot;number&quot; varchar DEFAULT NULL, &quot;total&quot; decimal DEFAULT NULL, &quot;completed_at&quot; datetime DEFAULT NULL, &quot;created_at&quot; datetime(6) NOT NULL, &quot;updated_at&quot; datetime(6) NOT NULL, &quot;user_id&quot; integer DEFAULT NULL)   (0.3ms)  INSERT INTO &quot;aorders&quot; (&quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot;)                     SELECT &quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot; FROM &quot;orders&quot;   (0.4ms)  DROP TABLE &quot;orders&quot;   (0.1ms)  CREATE TABLE &quot;orders&quot; (&quot;id&quot; integer NOT NULL PRIMARY KEY, &quot;number&quot; varchar DEFAULT NULL, &quot;total&quot; decimal DEFAULT NULL, &quot;completed_at&quot; datetime DEFAULT NULL, &quot;created_at&quot; datetime(6) NOT NULL, &quot;updated_at&quot; datetime(6) NOT NULL, &quot;user_id&quot; integer DEFAULT NULL)   (0.1ms)  INSERT INTO &quot;orders&quot; (&quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot;)                     SELECT &quot;id&quot;,&quot;number&quot;,&quot;total&quot;,&quot;completed_at&quot;,&quot;created_at&quot;,&quot;updated_at&quot;,&quot;user_id&quot; FROM &quot;aorders&quot;   (0.1ms)  DROP TABLE &quot;aorders&quot;   (0.0ms)  PRAGMA defer_foreign_keys = 0   (0.0ms)  PRAGMA foreign_keys = 1   (0.7ms)  commit transaction   -&gt; 0.0179s=&gt; []</code></pre><p>We can see here that with Rails 6,<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key">add_foreign_key</a>and<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key">remove_foreign_key</a>work and were able to add and remove foreign key constraint respectively.</p><p>Here is the relevant <a href="https://github.com/rails/rails/pull/35212">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActionDispatch::Request::Session#dig]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-actiondispatch-request-session-dig"/>
      <updated>2019-09-18T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-actiondispatch-request-session-dig</id>
      <content type="html"><![CDATA[<p>Rails 6 added<a href="https://github.com/rails/rails/pull/32446">ActionDispatch::Request::Session#dig</a>.</p><p>This works the same way as Hash#dig (Link is not available).</p><p>It extracts the nested value specified by the sequence of keys.</p><p>Hash#dig (Link is not available) was introduced in <code>Ruby 2.3</code>.</p><p>Before Rails 6, we can achieve the same thing by first converting session to ahash and then calling Hash#dig (Link is not available) on it.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><p>Let's add some user information in session and use dig after converting it to ahash.</p><pre><code class="language-ruby">&gt;&gt; session[:user] = { email: 'jon@bigbinary.com', name: { first: 'Jon', last: 'Snow' }  }=&gt; {:email=&gt;&quot;jon@bigbinary.com&quot;, :name=&gt;{:first=&gt;&quot;Jon&quot;, :last=&gt;&quot;Snow&quot;}}&gt;&gt; session.to_hash=&gt; {&quot;session_id&quot;=&gt;&quot;5fe8cc73c822361e53e2b161dcd20e47&quot;, &quot;_csrf_token&quot;=&gt;&quot;gyFd5nEEkFvWTnl6XeVbJ7qehgL923hJt8PyHVCH/DA=&quot;, &quot;return_to&quot;=&gt;&quot;http://localhost:3000&quot;, &quot;user&quot;=&gt;{:email=&gt;&quot;jon@bigbinary.com&quot;, :name=&gt;{:first=&gt;&quot;Jon&quot;, :last=&gt;&quot;Snow&quot;}}}&gt;&gt; session.to_hash.dig(&quot;user&quot;, :name, :first)=&gt; &quot;Jon&quot;</code></pre><h4>Rails 6.0.0.rc1</h4><p>Let's add the same information to session and now use <code>dig</code> on session objectwithout converting it to a hash.</p><pre><code class="language-ruby">&gt;&gt; session[:user] = { email: 'jon@bigbinary.com', name: { first: 'Jon', last: 'Snow' }  }=&gt; {:email=&gt;&quot;jon@bigbinary.com&quot;, :name=&gt;{:first=&gt;&quot;Jon&quot;, :last=&gt;&quot;Snow&quot;}}&gt;&gt; session.dig(:user, :name, :first)=&gt; &quot;Jon&quot;</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/32446">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Marking arrays of translations safe using html suffix]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-marks-arrays-of-translations-as-trusted-safe-by-using-the-_html-suffix"/>
      <updated>2019-09-11T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-marks-arrays-of-translations-as-trusted-safe-by-using-the-_html-suffix</id>
      <content type="html"><![CDATA[<h3>Before Rails 6</h3><p>Before Rails 6, keys with the <code>_html</code> suffix in the language locale files areautomatically marked as HTML safe. These HTML safe keys do not get escaped whenused in the views.</p><pre><code class="language-yaml"># config/locales/en.ymlen:  home:    index:      title_html: &lt;h2&gt;We build web &amp; mobile applications&lt;/h2&gt;      description_html:        We are a dynamic team of &lt;em&gt;developers&lt;/em&gt; and &lt;em&gt;designers&lt;/em&gt;.      sections:        blogs:          title_html: &lt;h3&gt;Blogs &amp; publications&lt;/h3&gt;          description_html:            We regularly write our blog. Our blogs are covered by &lt;strong&gt;Ruby            Inside&lt;/strong&gt; and &lt;strong&gt;Ruby Weekly Newsletter&lt;/strong&gt;.</code></pre><pre><code class="language-erb">&lt;!-- app/views/home/index.html.erb --&gt;&lt;%= t('.title_html') %&gt;&lt;%= t('.description_html') %&gt;&lt;%= t('.sections.blogs.title_html') %&gt;&lt;%= t('.sections.blogs.description_html') %&gt;</code></pre><p>Once rendered, this page looks like this.</p><p><img src="/blog_images/2019/rails-6-marks-arrays-of-translations-as-trusted-safe-by-using-the-_html-suffix/before-rails-6-i18n-_html-suffix-without-array-key.png" alt="before-rails-6"></p><p>This way of marking translations as HTML safe by adding <code>_html</code> suffix to thekeys does not work as expected when the value is an array.</p><pre><code class="language-yaml"># config/locales/en.ymlen:home:index:title_html: &lt;h2&gt;We build web &amp; mobile applications&lt;/h2&gt;description_html: We are a dynamic team of &lt;em&gt;developers&lt;/em&gt; and &lt;em&gt;designers&lt;/em&gt;.sections:blogs:title_html: &lt;h3&gt;Blogs &amp; publications&lt;/h3&gt;description_html: We regularly write our blog. Our blogs are covered by &lt;strong&gt;Ruby Inside&lt;/strong&gt; and &lt;strong&gt;Ruby Weekly Newsletter&lt;/strong&gt;.services:title_html: &lt;h3&gt;Services we offer&lt;/h3&gt;list_html: - &lt;strong&gt;Ruby on Rails&lt;/strong&gt; - React.js &amp;#9883; - React Native &amp;#9883; &amp;#128241;</code></pre><pre><code class="language-erb">&lt;!-- app/views/home/index.html.erb --&gt;&lt;%= t('.title_html') %&gt;&lt;%= t('.description_html') %&gt;&lt;%= t('.sections.blogs.title_html') %&gt;&lt;%= t('.sections.blogs.description_html') %&gt;&lt;%= t('.sections.services.title_html') %&gt;&lt;ul&gt;  &lt;% t('.sections.services.list_html').each do |service| %&gt;    &lt;li&gt;&lt;%= service %&gt;&lt;/li&gt;  &lt;% end %&gt;&lt;ul&gt;</code></pre><p>The rendered page escapes the unsafe HTML while rendering the array oftranslations for the key <code>.sections.services.list_html</code> even though that key hasthe <code>_html</code> suffix.</p><p><img src="/blog_images/2019/rails-6-marks-arrays-of-translations-as-trusted-safe-by-using-the-_html-suffix/before-rails-6-i18n-_html-suffix-with-array-key.png" alt="before-rails-6"></p><p>A workaround is to manually mark all the translations in that array as HTML safeusing the methods such as <code>#raw</code> or <code>#html_safe</code>.</p><pre><code class="language-erb">&lt;!-- app/views/home/index.html.erb --&gt;&lt;%= t('.title_html') %&gt;&lt;%= t('.description_html') %&gt;&lt;%= t('.sections.blogs.title_html') %&gt;&lt;%= t('.sections.blogs.description_html') %&gt;&lt;%= t('.sections.services.title_html') %&gt;&lt;ul&gt;  &lt;% t('.sections.services.list_html').each do |service| %&gt;    &lt;li&gt;&lt;%= service.html_safe %&gt;&lt;/li&gt;  &lt;% end %&gt;&lt;ul&gt;</code></pre><p><img src="/blog_images/2019/rails-6-marks-arrays-of-translations-as-trusted-safe-by-using-the-_html-suffix/rails-6-i18n-array-key-with-_html-suffix.png" alt="rails-6-i18n"></p><h3>Arrays of translations are trusted as HTML safe by using the '_html' suffix in Rails 6</h3><p>In Rails 6, the unexpected behavior of not marking an array of translations asHTML safe even though the key of that array has the <code>_html</code> suffix is fixed.</p><pre><code class="language-yaml"># config/locales/en.ymlen:home:index:title_html: &lt;h2&gt;We build web &amp; mobile applications&lt;/h2&gt;description_html: We are a dynamic team of &lt;em&gt;developers&lt;/em&gt; and &lt;em&gt;designers&lt;/em&gt;.sections:blogs:title_html: &lt;h3&gt;Blogs &amp; publications&lt;/h3&gt;description_html: We regularly write our blog. Our blogs are covered by &lt;strong&gt;Ruby Inside&lt;/strong&gt; and &lt;strong&gt;Ruby Weekly Newsletter&lt;/strong&gt;.services:title_html: &lt;h3&gt;Services we offer&lt;/h3&gt;list_html: - &lt;strong&gt;Ruby on Rails&lt;/strong&gt; - React.js &amp;#9883; - React Native &amp;#9883; &amp;#128241;</code></pre><pre><code class="language-erb">&lt;!-- app/views/home/index.html.erb --&gt;&lt;%= t('.title_html') %&gt;&lt;%= t('.description_html') %&gt;&lt;%= t('.sections.blogs.title_html') %&gt;&lt;%= t('.sections.blogs.description_html') %&gt;&lt;%= t('.sections.services.title_html') %&gt;&lt;ul&gt;  &lt;% t('.sections.services.list_html').each do |service| %&gt;    &lt;li&gt;&lt;%= service %&gt;&lt;/li&gt;  &lt;% end %&gt;&lt;ul&gt;</code></pre><p><img src="/blog_images/2019/rails-6-marks-arrays-of-translations-as-trusted-safe-by-using-the-_html-suffix/rails-6-i18n-array-key-with-_html-suffix.png" alt="rails-6-i18n"></p><p>We can see above that we no longer need to manually mark the translations asHTML safe for the key <code>.sections.services.title_html</code> using the methods such as<code>#raw</code> or <code>#html_safe</code> since that key has the <code>_html</code> suffix.</p><hr><p>To learn more about this feature, please checkout<a href="https://github.com/rails/rails/pull/32361">rails/rails#32361</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds filter_attributes on ActiveRecord::Base]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activerecord-base-filter_attributes"/>
      <updated>2019-09-03T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activerecord-base-filter_attributes</id>
      <content type="html"><![CDATA[<p>A lot of times, we ask user for sensitive data such as password, credit cardnumber etc. We should not be able to see this information in logs. So, theremust be a way in Rails to filter out these parameters from logs.</p><p>Rails provides a way of doing this. We can add parameters to<a href="https://guides.rubyonrails.org/configuring.html#initializers">Rails.application.config.filter_parameters</a>.</p><p>There is one more way of doing this in Rails. We can also use<a href="https://api.rubyonrails.org/classes/ActionDispatch/Http/FilterParameters.html">https://api.rubyonrails.org/classes/ActionDispatch/Http/FilterParameters.html</a>.</p><p>However there is still a security issue when we call inspect on an ActiveRecordobject for logging purposes. In this case, Rails does not consider<a href="https://guides.rubyonrails.org/configuring.html#initializers">Rails.application.config.filter_parameters</a>and displays the sensitive information.</p><p>Rails 6 fixes this. It considers<a href="https://guides.rubyonrails.org/configuring.html#initializers">Rails.application.config.filter_parameters</a>while inspecting an object.</p><p>Rails 6 also provides an alternative way to filter columns on ActiveRecord levelby adding <a href="https://github.com/rails/rails/pull/33756">filter_attributes</a> onActiveRecord::Base.</p><p>In Rails 6, <a href="https://github.com/rails/rails/pull/33756">filter_attributes</a> onActiveRecord::Base takes priority over<a href="https://guides.rubyonrails.org/configuring.html#initializers">Rails.application.config.filter_parameters</a>.</p><p>Let's checkout how it works.</p><h4>Rails 6.0.0.rc1</h4><p>Let's create a user record and call inspect on it.</p><pre><code class="language-ruby">&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;  validates :email, :password, presence: true&gt;&gt; end=&gt; {:presence=&gt;true}&gt;&gt; User.create(email: 'john@bigbinary.com', password: 'john_wick_bigbinary')BEGIN  User Create (0.6ms)  INSERT INTO &quot;users&quot; (&quot;email&quot;, &quot;password&quot;, &quot;created_at&quot;, &quot;updated_at&quot;) VALUES ($1, $2, $3, $4) RETURNING &quot;id&quot;  [[&quot;email&quot;, &quot;john@bigbinary.com&quot;], [&quot;password&quot;, &quot;john_wick_bigbinary&quot;], [&quot;created_at&quot;, &quot;2019-05-17 21:34:34.504394&quot;], [&quot;updated_at&quot;, &quot;2019-05-17 21:34:34.504394&quot;]]COMMIT=&gt; #&lt;User id: 2, email: &quot;john@bigbinary.com&quot;, password: [FILTERED], created_at: &quot;2019-05-17 21:34:34&quot;, updated_at: &quot;2019-05-17 21:34:34&quot;&gt;</code></pre><p>We can see that <code>password</code> is filtered as it is added to<a href="https://guides.rubyonrails.org/configuring.html#initializers">Rails.application.config.filter_parameters</a>by default in <code>config/initializers/filter_parameter_logging.rb</code>.</p><p>Now let's add just <code>:email</code> to <code>User.filter_attributes</code></p><pre><code class="language-ruby">&gt;&gt; User.filter_attributes = [:email]=&gt; [:email]&gt;&gt; User.first.inspectSELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; &quot;#&lt;User id: 2, email: [FILTERED], password: \&quot;john_wick_bigbinary\&quot;, created_at: \&quot;2019-05-17 21:34:34\&quot;, updated_at: \&quot;2019-05-17 21:34:34\&quot;&gt;&quot;</code></pre><p>We can see here that <code>User.filter_attributes</code> took priority over<a href="https://guides.rubyonrails.org/configuring.html#initializers">Rails.application.config.filter_parameters</a>and removed filtering from password and filtered just email.</p><p>Now, let's add both <code>:email</code> and <code>:password</code> to <code>User.filter_attributes</code>.</p><pre><code class="language-ruby">&gt;&gt; User.filter_attributes = [:email, :password]=&gt; [:email, :password]&gt;&gt; User.first.inspect=&gt; &quot;#&lt;User id: 2, email: [FILTERED], password: [FILTERED], created_at: \&quot;2019-05-17 21:34:34\&quot;, updated_at: \&quot;2019-05-17 21:34:34\&quot;&gt;&quot;</code></pre><p>We can see that now both <code>email</code> and <code>password</code> are filtered out.</p><p>Here is the relevant <a href="https://github.com/rails/rails/pull/33756">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[ArgumentError for invalid :limit & :precision Rails 6]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-raises-argumenterror-for-invalid-limit-and-precision"/>
      <updated>2019-08-27T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-raises-argumenterror-for-invalid-limit-and-precision</id>
      <content type="html"><![CDATA[<p>Rails 6 raises <a href="https://apidock.com/ruby/ArgumentError">ArgumentError</a> when<code>:limit</code> and <code>:precision</code> are used with invalid datatypes.</p><p>Before Rails 6, it used to return<a href="https://api.rubyonrails.org/classes/ActiveRecord/ActiveRecordError.html">ActiveRecord::ActiveRecordError</a>.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><p>Let's create an orders table and try using <code>:limit</code> with a column named asquantity with data type <code>integer</code>.</p><pre><code class="language-ruby">&gt; &gt; class CreateOrders &lt; ActiveRecord::Migration[5.2]&gt; &gt; def change&gt; &gt; create_table :orders do |t|&gt; &gt; t.string :item&gt; &gt; t.integer :quantity, limit: 10&gt; &gt;&gt; &gt;       t.timestamps&gt; &gt;     end&gt; &gt;&gt; &gt; end&gt; &gt; end=&gt; :change&gt; &gt; CreateOrders.new.change&gt; &gt; -- create_table(:orders)=&gt; Traceback (most recent call last):2: from (irb):111: from (irb):3:in 'change'ActiveRecord::ActiveRecordError (No integer type has byte size 10. Use a numeric with scale 0 instead.)</code></pre><p>We can see that use of <code>:limit</code> with <code>integer</code> column raises<a href="https://api.rubyonrails.org/classes/ActiveRecord/ActiveRecordError.html">ActiveRecord::ActiveRecordError</a>in <code>Rails 5.2</code>.</p><p>Now let's try using <code>:precision</code> of <code>10</code> with a <code>datetime</code> column.</p><pre><code class="language-ruby">&gt; &gt; class CreateOrders &lt; ActiveRecord::Migration[5.2]&gt; &gt; def change&gt; &gt; create_table :orders do |t|&gt; &gt; t.string :item&gt; &gt; t.integer :quantity&gt; &gt; t.datetime :completed_at, precision: 10&gt; &gt;&gt; &gt;       t.timestamps&gt; &gt;     end&gt; &gt;&gt; &gt; end&gt; &gt; end=&gt; :change&gt; &gt; CreateOrders.new.change&gt; &gt; -- create_table(:orders)=&gt; Traceback (most recent call last):2: from (irb):121: from (irb):3:in 'change'ActiveRecord::ActiveRecordError (No timestamp type has precision of 10. The allowed range of precision is from 0 to 6)</code></pre><p>We can see that invalid value of <code>:precision</code> with datetime column also raises<a href="https://api.rubyonrails.org/classes/ActiveRecord/ActiveRecordError.html">ActiveRecord::ActiveRecordError</a>in <code>Rails 5.2</code>.</p><h4>Rails 6.0.0.rc1</h4><p>Let's create an orders table and try using <code>:limit</code> with a column named asquantity with data type <code>integer</code> in Rails 6.</p><pre><code class="language-ruby">&gt; &gt; class CreateOrders &lt; ActiveRecord::Migration[6.0]&gt; &gt; def change&gt; &gt; create_table :orders do |t|&gt; &gt; t.string :item&gt; &gt; t.integer :quantity, limit: 10&gt; &gt;&gt; &gt;       t.timestamps&gt; &gt;     end&gt; &gt;&gt; &gt; end&gt; &gt; end=&gt; :change&gt; &gt; CreateOrders.new.change&gt; &gt; -- create_table(:orders)=&gt; Traceback (most recent call last):2: from (irb):111: from (irb):3:in 'change'ArgumentError (No integer type has byte size 10. Use a numeric with scale 0 instead.)</code></pre><p>We can see that use of <code>:limit</code> with <code>integer</code> column raises<a href="https://apidock.com/ruby/ArgumentError">ArgumentError</a> in <code>Rails 6</code>.</p><p>Now let's try using <code>:precision</code> of <code>10</code> with a <code>datetime</code> column.</p><pre><code class="language-ruby">&gt; &gt; class CreateOrders &lt; ActiveRecord::Migration[6.0]&gt; &gt; def change&gt; &gt; create_table :orders do |t|&gt; &gt; t.string :item&gt; &gt; t.integer :quantity&gt; &gt; t.datetime :completed_at, precision: 10&gt; &gt;&gt; &gt;       t.timestamps&gt; &gt;     end&gt; &gt;&gt; &gt; end&gt; &gt; end=&gt; :change&gt; &gt; CreateOrders.new.change&gt; &gt; -- create_table(:orders)=&gt; Traceback (most recent call last):2: from (irb):121: from (irb):3:in 'change'ArgumentError (No timestamp type has precision of 10. The allowed range of precision is from 0 to 6)</code></pre><p>We can see that invalid value of <code>:precision</code> with datetime column also raises<a href="https://apidock.com/ruby/ArgumentError">ArgumentError</a> in <code>Rails 6</code>.</p><p>Here is the relevant <a href="https://github.com/rails/rails/pull/35887">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 Pass custom config to ActionCable::Server::Base]]></title>
       <author><name>Taha Husain</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-allows-passing-custom-configuration-to-actioncable-server-base"/>
      <updated>2019-08-21T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-allows-passing-custom-configuration-to-actioncable-server-base</id>
      <content type="html"><![CDATA[<p>Before Rails 6, Action Cable server used default configuration on boot up,unless custom configuration is provided explicitly.</p><p>Custom configuration can be mentioned in either <code>config/cable.yml</code> or<code>config/application.rb</code> as shown below.</p><pre><code class="language-ruby"># config/cable.ymlproduction:  url: redis://redis.example.com:6379  adapter: redis  channel_prefix: custom_</code></pre><p>Or</p><pre><code class="language-ruby"># config/application.rbconfig.action_cable.cable = { adapter: &quot;redis&quot;, channel_prefix: &quot;custom_&quot; }</code></pre><p>In some cases, we need another Action Cable server running separately fromapplication with a different set of configuration.</p><p>Problem is that both approaches mentioned earlier set Action Cable serverconfiguration on application boot up. This configuration can not be changed forthe second server.</p><p>Rails 6 has added a provision to pass custom configuration. Rails 6 allows us topass <code>ActionCable::Server::Configuration</code> object as an option when initializinga new Action Cable server.</p><pre><code class="language-ruby">config = ActionCable::Server::Configuration.newconfig.cable = { adapter: &quot;redis&quot;, channel_prefix: &quot;custom_&quot; }ActionCable::Server::Base.new(config: config)</code></pre><p>For more details on Action Cable configurations, head to<a href="https://edgeguides.rubyonrails.org/action_cable_overview.html#configuration">Action Cable docs</a>.</p><p>Here's the relevant <a href="https://github.com/rails/rails/pull/34714">pull request</a>for this change.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds support of symbol keys]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activesupport-hashwithindifferentaccess-assoc"/>
      <updated>2019-08-20T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activesupport-hashwithindifferentaccess-assoc</id>
      <content type="html"><![CDATA[<p>Rails 6 added support of symbol keys with<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/HashWithIndifferentAccess.html#method-i-assoc">ActiveSupport::HashWithIndifferentAccess#assoc</a>.</p><p>Please note that documentation of<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/HashWithIndifferentAccess.html#method-i-assoc">ActiveSupport::HashWithIndifferentAccess#assoc</a>in Rails 5.2 shows that<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/HashWithIndifferentAccess.html#method-i-assoc">ActiveSupport::HashWithIndifferentAccess#assoc</a>works with symbol keys but it doesn't.</p><p>In Rails 6,<a href="https://github.com/rails/rails/pull/35080">ActiveSupport::HashWithIndifferentAccess</a>implements a hash where string and symbol keys are considered to be the same.</p><p>Before Rails 6, <code>HashWithIndifferentAccess#assoc</code> used to work with just stringkeys.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><p>Let's create an object of<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/HashWithIndifferentAccess.html">ActiveSupport::HashWithIndifferentAccess</a>and call <code>assoc</code> on that object.</p><pre><code class="language-ruby">&gt;&gt; info = { name: 'Mark', email: 'mark@bigbinary.com' }.with_indifferent_access=&gt; {&quot;name&quot;=&gt;&quot;Mark&quot;, &quot;email&quot;=&gt;&quot;mark@bigbinary.com&quot;}&gt;&gt; info.assoc(:name)=&gt; nil&gt;&gt; info.assoc('name')=&gt; [&quot;name&quot;, &quot;Mark&quot;]</code></pre><p>We can see that <code>assoc</code> does not work with symbol keys with<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/HashWithIndifferentAccess.html">ActiveSupport::HashWithIndifferentAccess</a>in Rails 5.2.</p><h4>Rails 6.0.0.beta2</h4><p>Now, let's call <code>assoc</code> on the same hash in Rails 6 with both string and symbolkeys.</p><pre><code class="language-ruby">&gt;&gt; info = { name: 'Mark', email: 'mark@bigbinary.com' }.with_indifferent_access=&gt; {&quot;name&quot;=&gt;&quot;Mark&quot;, &quot;email&quot;=&gt;&quot;mark@bigbinary.com&quot;}&gt;&gt; info.assoc(:name)=&gt; [&quot;name&quot;, &quot;Mark&quot;]&gt;&gt; info.assoc('name')=&gt; [&quot;name&quot;, &quot;Mark&quot;]</code></pre><p>As we can see, <code>assoc</code> works perfectly fine with both string and symbol keyswith<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/HashWithIndifferentAccess.html">ActiveSupport::HashWithIndifferentAccess</a>in Rails 6.</p><p>Here is the relevant <a href="https://github.com/rails/rails/pull/35080">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 preserves status of #html_safe?]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-preserves-status-of-html_safe-on-sliced-and-multiplied-html-safe-strings"/>
      <updated>2019-08-13T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-preserves-status-of-html_safe-on-sliced-and-multiplied-html-safe-strings</id>
      <content type="html"><![CDATA[<h2>Before Rails 6</h2><p>Before Rails 6, calling <code>#html_safe?</code> on a slice of an HTML safe string returns<code>nil</code>.</p><pre><code class="language-ruby">&gt;&gt; html_content = &quot;&lt;div&gt;Hello, world!&lt;/div&gt;&quot;.html_safe# =&gt; &quot;&lt;div&gt;Hello, world!&lt;/div&gt;&quot;&gt;&gt; html_content.html_safe?# =&gt; true&gt;&gt; html_content[0..-1].html_safe?# =&gt; nil</code></pre><p>Also, before Rails 6, the <code>ActiveSupport::SafeBuffer#*</code> method does not preservethe HTML safe status as well.</p><pre><code class="language-ruby">&gt;&gt; line_break = &quot;&lt;br /&gt;&quot;.html_safe# =&gt; &quot;&lt;br /&gt;&quot;&gt;&gt; line_break.html_safe?# =&gt; true&gt;&gt; two_line_breaks = (line_break * 2)# =&gt; &quot;&lt;br /&gt;&lt;br /&gt;&quot;&gt;&gt; two_line_breaks.html_safe?# =&gt; nil</code></pre><h2>Rails 6 returns expected status of <code>#html_safe?</code></h2><p>In Rails 6, both of the above cases have been fixed properly.</p><p>Therefore, we will now get the status of <code>#html_safe?</code> as expected.</p><pre><code class="language-ruby">&gt;&gt; html_content = &quot;&lt;div&gt;Hello, world!&lt;/div&gt;&quot;.html_safe# =&gt; &quot;&lt;div&gt;Hello, world!&lt;/div&gt;&quot;&gt;&gt; html_content.html_safe?# =&gt; true&gt;&gt; html_content[0..-1].html_safe?# =&gt; true&gt;&gt; line_break = &quot;&lt;br /&gt;&quot;.html_safe# =&gt; &quot;&lt;br /&gt;&quot;&gt;&gt; line_break.html_safe?# =&gt; true&gt;&gt; two_line_breaks = (line_break * 2)# =&gt; &quot;&lt;br /&gt;&lt;br /&gt;&quot;&gt;&gt; two_line_breaks.html_safe?# =&gt; true</code></pre><p>Please check <a href="https://github.com/rails/rails/pull/33808">rails/rails#33808</a> and<a href="https://github.com/rails/rails/pull/36012">rails/rails#36012</a> for the relevantchanges.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Recyclable cache keys in Rails]]></title>
       <author><name>Taha Husain</name></author>
      <link href="https://www.bigbinary.com/blog/rails-adds-support-for-recyclable-cache-keys"/>
      <updated>2019-08-06T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-adds-support-for-recyclable-cache-keys</id>
      <content type="html"><![CDATA[<p><a href="https://github.com/rails/rails/pull/29092">Recyclable cache keys</a> or <em>cacheversioning</em> was introduced in Rails 5.2. Large applications frequently need toinvalidate their cache because cache store has limited memory. We can optimizecache storage and minimize cache miss using recyclable cache keys.</p><p>Recyclable cache keys is supported by all<a href="https://guides.rubyonrails.org/caching_with_rails.html#cache-stores">cache stores</a>that ship with Rails.</p><p>Before Rails 5.2, <code>cache_key</code>'s format was <em>{model_name}/{id}-{update_at}</em>. Here<code>model_name</code> and <code>id</code> are always constant for an object and <code>updated_at</code> changeson every update.</p><h4>Rails 5.1</h4><pre><code class="language-ruby">&gt;&gt; post = Post.last&gt;&gt; post.cache_key=&gt; &quot;posts/1-20190522104553296111&quot;# Update post&gt;&gt; post.touch&gt;&gt; post.cache_key=&gt; &quot;posts/1-20190525102103422069&quot; # cache_key changed</code></pre><p>In Rails 5.2, <code>#cache_key</code> returns <em>{model_name}/{id}</em> and new method<code>#cache_version</code> returns <em>{updated_at}</em>.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">&gt;&gt; ActiveRecord::Base.cache_versioning = true&gt;&gt; post = Post.last&gt;&gt; post.cache_key=&gt; &quot;posts/1&quot;&gt;&gt; post.cache_version=&gt; &quot;20190522070715422750&quot;&gt;&gt; post.cache_key_with_version=&gt; &quot;posts/1-20190522070715422750&quot;</code></pre><p>Let's update <code>post</code> instance and check <code>cache_key</code> and <code>cache_version</code>'sbehaviour.</p><pre><code class="language-ruby">&gt;&gt; post.touch&gt;&gt; post.cache_key=&gt; &quot;posts/1&quot; # cache_key remains same&gt;&gt; post.cache_version=&gt; &quot;20190527062249879829&quot; # cache_version changed</code></pre><p>To use cache versioning feature, we have to enable<code>ActiveRecord::Base.cache_versioning</code> configuration. By default<code>cache_versioning</code> config is set to false for backward compatibility.</p><p>We can enable cache versioning configuration globally as shown below.</p><pre><code class="language-ruby">ActiveRecord::Base.cache_versioning = true# orconfig.active_record.cache_versioning = true</code></pre><p>Cache versioning config can be applied at model level.</p><pre><code class="language-ruby">class Post &lt; ActiveRecord::Base  self.cache_versioning = trueend# Or, when setting `#cache_versioning` outside the model -Post.cache_versioning = true</code></pre><p>Let's understand the problem step by step with cache keys before Rails 5.2.</p><h4>Rails 5.1 (without cache versioning)</h4><p><em>1. Write <code>post</code> instance to cache using<a href="https://apidock.com/rails/ActiveSupport/Cache/Store/fetch"><code>fetch</code></a> api.</em></p><pre><code class="language-ruby">&gt;&gt; before_update_cache_key = post.cache_key=&gt; &quot;posts/1-20190527062249879829&quot;&gt;&gt; Rails.cache.fetch(before_update_cache_key) { post }=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 06:22:49&quot;&gt;</code></pre><p><em>2. Update <code>post</code> instance using<a href="https://apidock.com/rails/ActiveRecord/Persistence/touch"><code>touch</code></a>.</em></p><pre><code class="language-ruby">&gt;&gt; post.touch   (0.1ms)  begin transaction  Post Update (1.6ms)  UPDATE &quot;posts&quot; SET &quot;updated_at&quot; = ? WHERE &quot;posts&quot;.&quot;id&quot; = ?  [[&quot;updated_at&quot;, &quot;2019-05-27 08:01:52.975653&quot;], [&quot;id&quot;, 1]]   (1.2ms)  commit transaction=&gt; true</code></pre><p><em>3. Verify stale <code>cache_key</code> in cache store.</em></p><pre><code class="language-ruby">&gt;&gt; Rails.cache.fetch(before_update_cache_key)=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 06:22:49&quot;&gt;</code></pre><p><em>4. Write updated <code>post</code> instance to cache using new <code>cache_key</code>.</em></p><pre><code class="language-ruby">&gt;&gt; after_update_cache_key = post.cache_key=&gt; &quot;posts/1-20190527080152975653&quot;&gt;&gt; Rails.cache.fetch(after_update_cache_key) { post }=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 08:01:52&quot;&gt;</code></pre><p><em>5. Cache store now has two copies of <code>post</code> instance.</em></p><pre><code class="language-ruby">&gt;&gt; Rails.cache.fetch(before_update_cache_key)=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 06:22:49&quot;&gt;&gt;&gt; Rails.cache.fetch(after_update_cache_key)=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 08:01:52&quot;&gt;</code></pre><p><em>cache_key</em> and its associated instance becomes irrelevant as soon as aninstance is updated. But it stays in cache store until it is manuallyinvalidated.</p><p>This sometimes result in overflowing cache store with stale keys and data. Inapplications that extensively use cache store, a huge chunk of cache store getsfilled with stale data frequently.</p><p>Now let's take a look at the same example. This time with <em>cache versioning</em> tounderstand how recyclable cache keys help optimize cache storage.</p><h4>Rails 5.2 (cache versioning)</h4><p><em>1. Write <code>post</code> instance to cache store with <code>version</code> option.</em></p><pre><code class="language-ruby">&gt;&gt; ActiveRecord::Base.cache_versioning = true&gt;&gt; post = Post.last&gt;&gt; cache_key = post.cache_key=&gt; &quot;posts/1&quot;&gt;&gt; before_update_cache_version = post.cache_version=&gt; &quot;20190527080152975653&quot;&gt;&gt; Rails.cache.fetch(cache_key, version: before_update_cache_version) { post }=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 08:01:52&quot;&gt;</code></pre><p><em>2. Update <code>post</code> instance.</em></p><pre><code class="language-ruby">&gt;&gt; post.touch   (0.1ms)  begin transaction  Post Update (0.4ms)  UPDATE &quot;posts&quot; SET &quot;updated_at&quot; = ? WHERE &quot;posts&quot;.&quot;id&quot; = ?  [[&quot;updated_at&quot;, &quot;2019-05-27 09:09:15.651029&quot;], [&quot;id&quot;, 1]]   (0.7ms)  commit transaction=&gt; true</code></pre><p><em>3. Verify stale <code>cache_version</code> in cache store.</em></p><pre><code class="language-ruby">&gt;&gt; Rails.cache.fetch(cache_key, version: before_update_cache_version)=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 08:01:52&quot;&gt;</code></pre><p><em>4. Write updated <code>post</code> instance to cache.</em></p><pre><code class="language-ruby">&gt;&gt; after_update_cache_version = post.cache_version=&gt; &quot;20190527090915651029&quot;&gt;&gt; Rails.cache.fetch(cache_key, version: after_update_cache_version) { post }=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 09:09:15&quot;&gt;</code></pre><p><em>5. Cache store has replaced old copy of <code>post</code> with new version automatically.</em></p><pre><code class="language-ruby">&gt;&gt; Rails.cache.fetch(cache_key, version: before_update_cache_version)=&gt; nil&gt;&gt; Rails.cache.fetch(cache_key, version: after_update_cache_version)=&gt; #&lt;Post id: 1, title: &quot;First Post&quot;, created_at: &quot;2019-05-22 17:23:22&quot;, updated_at: &quot;2019-05-27 09:09:15&quot;&gt;</code></pre><p>Above example shows how recyclable cache keys maintains single, latest copy ofan instance. Stale versions are removed automatically when new version is addedto cache store.</p><p><em>Rails 6</em> added <code>#cache_versioning</code> for <code>ActiveRecord::Relation</code>.</p><p><code>ActiveRecord::Base.collection_cache_versioning</code> configuration should be enabledto use cache versioning feature on collections. It is set to false by default.</p><p>We can enable this configuration as shown below.</p><pre><code class="language-ruby">ActiveRecord::Base.collection_cache_versioning = true# orconfig.active_record.collection_cache_versioning = true</code></pre><p>Before Rails 6, <code>ActiveRecord::Relation</code> had <code>cache_key</code> in format<code>{table_name}/query-{query-hash}-{count}-{max(updated_at)}</code>.</p><p>In Rails 6, cache_key is split in stable part <code>cache_key</code> -<code>{table_name}/query-{query-hash}</code> and volatile part <code>cache_version</code> -<code>{count}-{max(updated_at)}</code>.</p><p>For more information, check out<a href="https://blog.bigbinary.com/2016/02/02/activerecord-relation-cache-key.html">blog on ActiveRecord::Relation#cache_key in Rails 5</a>.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">&gt;&gt; posts = Post.all&gt;&gt; posts.cache_key=&gt; &quot;posts/query-00644b6a00f2ed4b925407d06501c8fb-3-20190522172326885804&quot;</code></pre><h4>Rails 6</h4><pre><code class="language-ruby">&gt;&gt; ActiveRecord::Base.collection_cache_versioning = true&gt;&gt; posts = Post.all&gt;&gt; posts.cache_key=&gt; &quot;posts/query-00644b6a00f2ed4b925407d06501c8fb&quot;&gt;&gt; posts.cache_version=&gt; &quot;3-20190522172326885804&quot;</code></pre><p>Cache versioning works similarly for <code>ActiveRecord::Relation</code> as<code>ActiveRecord::Base</code>.</p><p>In case of <code>ActiveRecord::Relation</code>, if number of records change and/orrecord(s) are updated, then same <code>cache_key</code> is written to cache store with new<code>cache_version</code> and updated records.</p><h2>Conclusion</h2><p>Previously, cache invalidation had to be done manually either by deleting cacheor setting cache expire duration. Cache versioning invalidates stale dataautomatically and keeps latest copy of data, saving on storage and performancedrastically.</p><p>Check out the <a href="https://github.com/rails/rails/pull/29092">pull request</a> and<a href="https://github.com/rails/rails/commit/4f2ac80d4cdb01c4d3c1765637bed76cc91c1e35">commit</a>for more details.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 deprecates where.not as NOR & Rails 6.1 as NAND]]></title>
       <author><name>Taha Husain</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-deprecates-where-not-working-as-nor-and-will-change-to-nand-in-rails-6-1"/>
      <updated>2019-07-31T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-deprecates-where-not-working-as-nor-and-will-change-to-nand-in-rails-6-1</id>
      <content type="html"><![CDATA[<p>A notable deprecation warning has been added in Rails 6 when using <code>where.not</code>with multiple attributes.</p><p>Before Rails 6, if we use <code>where.not</code> with multiple attributes, it applieslogical <em>NOR (NOT(A) AND NOT(B))</em> in <em>WHERE</em> clause of the query. This does notalways work as expected.</p><p>Let's look at an example to understand this better.</p><p>We have <code>Post</code> model with a polymorphic association.</p><h5>Rails 5.2</h5><pre><code class="language-ruby">&gt;&gt; Post.all=&gt; #&lt;ActiveRecord::Relation [#&lt;Post id: 1, title: &quot;First Post&quot;, source_type: &quot;Feed&quot;, source_id: 100&gt;,#&lt;Post id: 2, title: &quot;Second Post&quot;, source_type: &quot;Feed&quot;, source_id: 101&gt;]&gt;&gt;&gt; Post.where(source_type: &quot;Feed&quot;, source_id: 100)=&gt; #&lt;ActiveRecord::Relation [#&lt;Post id: 1, title: &quot;First Post&quot;, source_type: &quot;Feed&quot;, source_id: 100&gt;]&gt;&gt;&gt; Post.where.not(source_type: &quot;Feed&quot;, source_id: 100)=&gt; #&lt;ActiveRecord::Relation []&gt;</code></pre><p>In the last query, we expect ActiveRecord to fetch one record.</p><p>Let's check SQL generated for the above case.</p><pre><code class="language-ruby">&gt;&gt; Post.where.not(source_type: &quot;Feed&quot;, source_id: 100).to_sql=&gt; SELECT &quot;posts&quot;.* FROM &quot;posts&quot; WHERE &quot;posts&quot;.&quot;source_type&quot; != 'Feed' AND &quot;posts&quot;.&quot;source_id&quot; != 100</code></pre><p><code>where.not</code> applies <em>AND</em> to the negation of <code>source_type</code> and <code>source_id</code>, andfails to fetch expected records.</p><p>In such cases, correct implementation of <code>where.not</code> would be logical <em>NAND</em><em>(NOT(A) OR NOT(B))</em>.</p><p>Let us query <code>posts</code> table using NAND this time.</p><pre><code class="language-ruby">&gt;&gt; Post.where(&quot;source_type != 'Feed' OR source_id != 100&quot;)   SELECT &quot;posts&quot;.* FROM &quot;posts&quot; WHERE (source_type != 'Feed' OR source_id != 100)=&gt; #&lt;ActiveRecord::Relation [#&lt;Post id: 2, title: &quot;Second Post&quot;, source_type: &quot;Feed&quot;, source_id: 101&gt;]&gt;</code></pre><p>Above query works as expected and returns one record. Rails 6.1 will change<code>where.not</code> working to NAND similar to the above query.</p><h5>Rails 6.0.0.rc1</h5><pre><code class="language-ruby">&gt;&gt; Post.where.not(source_type: &quot;Feed&quot;, source_id: 100)DEPRECATION WARNING: NOT conditions will no longer behave as NOR in Rails 6.1. To continue using NOR conditions, NOT each conditions manually (`.where.not(:source_type =&gt; ...).where.not(:source_id =&gt; ...)`). (called from irb_binding at (irb):1)=&gt; #&lt;ActiveRecord::Relation []&gt;</code></pre><p>It is well mentioned in deprecation warning that if we wish to use <em>NOR</em>condition with multiple attributes, we can chain multiple <code>where.not</code> using asingle predicate.</p><pre><code class="language-ruby">&gt;&gt; Post.where.not(source_type: &quot;Feed&quot;).where.not(source_id: 100)</code></pre><p>Here's the relevant <a href="https://github.com/rails/rails/issues/31209">discussion</a>and <a href="https://github.com/rails/rails/pull/36029">pull request</a> for this change.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds support for Optimizer Hints]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-supports-optimizer-hints"/>
      <updated>2019-07-30T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-supports-optimizer-hints</id>
      <content type="html"><![CDATA[<p>Rails 6 has added support to provide optimizer hints.</p><h2>What is Optimizer Hints?</h2><p>Many relational database management systems (RDBMS) have a query optimizer. Thejob of the query optimizer is to determine the most efficient and fast plan toexecute a given SQL query. Query optimizer has to consider all possible queryexecution plans before it can determine which plan is the optimal plan forexecuting the given SQL query and then compile and execute that query.</p><p>An optimal plan is chosen by the query optimizer by calculating the cost of eachpossible plans. Typically, when the number of tables referenced in a join queryincreases, then the time spent in query optimization grows exponentially whichoften affects the system's performance. The fewer the execution plans the queryoptimizer needs to evaluate, the lesser time is spent in compiling and executingthe query.</p><p>As an application designer, we might have more context about the data stored inour database. With the contextual knowledge about our database, we might be ableto choose a more efficient execution plan than the query optimizer.</p><p>This is where the optimizer hints or optimizer guidelines come into picture.</p><p>Optimizer hints allow us to control the query optimizer to choose a certainquery execution plan based on the specific criteria. In other words, we can hintthe optimizer to use or ignore certain optimization plans using optimizer hints.</p><p>Usually, optimizer hints should be provided only when executing a complex queryinvolving multiple table joins.</p><p>Note that the optimizer hints only affect an individual SQL statement. To alterthe optimization strategies at the global level, there are different mechanismssupported by different databases. Optimizer hints provide finer control overother mechanisms which allow altering optimization plans by other means.</p><p>Optimizer hints are supported by many databases such as<a href="https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html">MySQL</a>,<a href="https://pghintplan.osdn.jp/pg_hint_plan.html">PostgreSQL with the help of <code>pg_hint_plan</code> extension</a>,<a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/tgsql/influencing-the-optimizer.html">Oracle</a>,<a href="https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-query?view=sql-server-2017">MS SQL</a>,<a href="https://www.ibm.com/support/knowledgecenter/en/SSEPGG_11.1.0/com.ibm.db2.luw.admin.perf.doc/doc/c0070117.html">IBM DB2</a>,etc. with varying syntax and options.</p><h2>Optimizer Hints in Rails 6</h2><p>Before Rails 6, we have to execute a raw SQL query to use the optimizer hints.</p><pre><code class="language-ruby">query = &quot;SELECT            /*+ JOIN_ORDER(articles, users) MAX_EXECUTION_TIME(60000) */            articles.*         FROM articles         INNER JOIN users         ON users.id = articles.user_id         WHERE (published_at &gt; '2019-02-17 13:15:44')        &quot;.squishActiveRecord::Base.connection.execute(query)</code></pre><p>In the above query, we provided two optimizer hints to MySQL .</p><pre><code class="language-plaintext">/*+ HINT_HERE ANOTHER_HINT_HERE ... */</code></pre><p>Another approach to use optimizer hints prior to Rails 6 is to<a href="https://gist.github.com/kamipo/4c8539f0ce4acf85075cf5a6b0d9712e">use a monkey patch like this</a>.</p><p>In Rails 6, using optimizer hints is easier.</p><p>The same example looks like this in Rails 6.</p><pre><code class="language-ruby">Article  .joins(:user)  .where(&quot;published_at &gt; ?&quot;, 2.months.ago)  .optimizer_hints(    &quot;JOIN_ORDER(articles, users)&quot;,    &quot;MAX_EXECUTION_TIME(60000)&quot;  )</code></pre><p>This produces the same SQL query as above but the result is of type<code>ActiveRecord::Relation</code>.</p><p>In PostgreSQL (using the <code>pg_hint_plan</code> extension), the optimizer hints have adifferent syntax.</p><pre><code class="language-ruby">Article  .joins(:user)  .where(&quot;published_at &gt; ?&quot;, 2.months.ago)  .optimizer_hints(&quot;Leading(articles users)&quot;, &quot;SeqScan(articles)&quot;)</code></pre><p>Please checkout the documentation of each database separately to learn thesupport and syntax of optimizer hints.</p><p>To learn more, please<a href="https://github.com/rails/rails/pull/35615">checkout this PR</a> which introducedthe <code>#optimization_hints</code> method to Rails 6.</p><h2>Bonus example: Using optimizer hints to speedup a slow SQL statement in MySQL</h2><p>Consider that we have <code>articles</code> table with some indexes.</p><pre><code class="language-ruby">class CreateArticles &lt; ActiveRecord::Migration[6.0]  def change    create_table :articles do |t|      t.string :title, null: false      t.string :slug, null: false      t.references :user      t.datetime :published_at      t.text :description      t.timestamps      t.index :slug, unique: true      t.index [:published_at]      t.index [:slug, :user_id]      t.index [:published_at, :user_id]      t.index [:title, :slug]    end  endend</code></pre><p>Let's try to fetch all the articles which have been published in the last 2months.</p><pre><code class="language-ruby">&gt;&gt; Article.joins(:user).where(&quot;published_at &gt; ?&quot;, 2.months.ago)# Article Load (10.5ms)  SELECT `articles`.* FROM `articles` INNER JOIN `users` ON `users`.`id` = `articles`.`user_id` WHERE (published_at &gt; '2019-02-17 11:38:18.647296')=&gt; #&lt;ActiveRecord::Relation [#&lt;Article id: 20, title: &quot;Article 20&quot;, slug: &quot;article-20&quot;, user_id: 1, ...]&gt;</code></pre><p>Let's use <code>EXPLAIN</code> to investigate why it is taking 10.5ms to execute thisquery.</p><pre><code class="language-ruby">&gt;&gt; Article.joins(:user).where(&quot;published_at &gt; ?&quot;, 2.months.ago).explain# Article Load (13.9ms)  SELECT `articles`.* FROM `articles` INNER JOIN `users` ON `users`.`id` = `articles`.`user_id` WHERE (published_at &gt; '2019-02-17 11:39:05.380577')=&gt; # EXPLAIN for: SELECT `articles`.* FROM `articles` INNER JOIN `users` ON `users`.`id` = `articles`.`user_id` WHERE (published_at &gt; '2019-02-17 11:39:05.380577')# +--------+----------+----------------+-----------+------+----------+-------+# | select |   table  | possible_keys  | key       | rows | filtered | Extra |# | _type  |          |                |           |      |          |       |# +--------+----------+----------------+-----------+------+----------+-------+# | SIMPLE |   users  | PRIMARY        | PRIMARY   | 2    | 100.0    | Using |# |        |          |                |           |      |          | index |# +--------+----------+----------------+-----------+------+----------+-------+# | SIMPLE | articles | index          | index     | 9866 | 10.0     | Using |# |        |          | _articles      | _articles |      |          | where |# |        |          | _on_user_id,   | _on       |      |          |       |# |        |          | index          | _user_id  |      |          |       |# |        |          | _articles      |           |      |          |       |# |        |          | _on            |           |      |          |       |# |        |          | _published_at, |           |      |          |       |# |        |          | index          |           |      |          |       |# |        |          | _articles      |           |      |          |       |# |        |          | _on            |           |      |          |       |# |        |          | _published_at  |           |      |          |       |# |        |          | _and_user_id   |           |      |          |       |# +--------+----------+----------------+-----------+------+----------+-------+</code></pre><p>According to the above table, it appears that the query optimizer is considering<code>users</code> table first and then the <code>articles</code> table.</p><p>The <code>rows</code> column indicates the estimated number of rows the query optimizermust examine to execute the query.</p><p>The<a href="https://dev.mysql.com/doc/refman/8.0/en/explain-output.html#explain_filtered"><code>filtered</code></a>column indicates an estimated percentage of table rows that will be filtered bythe table condition.</p><p>The formula <code>rows x filtered</code> gives the number of rows that will be joined withthe following table.</p><p>Also,</p><ul><li>For <code>users</code> table, the number of rows to be joined with the following table is<code>2 x 100% = 2</code>,</li><li>For <code>articles</code> table, the number of rows to be joined with the following tableis <code>500 * 7.79 = 38.95</code>.</li></ul><p>Since the <code>articles</code> tables contain more records which references very fewrecords from the <code>users</code> table, it would be better to consider the <code>articles</code>table first and then the <code>users</code> table.</p><p>We can hint MySQL to consider the <code>articles</code> table first as follows.</p><pre><code class="language-ruby">&gt;&gt; Article.joins(:user).where(&quot;published_at &gt; ?&quot;, 2.months.ago).optimizer_hints(&quot;JOIN_ORDER(articles, users)&quot;)# Article Load (2.2ms)  SELECT `articles`.* FROM `articles` INNER JOIN `users` ON `users`.`id` = `articles`.`user_id` WHERE (published_at &gt; '2019-02-17 11:54:06.230651')=&gt; #&lt;ActiveRecord::Relation [#&lt;Article id: 20, title: &quot;Article 20&quot;, slug: &quot;article-20&quot;, user_id: 1, ...]&gt;</code></pre><p>Note that it took 2.2ms now to fetch the same records by providing<code>JOIN_ORDER(articles, users)</code> optimization hint.</p><p>Let's try to <code>EXPLAIN</code> what changed by using this <code>JOIN_ORDER(articles, users)</code>optimization hint.</p><pre><code class="language-ruby">&gt;&gt; Article.joins(:user).where(&quot;published_at &gt; ?&quot;, 2.months.ago).optimizer_hints(&quot;JOIN_ORDER(articles, users)&quot;).explain# Article Load (4.1ms)  SELECT /*+ JOIN_ORDER(articles, users) */ `articles`.* FROM `articles` INNER JOIN `users` ON `users`.`id` = `articles`.`user_id` WHERE (published_at &gt; '2019-02-17 11:55:24.335152')=&gt; # EXPLAIN for: SELECT /*+ JOIN_ORDER(articles, users) */ `articles`.* FROM `articles` INNER JOIN `users` ON `users`.`id` = `articles`.`user_id` WHERE (published_at &gt; '2019-02-17 11:55:24.335152')# +--------+----------+----------------+-----------+------+----------+--------+# | select |   table  | possible_keys  | key       | rows | filtered | Extra  |# | _type  |          |                |           |      |          |        |# +--------+----------+----------------+-----------+------+----------+--------+# | SIMPLE | articles | index          | index     | 769  | 100.0    | Using  |# |        |          | _articles      | _articles |      |          | index  |# |        |          | _on_user_id,   | _on       |      |          | condi  |# |        |          | index          | _publish  |      |          | tion;  |# |        |          | _articles      | ed_at,    |      |          | Using  |# |        |          | _on            |           |      |          | where  |# |        |          | _published_at, |           |      |          |        |# |        |          | index          |           |      |          |        |# |        |          | _articles      |           |      |          |        |# |        |          | _on            |           |      |          |        |# |        |          | _published_at  |           |      |          |        |# |        |          | _and_user_id   |           |      |          |        |# +--------+----------+----------------+-----------+------+----------+--------+# | SIMPLE | users    | PRIMARY        | PRIMARY   | 2    | 100.0    | Using  |# |        |          |                |           |      |          | index  |# +--------+----------+----------------+-----------+------+----------+--------+</code></pre><p>The result of the <code>EXPLAIN</code> query shows that the <code>articles</code> table was consideredfirst and then the <code>users</code> table as expected. We can also see that the<code>index_articles_on_published_at</code> index key was considered from the possible keysto execute the given query. The <code>filtered</code> column for both tables shows that thenumber of filtered rows was 100% which means no filtering of rows occurred.</p><p>We hope this example helps in understanding how to use <code>#explain</code> and<code>#optimization_hints</code> methods in order to investigate and debug the performanceissues and then fixing it.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 reports object allocations while rendering]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-reports-object-allocations-made-while-rendering-view-templates"/>
      <updated>2019-07-23T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-reports-object-allocations-made-while-rendering-view-templates</id>
      <content type="html"><![CDATA[<p>Recently, Rails 6<a href="https://github.com/rails/rails/pull/33449">added <code>allocations</code> feature</a> to<code>ActiveSupport::Notifications::Event</code>. Using this feature, an event subscribercan see how many number of objects were allocated during the event's start timeand end time. We have written in detail about this feature<a href="https://blog.bigbinary.com/2019/04/24/rails-6-adds-cpu-time-idle-time-and-allocations-to-activesupport-notifications-event.html">here</a>.</p><p>By taking the benefit of this feature, Rails 6 now reports the allocations madewhile rendering a view template, a partial and a collection.</p><pre><code class="language-plaintext">Started GET &quot;/articles&quot; for ::1 at 2019-04-15 17:24:09 +0530Processing by ArticlesController#index as HTMLRendering articles/index.html.erb within layouts/applicationRendered shared/\_ad_banner.html.erb (Duration: 0.1ms | Allocations: 6)Article Load (1.3ms) SELECT &quot;articles&quot;.\* FROM &quot;articles&quot; app/views/articles/index.html.erb:5Rendered collection of articles/\_article.html.erb [100 times] (Duration: 6.1ms | Allocations: 805)Rendered articles/index.html.erb within layouts/application (Duration: 17.6ms | Allocations: 3901)Completed 200 OK in 86ms (Views: 83.6ms | ActiveRecord: 1.3ms | Allocations: 29347)</code></pre><p>Notice the <code>Allocations:</code> information in the above logs.</p><p>We can see that</p><ul><li>6 objects were allocated while rendering <code>shared/_ad_banner.html.erb</code> viewpartial,</li><li>805 objects were allocated while rendering a collection of 100<code>articles/_article.html.erb</code> view partials,</li><li>and 3901 objects were allocated while rendering <code>articles/index.html.erb</code> viewtemplate.</li></ul><p>We can use this information to understand how much time was spent whilerendering a view template and how many objects were allocated in the process'memory between the time when that view template had started rendering and thetime when that view template had finished rendering.</p><p>To learn more about this feature, please check<a href="https://github.com/rails/rails/pull/34136">rails/rails#34136</a>.</p><p>Note that we can also collect this information by subscribing to<a href="https://edgeguides.rubyonrails.org/active_support_instrumentation.html#action-view">Action View hooks</a>.</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe /^render\_.+.action_view\$/ do |event|views_path = Rails.root.join(&quot;app/views/&quot;).to_stemplate_identifier = event.payload[:identifier]template_name = template_identifier.sub(views_path, &quot;&quot;)message = &quot;[#{event.name}] #{template_name} (Allocations: #{event.allocations})&quot;ViewAllocationsLogger.log(message)end</code></pre><p>This should log something like this.</p><pre><code class="language-plaintext">[render_partial.action_view] shared/\_ad_banner.html.erb (Allocations: 43)[render_collection.action_view] articles/\_article.html.erb (Allocations: 842)[render_template.action_view] articles/index.html.erb (Allocations: 4108)</code></pre>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveRecord::Relation#annotate]]></title>
       <author><name>Abhay Nikam</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-annotate-to-activerecord-relation-queries"/>
      <updated>2019-07-15T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-annotate-to-activerecord-relation-queries</id>
      <content type="html"><![CDATA[<p>Rails 6 has added <code>ActiveRecord::Relation#annotate</code> to allow adding comments tothe SQL queries generated by the <code>ActiveRecord::Relation</code> instance.</p><p>Here is how it can be used.</p><pre><code class="language-ruby">&gt; &gt; User.annotate(&quot;User whose name starts with 'A'&quot;).where(&quot;name LIKE ?&quot;, &quot;A%&quot;)SELECT &quot;users&quot;._ FROM &quot;users&quot;WHERE (name LIKE 'A%')/_ User whose name starts with 'A' \*/LIMIT ? [[&quot;LIMIT&quot;, 11]]</code></pre><p><code>ActiveRecord::Relation#annotate</code> allows to add multiple annotations on a query</p><pre><code class="language-ruby">&gt; &gt; bigbinary = Organization.find_by!(name: &quot;BigBinary&quot;)&gt; &gt; User.annotate(&quot;User whose name starts with 'A'&quot;)       .annotate(&quot;AND belongs to BigBinary organization&quot;)       .where(&quot;name LIKE ?&quot;, &quot;A%&quot;)       .where(organization: bigbinary)SELECT &quot;users&quot;._ FROM &quot;users&quot;WHERE (name LIKE 'A%') AND &quot;users&quot;.&quot;organization_id&quot; = ?/_ Users whose name starts with 'A' _//_ AND belongs to BigBinary organization \*/LIMIT ? [[&quot;organization_id&quot;, 1], [&quot;LIMIT&quot;, 11]]</code></pre><p>Also, <code>ActiveRecord::Relation#annotate</code> allows annotating scopes and modelassociations.</p><pre><code class="language-ruby">class User &lt; ActiveRecord::Basescope :active, -&gt; { where(status: 'active').annotate(&quot;Active users&quot;) }end&gt; &gt; User.active&gt; &gt; SELECT &quot;users&quot;._ FROM &quot;users&quot;&gt; &gt; /_ Active users \*/&gt; &gt; LIMIT ? [[&quot;LIMIT&quot;, 11]]&gt; &gt; ~~~Check out the[pull request](https://github.com/rails/rails/pull/35617)for more details on this.</code></pre>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds hook to Active Job for retry & discard]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-hooks-to-activejob-around-retries-and-discards"/>
      <updated>2019-07-09T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-hooks-to-activejob-around-retries-and-discards</id>
      <content type="html"><![CDATA[<h2>Before Rails 6</h2><p>Before Rails 6, we have to provide a custom block to perform custom logging andmonitoring around retries and discards of the jobs defined using Active Jobframework.</p><pre><code class="language-ruby">class Container::DeleteJob &lt; ActiveJob::Baseretry_on Timeout::Error, wait: 2.seconds, attempts: 3 do |job, error|message = &quot;Stopped retrying #{job.class} (JID #{job.job_id})with #{job.arguments.join(', ')} due to'#{error.class} - #{error.message}'.This job was retried for #{job.executions} times.&quot;.squish    BackgroundJob::ErrorLogger.log(message)enddiscard_on Container::NotFoundError do |job, error|message = &quot;Discarded #{job.class} (JID #{job.job_id})with #{job.arguments.join(', ')} due to'#{error.class} - #{error.message}' error.&quot;.squish    BackgroundJob::ErrorLogger.log(message)enddef perform(container_id)Container::DeleteService(container_id).process    # Will raise Container::NotFoundError    # if no container is found with 'container_id'.    # Might raise Timeout::Error when the remote API is not responding.endend</code></pre><p>Notice the custom blocks provided to <code>retry_on</code> and <code>discard_on</code> methods to anindividual job in the above example.</p><p>Extracting such custom logic to a base class or to a 3rd-party gem is possiblebut it will be non-standard and will be a bit difficult task.</p><p>An alternative approach is to<a href="https://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event">subscribe</a>to the hooks instrumented using Active Support Instrumentation API which is astandard and recommended way. Prior versions of Rails 6 already instruments<a href="https://guides.rubyonrails.org/v5.2/active_support_instrumentation.html#active-job">some hooks</a>such as <code>enqueue_at.active_job</code>, <code>enqueue.active_job</code>,<code>perform_start.active_job</code>, and <code>perform.active_job</code>. Unfortunately no hook isinstrumented around retries and discards of an Active Job prior to Rails 6.</p><h2>Rails 6</h2><p>Rails 6 has introduced hooks to Active Job<a href="https://github.com/rails/rails/pull/33751">around retries and discards</a> towhich one can easily subscribe using Active Support Instrumentation API toperform custom logging and monitoring or to collect any custom information.</p><p>The newly introduced hooks are <code>enqueue_retry.active_job</code>,<code>retry_stopped.active_job</code> and <code>discard.active_job</code>.</p><p>Let's discuss each of these hooks in detail.</p><p>Note that whenever we say <code>a job</code>, it means a job of type <code>ActiveJob</code>.</p><h5><code>enqueue_retry.active_job</code></h5><p>The <code>enqueue_retry.active_job</code> hook is instrumented when a job is<a href="https://github.com/rails/rails/blob/2ab3751781e34ca4a8d477ba53ff307ae9884b0d/activejob/lib/active_job/exceptions.rb#L119-L123">enqueued to retry again</a>due to occurrence of an exception which is configured using the <code>retry_on</code>method in the job's definition. This hook is triggered only when above conditionis satisfied and the number of executions of the job is less than the number of<code>attempts</code> defined using the <code>retry_on</code> method. The number of <code>attempts</code> is bydefault set to 5 if not defined explicitly.</p><p>This is how we would subscribe to this hook and perform custom logging in ourRails application.</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe &quot;enqueue_retry.active_job&quot; do |*args|event = ActiveSupport::Notifications::Event.new *argspayload = event.payloadjob = payload[:job]error = payload[:error]message = &quot;#{job.class} (JID #{job.job_id})with arguments #{job.arguments.join(', ')}will be retried again in #{payload[:wait]}sdue to error '#{error.class} - #{error.message}'.It is executed #{job.executions} times so far.&quot;.squishBackgroundJob::Logger.log(message)end</code></pre><p>Note that the <code>BackgroundJob::Logger</code> above is our custom logger. If we want, wecan add any other logic instead.</p><p>We will change the definition of <code>Container::DeleteJob</code> job as below.</p><pre><code class="language-ruby">class Container::DeleteJob &lt; ActiveJob::Baseretry_on Timeout::Error, wait: 2.seconds, attempts: 3def perform(container_id)Container::DeleteService(container_id).process    # Will raise Timeout::Error when the remote API is not responding.endend</code></pre><p>Let's enqueue this job.</p><pre><code class="language-ruby">Container::DeleteJob.perform_now(&quot;container-1234&quot;)</code></pre><p>Assume that this job keeps throwing <code>Timeout::Error</code> exception due to a networkissue. The job will be retried twice since it is configured to retry when a<code>Timeout::Error</code> exception occurs up to maximum 3 attempts. While retrying thisjob, Active Job will instrument <code>enqueue_retry.active_job</code> hook along with thenecessary job payload.</p><p>Since we have already subscribed to this hook, our subscriber would logsomething like this with the help of <code>BackgroundJob::Logger.log</code>.</p><pre><code class="language-plaintext">Container::DeleteJob (JID 770f4f2a-daa7-4c1e-be51-24adc04b4cb2) with arguments container-1234 will be retried again in 2s due to error 'Timeout::Error - Couldn't establish connection to cluster within 10s'. It is executed 1 times so far.Container::DeleteJob (JID 770f4f2a-daa7-4c1e-be51-24adc04b4cb2) with arguments container-1234 will be retried again in 2s due to error 'Timeout::Error - Couldn't establish connection to cluster within 10s'. It is executed 2 times so far.</code></pre><h5><code>retry_stopped.active_job</code></h5><p>The <code>retry_stopped.active_job</code> hook is triggered when a job is retried<a href="https://github.com/rails/rails/blob/2ab3751781e34ca4a8d477ba53ff307ae9884b0d/activejob/lib/active_job/exceptions.rb#L59-L66">up to the available number of <code>attempts</code></a>.</p><p>Let's see how this hook is triggered.</p><p>Along with the subscription for the <code>enqueue_retry.active_job</code> hook, let'ssubscribe to the <code>retry_stopped.active_job</code> hook, too.</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe &quot;retry_stopped.active_job&quot; do |*args|event = ActiveSupport::Notifications::Event.new *argspayload = event.payloadjob = payload[:job]error = payload[:error]message = &quot;Stopped processing #{job.class} (JID #{job.job_id})further with arguments #{job.arguments.join(', ')}since it failed due to '#{error.class} - #{error.message}' errorwhich reoccurred #{job.executions} times.&quot;.squishBackgroundJob::Logger.log(message)end</code></pre><p>Let's keep the <code>Container::DeleteJob</code> job's definition unchanged and enqueue itagain.</p><pre><code class="language-ruby">Container::DeleteJob.perform_now(&quot;container-1234&quot;)</code></pre><p>We will assume that the job will keep throwing <code>Timeout::Error</code> exception due toa network issue.</p><p>In the logs recorded using <code>BackgroundJob::Logger.log</code>, we should see somethinglike this.</p><pre><code class="language-plaintext">Container::DeleteJob (JID 770f4f2a-daa7-4c1e-be51-24adc04b4cb2) with arguments container-1234 will be retried again in 2s due to error 'Timeout::Error - Couldn't establish connection to cluster within 10s'. It is executed 1 times so far.Container::DeleteJob (JID 770f4f2a-daa7-4c1e-be51-24adc04b4cb2) with arguments container-1234 will be retried again in 2s due to error 'Timeout::Error - Couldn't establish connection to cluster within 10s'. It is executed 2 times so far.Stopped processing Container::DeleteJob (JID 770f4f2a-daa7-4c1e-be51-24adc04b4cb2) further with arguments container-1234 since it failed due to 'Timeout::Error - Couldn't establish connection to cluster within 10s' error which reoccurred 3 times.</code></pre><p>Notice the last entry in the logs above and its order.</p><h5><code>discard.active_job</code></h5><p>The <code>discard.active_job</code> hook is triggered when a job's further execution isdiscarded due to occurrence of an exception which is configured using<code>discard_on</code> method.</p><p>To see how this hook is triggered, we will subscribe to the <code>discard.active_job</code>hook.</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe &quot;discard.active_job&quot; do |*args|event = ActiveSupport::Notifications::Event.new *argspayload = event.payloadjob = payload[:job]error = payload[:error]message = &quot;Discarded #{job.class} (JID #{job.job_id})with arguments #{job.arguments.join(', ')}due to '#{error.class} - #{error.message}' error.&quot;.squishBackgroundJob::Logger.log(message)end</code></pre><p>We will configure our <code>Container::DeleteJob</code> job to discard when<code>Container::NotFoundError</code> exception occurs while executing the job.</p><pre><code class="language-ruby">class Container::DeleteJob &lt; ActiveJob::Basediscard_on Container::NotFoundErrorretry_on Timeout::Error, wait: 2.seconds, attempts: 3def perform(container_id)Container::DeleteService(container_id).process    # Will raise Container::NotFoundError    # if no container is found with 'container_id'.    # Will raise Timeout::Error when the remote API is not responding.endend</code></pre><p>Let's enqueue this job and assume that it would throw <code>Container::NotFoundError</code>exception.</p><pre><code class="language-ruby">Container::DeleteJob.perform_now(&quot;unknown-container-9876&quot;)</code></pre><p>We should see following in the logs recorded by <code>BackgroundJob::Logger.log</code>.</p><pre><code class="language-plaintext">Discarded Container::DeleteJob (JID e9b1cb5c-6d2d-49ae-b1d7-fef44f09ab8d) with arguments unknown-container-9876 due to 'Container::NotFoundError - Container 'unknown-container-9876' was not found' error.</code></pre><h2>Notes</h2><ol><li><p>These new hooks are also instrumented for the jobs which are enqueued using<code>perform_later</code> method since both <code>perform_now</code> and <code>perform_later</code> calls<code>perform</code> method under the hood.</p></li><li><p>Active Job already subscribes to the these hooks and writes them using the<a href="https://github.com/rails/rails/blob/2ab3751781e34ca4a8d477ba53ff307ae9884b0d/activejob/lib/active_job/logging.rb#L91-L121">Rails' default logger</a>.</p></li><li><p>If a block is provided to <code>retry_on</code> or <code>discard_on</code> methods then anapplicable hook is instrumented first and then the given block is yielded.</p></li></ol>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds support for Multi Environment credentials]]></title>
       <author><name>Berin Larson</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-support-for-multi-environment-credentials"/>
      <updated>2019-07-03T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-support-for-multi-environment-credentials</id>
      <content type="html"><![CDATA[<p>In Rails 5.2, encrypted credentials are stored in the file<code>config/credentials.yml.enc</code>. This is a single flat file which is encrypted bythe key located in <code>config/master.key</code>.</p><p>Rails 5.2 does not support storing credentials of different environments withdifferent encryption keys. If we want environment specific encryptedcredentials, we'll have to follow<a href="https://github.com/rails/rails/pull/33521#issuecomment-449403068">this workaround</a>.</p><p>Rails 6 has added support for Multi Environment credentials. With this change,credentials that belong to different environments can be stored in separatefiles with their own encryption key.</p><p>Let's see how this works in Rails 6.0.0.beta3</p><h2>Rails 6.0.0.beta3</h2><p>If we want to add credentials to be used in staging environment, we can run</p><pre><code class="language-bash">rails credentials:edit --environment staging</code></pre><p>This will create the credentials file <code>config/credentials/staging.yml.enc</code> and astaging specific encryption key <code>config/credentials/staging.key</code> and open thecredentials file in your text editor.</p><p>Let's add our AWS access key id here.</p><pre><code class="language-yaml">aws:  access_key_id: &quot;STAGING_KEY&quot;</code></pre><p>We can then access the access_key_id in staging environment.</p><pre><code class="language-ruby">&gt;&gt; RAILS_ENV=staging rails cpry(main)&gt; Rails.application.credentials.aws[:access_key_id]=&gt; &quot;STAGING_KEY&quot;</code></pre><h2>Which takes precedence: Global or Environment Specific credentials?</h2><p>Credentials added to global file <code>config/credentials.yml.enc</code><a href="https://github.com/rails/rails/pull/33521#issuecomment-412382031">will not be loaded</a>in environments which have their own environment specific credentials file(<code>config/credentials/$environment.yml.enc</code>).</p><p>So if we decide to add the following to the global credentials file, thesecredentials will not be available in staging. Since we already have aenvironment specific credentials file for staging.</p><pre><code class="language-yaml">aws:  access_key_id: &quot;DEFAULT_KEY&quot;stripe:  secret_key: &quot;DEFAULT_SECRET_KEY&quot;</code></pre><pre><code class="language-ruby">&gt;&gt; RAILS_ENV=staging rails cpry(main)&gt; Rails.application.credentials.aws[:access_key_id]=&gt; &quot;STAGING_KEY&quot;pry(main)&gt; Rails.application.credentials.stripe[:secret_key]Traceback (most recent call last):        1: from (irb):6NoMethodError (undefined method `[]' for nil:NilClass)</code></pre><p>Here is the <a href="https://github.com/rails/rails/pull/33521">relevant pull request</a></p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds before? and after? to Date and Time]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-before-and-after-to-date-and-time"/>
      <updated>2019-06-26T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-before-and-after-to-date-and-time</id>
      <content type="html"><![CDATA[<p>Rails 6 adds <a href="https://github.com/rails/rails/pull/32185">before?</a> and<a href="https://github.com/rails/rails/pull/32185">after?</a> to<a href="https://api.rubyonrails.org/v5.2/classes/Date.html">Date</a> ,<a href="https://api.rubyonrails.org/v5.2/classes/DateTime.html">DateTime</a> ,<a href="https://api.rubyonrails.org/v5.2/classes/Time.html">Time</a> and<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/TimeWithZone.html">ActiveSupport::TimeWithZone</a>classes.</p><p><a href="https://github.com/rails/rails/pull/32185">before?</a> and<a href="https://github.com/rails/rails/pull/32185">after?</a> are aliases to<a href="https://api.rubyonrails.org/v5.2/classes/Date.html#method-i-3C-3D-3E">&lt; (less than)</a>and<a href="https://api.rubyonrails.org/v5.2/classes/Date.html#method-i-3C-3D-3E">&gt; (greater than)</a>methods respectively.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><p>Let's try calling <code>before?</code> on a date object in Rails 5.2.</p><pre><code class="language-ruby">&gt; &gt; Date.new(2019, 3, 31).before?(Date.new(2019, 4, 1))=&gt; NoMethodError: undefined method 'before?' for Sun, 31 Mar 2019:Datefrom (irb):1&gt; &gt; Date.new(2019, 3, 31) &lt; Date.new(2019, 4, 1)=&gt; true</code></pre><h4>Rails 6.0.0.beta2</h4><p>Now, let's compare <a href="https://api.rubyonrails.org/v5.2/classes/Date.html">Date</a> ,<a href="https://api.rubyonrails.org/v5.2/classes/DateTime.html">DateTime</a> ,<a href="https://api.rubyonrails.org/v5.2/classes/Time.html">Time</a> and<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/TimeWithZone.html">ActiveSupport::TimeWithZone</a>objects using <a href="https://github.com/rails/rails/pull/32185">before?</a> and<a href="https://github.com/rails/rails/pull/32185">after?</a> in Rails 6.</p><pre><code class="language-ruby">&gt; &gt; Date.new(2019, 3, 31).before?(Date.new(2019, 4, 1))=&gt; true&gt; &gt; Date.new(2019, 3, 31).after?(Date.new(2019, 4, 1))=&gt; false&gt; &gt; DateTime.parse('2019-03-31').before?(DateTime.parse('2019-04-01'))=&gt; true&gt; &gt; DateTime.parse('2019-03-31').after?(DateTime.parse('2019-04-01'))=&gt; false&gt; &gt; Time.parse('2019-03-31').before?(Time.parse('2019-04-01'))=&gt; true&gt; &gt; Time.parse('2019-03-31').after?(Time.parse('2019-04-01'))=&gt; false&gt; &gt; ActiveSupport::TimeWithZone.new(Time.utc(2019, 3, 31, 12, 0, 0), ActiveSupport::TimeZone[&quot;Eastern Time (US &amp; Canada)&quot;]).before?(ActiveSupport::TimeWithZone.new(Time.utc(2019, 4, 1, 12, 0, 0), ActiveSupport::TimeZone[&quot;Eastern Time (US &amp; Canada)&quot;]))=&gt; true&gt; &gt; ActiveSupport::TimeWithZone.new(Time.utc(2019, 3, 31, 12, 0, 0), ActiveSupport::TimeZone[&quot;Eastern Time (US &amp; Canada)&quot;]).after?(ActiveSupport::TimeWithZone.new(Time.utc(2019, 4, 1, 12, 0, 0), ActiveSupport::TimeZone[&quot;Eastern Time (US &amp; Canada)&quot;]))=&gt; false</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/32185">pull request</a>for adding <code>before?</code> and <code>after?</code> methods and the<a href="https://github.com/rails/rails/pull/32398">pull request</a> for moving <code>before?</code>and <code>after?</code> to<a href="https://api.rubyonrails.org/v5.2/classes/DateAndTime/Calculations.html">DateAndTime::Calculations</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds Array#extract!]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-array-extract"/>
      <updated>2019-06-24T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-array-extract</id>
      <content type="html"><![CDATA[<p>Rails 6 added <a href="https://github.com/rails/rails/pull/33137">extract!</a> on<a href="https://api.rubyonrails.org/v5.2/classes/Array.html">Array</a> class.<a href="https://github.com/rails/rails/pull/33137">extract!</a> removes and returns theelements for which the given block returns true.</p><p><a href="https://github.com/rails/rails/pull/33137">extract!</a> is different from<a href="https://ruby-doc.org/core-2.5.1/Array.html#method-i-reject-21">reject!</a> in theway that<a href="https://ruby-doc.org/core-2.5.1/Array.html#method-i-reject-21">reject!</a> returnsthe array after removing the elements whereas<a href="https://github.com/rails/rails/pull/33137">extract!</a> returns removed elementsfrom the array.</p><p>Let's checkout how it works.</p><h4>Rails 6.0.0.beta2</h4><p>Let's pluck all the user emails and then extract emails which include<code>gmail.com</code>.</p><pre><code class="language-ruby">&gt; &gt; emails = User.pluck(:email)&gt; &gt; SELECT &quot;users&quot;.&quot;email&quot; FROM &quot;users&quot;=&gt; [&quot;amit.choudhary@bigbinary.com&quot;, &quot;amit@gmail.com&quot;, &quot;mark@gmail.com&quot;, &quot;sam@gmail.com&quot;]&gt; &gt; emails.extract! { |email| email.include?('gmail.com') }=&gt; [&quot;amit@gmail.com&quot;, &quot;mark@gmail.com&quot;, &quot;sam@gmail.com&quot;]&gt; &gt; emails=&gt; [&quot;amit.choudhary@bigbinary.com&quot;]&gt; &gt; emails = User.pluck(:email)&gt; &gt; SELECT &quot;users&quot;.&quot;email&quot; FROM &quot;users&quot;=&gt; [&quot;amit.choudhary@bigbinary.com&quot;, &quot;amit@gmail.com&quot;, &quot;mark@gmail.com&quot;, &quot;sam@gmail.com&quot;]&gt; &gt; emails.reject! { |email| email.include?('gmail.com') }=&gt; [&quot;amit.choudhary@bigbinary.com&quot;]&gt; &gt; emails=&gt; [&quot;amit.choudhary@bigbinary.com&quot;]</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/33137">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds Enumerable#index_with]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-enumerable-index_with"/>
      <updated>2019-06-17T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-enumerable-index_with</id>
      <content type="html"><![CDATA[<p>Rails 6 added <a href="https://github.com/rails/rails/pull/32523">index_with</a> on<a href="https://api.rubyonrails.org/v5.2/classes/Enumerable.html">Enumerable</a> module.This will help in creating a hash from an enumerator with default or fetchedvalues.</p><p>Before Rails 6, we can achieve this by calling<a href="https://ruby-doc.org/core-2.5.1/Array.html#method-i-map">map</a> along with<a href="https://ruby-doc.org/core-2.5.1/Array.html#method-i-to_h">to_h</a>.</p><p><a href="https://github.com/rails/rails/pull/32523">index_with</a> takes both value or ablock as a parameter.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><p>Let's create a hash from an array in Rails 5.2 using<a href="https://ruby-doc.org/core-2.5.1/Array.html#method-i-map">map</a> and<a href="https://ruby-doc.org/core-2.5.1/Array.html#method-i-to_h">to_h</a>.</p><pre><code class="language-ruby">&gt; &gt; address = Address.first&gt; &gt; SELECT &quot;addresses&quot;.\* FROM &quot;addresses&quot;&gt; &gt; ORDER BY &quot;addresses&quot;.&quot;id&quot; ASC LIMIT \$1 [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;Address id: 1, first_name: &quot;Amit&quot;, last_name: &quot;Choudhary&quot;, state: &quot;California&quot;, created_at: &quot;2019-03-21 10:03:57&quot;, updated_at: &quot;2019-03-21 10:03:57&quot;&gt;&gt; &gt; NAME_ATTRIBUTES = [:first_name, :last_name]=&gt; [:first_name, :last_name]&gt; &gt; NAME_ATTRIBUTES.map { |attr| [attr, address.public_send(attr)] }.to_h=&gt; {:first_name=&gt;&quot;Amit&quot;, :last_name=&gt;&quot;Choudhary&quot;}</code></pre><h4>Rails 6.0.0.beta2</h4><p>Now let's create the same hash from the array using<a href="https://github.com/rails/rails/pull/32523">index_with</a> in Rails 6.</p><pre><code class="language-ruby">&gt; &gt; address = Address.first&gt; &gt; SELECT &quot;addresses&quot;.\* FROM &quot;addresses&quot;&gt; &gt; ORDER BY &quot;addresses&quot;.&quot;id&quot; ASC LIMIT \$1 [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;Address id: 1, first_name: &quot;Amit&quot;, last_name: &quot;Choudhary&quot;, state: &quot;California&quot;, created_at: &quot;2019-03-21 10:02:47&quot;, updated_at: &quot;2019-03-21 10:02:47&quot;&gt;&gt; &gt; NAME_ATTRIBUTES = [:first_name, :last_name]=&gt; [:first_name, :last_name]&gt; &gt; NAME_ATTRIBUTES.index_with { |attr| address.public_send(attr) }=&gt; {:first_name=&gt;&quot;Amit&quot;, :last_name=&gt;&quot;Choudhary&quot;}&gt; &gt; NAME_ATTRIBUTES.index_with('Default')=&gt; {:first_name=&gt;&quot;Default&quot;, :last_name=&gt;&quot;Default&quot;}</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/32523">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds private option to delegate method]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-private-option-to-delegate-method"/>
      <updated>2019-06-10T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-private-option-to-delegate-method</id>
      <content type="html"><![CDATA[<p>Rails 6 adds <code>:private</code> option to<a href="https://api.rubyonrails.org/v5.2/classes/Module.html#method-i-delegate">delegate</a>method. After this addition, we can delegate methods in the private scope.</p><p>Let's checkout how it works.</p><h4>Rails 6.0.0.beta2</h4><p>Let's create two models named as <code>Address</code> and <code>Order</code>. Let's also delegate<code>validate_state</code> method in <code>Order</code> to <code>Address</code>.</p><pre><code class="language-ruby">class Address &lt; ApplicationRecord  validates :first_name, :last_name, :state, presence: true  DELIVERABLE_STATES = ['New York']  def validate_state    unless DELIVERABLE_STATES.include?(state)      errors.add(:state, :invalid)    end  endendclass Order &lt; ApplicationRecord  belongs_to :address  delegate :validate_state, to: :addressend&gt;&gt; Order.firstSELECT &quot;orders&quot;.* FROM &quot;orders&quot;ORDER BY &quot;orders&quot;.&quot;id&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;Order id: 1, amount: 0.1e2, address_id: 1, created_at: &quot;2019-03-21 10:02:58&quot;, updated_at: &quot;2019-03-21 10:17:44&quot;&gt;&gt;&gt; Address.firstSELECT &quot;addresses&quot;.* FROM &quot;addresses&quot;ORDER BY &quot;addresses&quot;.&quot;id&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;Address id: 1, first_name: &quot;Amit&quot;, last_name: &quot;Choudhary&quot;, state: &quot;California&quot;, created_at: &quot;2019-03-21 10:02:47&quot;, updated_at: &quot;2019-03-21 10:02:47&quot;&gt;&gt;&gt; Order.first.validate_stateSELECT &quot;orders&quot;.* FROM &quot;orders&quot;ORDER BY &quot;orders&quot;.&quot;id&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]SELECT &quot;addresses&quot;.* FROM &quot;addresses&quot;WHERE &quot;addresses&quot;.&quot;id&quot; = $1 LIMIT $2  [[&quot;id&quot;, 1], [&quot;LIMIT&quot;, 1]]=&gt; [&quot;is invalid&quot;]</code></pre><p>Now, let's add <code>private: true</code> to the delegation.</p><pre><code class="language-ruby">class Order &lt; ApplicationRecord  belongs_to :address  delegate :validate_state, to: :address, private: trueend&gt;&gt; Order.first.validate_stateSELECT &quot;orders&quot;.* FROM &quot;orders&quot;ORDER BY &quot;orders&quot;.&quot;id&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; Traceback (most recent call last):        1: from (irb):7NoMethodError (private method 'validate_state' called for #&lt;Order:0x00007fb9d72fc1f8&gt;Did you mean?  validate)</code></pre><p>As we can see, Rails now raises an exception of private method called if<code>private</code> option is set with <code>delegate</code> method.</p><p>Here is the relevant <a href="https://github.com/rails/rails/pull/31944">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 allows spaces in postgres table names]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-allows-spaces-in-postgres-table-names"/>
      <updated>2019-06-05T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-allows-spaces-in-postgres-table-names</id>
      <content type="html"><![CDATA[<p>Rails 6 allows spaces in tables names in PostgreSQL. Before Rails 6, if we tryto create a table named as <code>user reviews</code>, Rails tries to create a table namedas <code>reviews</code> in schema named as <code>user</code>.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><p>Let's create a table <code>user reviews</code> in Rails 5.2.</p><pre><code class="language-ruby">&gt;&gt; class CreateUserReviews &lt; ActiveRecord::Migration[5.2]&gt;&gt;   def change&gt;&gt;     create_table 'user reviews' do |t|&gt;&gt;       t.string :value&gt;&gt;&gt;&gt;       t.timestamps&gt;&gt;     end&gt;&gt;   end&gt;&gt; end=&gt; :change&gt;&gt; CreateUserReviews.new.change-- create_table(&quot;user reviews&quot;)CREATE TABLE &quot;user&quot;.&quot;reviews&quot; (&quot;id&quot; bigserial primary key, &quot;value&quot; character varying, &quot;created_at&quot; timestamp NOT NULL, &quot;updated_at&quot; timestamp NOT NULL)=&gt; Traceback (most recent call last):        2: from (irb):10        1: from (irb):3:in 'change'ActiveRecord::StatementInvalid (PG::InvalidSchemaName: ERROR:  schema &quot;user&quot; does not exist)LINE 1: CREATE TABLE &quot;user&quot;.&quot;reviews&quot; (&quot;id&quot; bigserial primary key, &quot;...                     ^: CREATE TABLE &quot;user&quot;.&quot;reviews&quot; (&quot;id&quot; bigserial primary key, &quot;value&quot; character varying, &quot;created_at&quot; timestamp NOT NULL, &quot;updated_at&quot; timestamp NOT NULL)</code></pre><p>We can see that Rails 5.2 raised an exception and tried to create table named as<code>reviews</code> in <code>user</code> schema.</p><h4>Rails 6.0.0.beta2</h4><p>Now, let's create a table <code>user reviews</code> in Rails 6.</p><pre><code class="language-ruby">&gt;&gt; class CreateUserReviews &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     create_table 'user reviews' do |t|&gt;&gt;       t.string :value&gt;&gt;&gt;&gt;       t.timestamps&gt;&gt;     end&gt;&gt;   end&gt;&gt; end=&gt; :change&gt;&gt; CreateUserReviews.new.change-- create_table(&quot;user reviews&quot;)CREATE TABLE &quot;user reviews&quot; (&quot;id&quot; bigserial primary key, &quot;value&quot; character varying, &quot;created_at&quot; timestamp(6) NOT NULL, &quot;updated_at&quot; timestamp(6) NOT NULL)=&gt; #&lt;PG::Result:0x00007f9d633c5458 status=PGRES_COMMAND_OK ntuples=0 nfields=0 cmd_tuples=0&gt;</code></pre><p>Now, we can see that the SQL generated is correct and Rails successfully createda table named as <code>user reviews</code>.</p><p>Here is the relevant <a href="https://github.com/rails/rails/pull/34561">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds if_not_exists option to create_table]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-if_not_exists-option-to-create_table"/>
      <updated>2019-05-22T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-if_not_exists-option-to-create_table</id>
      <content type="html"><![CDATA[<p>Rails 6 added <a href="https://github.com/rails/rails/pull/31382">if_not_exists</a> to<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-create_table">create_table</a>option to create a table if it doesn't exist.</p><p>Before Rails 6, we could use<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-table_exists-3F">ActiveRecord::Base.connection.table_exists?</a>.</p><p>Default value of <a href="https://github.com/rails/rails/pull/31382">if_not_exists</a>option is <code>false</code>.</p><h4>Rails 5.2</h4><p>Let's create <code>users</code> table in Rails 5.2.</p><pre><code class="language-ruby">&gt;&gt; class CreateUsers &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     create_table :users do |t|&gt;&gt;       t.string :name, index: { unique: true }&gt;&gt;&gt;&gt;       t.timestamps&gt;&gt;     end&gt;&gt;   end&gt;&gt; end&gt;&gt; CreateUsers.new.change-- create_table(:users)CREATE TABLE &quot;users&quot; (&quot;id&quot; bigserial primary key, &quot;name&quot; character varying, &quot;created_at&quot; timestamp NOT NULL, &quot;updated_at&quot; timestamp NOT NULL)=&gt; #&lt;PG::Result:0x00007fd73e711cf0 status=PGRES_COMMAND_OK ntuples=0 nfields=0 cmd_tuples=0&gt;</code></pre><p>Now let's try creating <code>users</code> table again with<a href="https://github.com/rails/rails/pull/31382">if_not_exists</a> option.</p><pre><code class="language-ruby">&gt;&gt; class CreateUsers &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     create_table :users, if_not_exists: true do |t|&gt;&gt;       t.string :name, index: { unique: true }&gt;&gt;&gt;&gt;       t.timestamps&gt;&gt;     end&gt;&gt;   end&gt;&gt; end&gt;&gt; CreateUsers.new.change-- create_table(:users, {:if_not_exists=&gt;true})CREATE TABLE &quot;users&quot; (&quot;id&quot; bigserial primary key, &quot;name&quot; character varying, &quot;created_at&quot; timestamp NOT NULL, &quot;updated_at&quot; timestamp NOT NULL)=&gt; Traceback (most recent call last):        2: from (irb):121        1: from (irb):114:in 'change'ActiveRecord::StatementInvalid (PG::DuplicateTable: ERROR:  relation &quot;users&quot; already exists): CREATE TABLE &quot;users&quot; (&quot;id&quot; bigserial primary key, &quot;name&quot; character varying, &quot;created_at&quot; timestamp NOT NULL, &quot;updated_at&quot; timestamp NOT NULL)</code></pre><p>We can see that Rails 5.2 ignored<a href="https://github.com/rails/rails/pull/31382">if_not_exists</a> option and triedcreating the table again.</p><p>Now let's try<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-table_exists-3F">ActiveRecord::Base.connection.table_exists?</a>with Rails 5.2.</p><pre><code class="language-ruby">&gt;&gt; class CreateUsers &lt; ActiveRecord::Migration[5.2]&gt;&gt;   def change&gt;&gt;     unless ActiveRecord::Base.connection.table_exists?('users')&gt;&gt;       create_table :users do |t|&gt;&gt;         t.string :name&gt;&gt;&gt;&gt;         t.timestamps&gt;&gt;       end&gt;&gt;     end&gt;&gt;   end&gt;&gt; end&gt;&gt; CreateUsers.new.change=&gt; nil</code></pre><p>We can see that <code>create_table :users</code> never executed because<code>ActiveRecord::Base.connection.table_exists?('users')</code> returned true.</p><h4>Rails 6.0.0.beta2</h4><p>Let's create <code>users</code> table in Rails 6 with<a href="https://github.com/rails/rails/pull/31382">if_not_exists</a> option set as true.</p><pre><code class="language-ruby">&gt;&gt; class CreateUsers &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     create_table :users, if_not_exists: true do |t|&gt;&gt;       t.string :name, index: { unique: true }&gt;&gt;&gt;&gt;       t.timestamps&gt;&gt;     end&gt;&gt;   end&gt;&gt; end&gt;&gt; CreateUsers.new.change-- create_table(:users, {:if_not_exists=&gt;true})CREATE TABLE IF NOT EXISTS &quot;users&quot; (&quot;id&quot; bigserial primary key, &quot;name&quot; character varying, &quot;created_at&quot; timestamp(6) NOT NULL, &quot;updated_at&quot; timestamp(6) NOT NULL)=&gt; #&lt;PG::Result:0x00007fc4614fef48 status=PGRES_COMMAND_OK ntuples=0 nfields=0 cmd_tuples=0&gt;&gt;&gt; CreateUsers.new.change-- create_table(:users, {:if_not_exists=&gt;true})CREATE TABLE IF NOT EXISTS &quot;users&quot; (&quot;id&quot; bigserial primary key, &quot;name&quot; character varying, &quot;created_at&quot; timestamp(6) NOT NULL, &quot;updated_at&quot; timestamp(6) NOT NULL)=&gt; #&lt;PG::Result:0x00007fc46513fde0 status=PGRES_COMMAND_OK ntuples=0 nfields=0 cmd_tuples=0&gt;</code></pre><p>We can see that no exception was raised when we tried creating <code>users</code> table thesecond time.</p><p>Now let's see what happens if we set<a href="https://github.com/rails/rails/pull/31382">if_not_exists</a> to false.</p><pre><code class="language-ruby">&gt;&gt; class CreateUsers &lt; ActiveRecord::Migration[6.0]&gt;&gt;   def change&gt;&gt;     create_table :users, if_not_exists: false do |t|&gt;&gt;       t.string :name, index: { unique: true }&gt;&gt;&gt;&gt;       t.timestamps&gt;&gt;     end&gt;&gt;   end&gt;&gt; end&gt;&gt; CreateUsers.new.change-- create_table(:users, {:if_not_exists=&gt;false})CREATE TABLE &quot;users&quot; (&quot;id&quot; bigserial primary key, &quot;name&quot; character varying, &quot;created_at&quot; timestamp(6) NOT NULL, &quot;updated_at&quot; timestamp(6) NOT NULL)=&gt; Traceback (most recent call last):        2: from (irb):23        1: from (irb):15:in `change'ActiveRecord::StatementInvalid (PG::DuplicateTable: ERROR:  relation &quot;users&quot; already exists)</code></pre><p>As we can see, Rails raised an exception here because<a href="https://github.com/rails/rails/pull/31382">if_not_exists</a> was set to false.</p><p>Here is the relevant <a href="https://github.com/rails/rails/pull/31382">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 has added a way to change the database of the app]]></title>
       <author><name>Prathamesh Sonpatki</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-has-added-a-way-to-change-the-database-of-the-app"/>
      <updated>2019-04-30T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-has-added-a-way-to-change-the-database-of-the-app</id>
      <content type="html"><![CDATA[<p>Rails allows us to use different databases using the <code>database.yml</code> config file.It uses sqlite3 as the default database when a new Rails app is created. But itis also possible to use different databases such as MySQL or PostgreSQL. Thecontents of <code>database.yml</code> change as per the database. Also each database has adifferent adapter. We need to include the gems <code>pg</code> or <code>mysql2</code> accordingly.</p><p>Before Rails 6, it was not possible to change the contents of <code>database.yml</code>automatically. But now a<a href="https://github.com/rails/rails/pull/34832">command has been added</a> to do thisautomatically.</p><p>Let's say our app has started with sqlite and now we have to switch to MySQL.</p><pre><code class="language-bash">$ rails db:system:change --to=mysql    conflict  config/database.ymlOverwrite /Users/prathamesh/Projects/reproductions/squish_app/config/database.yml? (enter &quot;h&quot; for help) [Ynaqdhm] Y       force  config/database.yml        gsub  Gemfile        gsub  Gemfile</code></pre><p>Our <code>database.yml</code> is now changed to contain the configuration for MySQLdatabase and the <code>Gemfile</code> also gets updated automatically with addition of<code>mysql2</code> gem in place of <code>sqlite3</code>.</p><p>This command also takes care of using<a href="https://github.com/rails/rails/pull/35522">proper gem versions</a> in the Gemfilewhen the database backend is changed.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds parallel testing]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-parallel-testing"/>
      <updated>2019-04-29T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-parallel-testing</id>
      <content type="html"><![CDATA[<p>We frequently think about how good it would be if we could run tests in parallelon local so there would be less wait time for tests to be completed. Wait timesincrease considerably when the count of tests are on the higher side, which is acommon case for a lot of applications.</p><p>Though CI tools like <a href="https://circleci.com/">CircleCi</a> and<a href="https://travis-ci.org/">Travis CI</a> provide a feature to run tests in parallel,there still wasn't a straightforward way to parallelize tests on local beforeRails 6.</p><p>Before Rails 6, if we wanted to parallelize tests, we would use<a href="https://github.com/grosser/parallel_tests">Parallel Tests</a>.</p><p>Rails 6 adds the parallelization of tests by default. Rails 6 added<a href="https://github.com/rails/rails/pull/31900">parallelize</a> as a class method on<a href="https://api.rubyonrails.org/v5.2/classes/ActiveSupport/TestCase.html">ActiveSupport::TestCase</a>which takes a hash as a parameter with the keys <code>workers</code> and <code>with</code>. The<code>worker</code> key is responsible for setting the number of parallel workers. Thedefault value of the <code>worker</code> key is <code>:number_of_processors</code>, which finds thenumber of processors on the machine and sets it as the number of parallelworkers. <code>with</code> takes two values - <code>:processes</code>, which is the default one, and<code>:threads</code> as a value.</p><p>Rails 6 also added two hooks - <code>parallelize_setup</code>, which is called before theprocesses are forked, and <code>parallelize_teardown</code>, which is called after theprocesses are killed. Rails 6 also handles creation of multiple databases andnamespacing of those databases for parallel tests out of the box.</p><p>If we want to disable parallel testing, we can set the value of <code>workers</code> as 1or less.</p><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">class ActiveSupport::TestCase  parallelize_setup do |worker|    # setup databases  end   parallelize_teardown do |worker|    # cleanup database  end  # Run tests in parallel with specified workers  parallelize(workers: :number_of_processors)  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.  fixtures :all  # Add more helper methods to be used by all tests here...end</code></pre><p>Rails 6 also provides an environment variable <code>PARALLEL_WORKERS</code> to set thenumber of parallel workers on runtime.</p><pre><code class="language-bash">$ PARALLEL_WORKERS=10 bin/rails test</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/31900">pull request</a>for adding <code>parallelize</code> and<a href="https://github.com/rails/rails/pull/34735">pull request</a> for setting number ofprocessors as default workers count.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 improves ActiveSupport::Notifications::Event]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-cpu-time-idle-time-and-allocations-to-activesupport-notifications-event"/>
      <updated>2019-04-24T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-cpu-time-idle-time-and-allocations-to-activesupport-notifications-event</id>
      <content type="html"><![CDATA[<p>Rails provides an easy way to instrument events and ability to subscribe tothose events using<a href="https://guides.rubyonrails.org/active_support_instrumentation.html">Active Support Instrumentation</a>API.</p><h2>Before Rails 6</h2><p>Before Rails 6, the subscriber of an event can access the event's start time,end time and the duration along with the other event information.</p><p>To demonstrate how to access this information from an event, we will instrumenta custom event <code>custom_sleep_event</code> and attach a subscriber to that event.</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe('custom_sleep_event') do |*args|  event = ActiveSupport::Notifications::Event.new(*args)  puts &quot;Event: #{event.inspect}&quot;  puts &quot;Started: #{event.time}&quot;  puts &quot;Finished: #{event.end}&quot;  puts &quot;Duration (ms): #{event.duration}&quot;endActiveSupport::Notifications.instrument('custom_sleep_event') do  sleep 2end</code></pre><p>The event subscriber should print something similar.</p><pre><code class="language-plaintext">Event: #&lt;ActiveSupport::Notifications::Event:0x00007f952fc6a0b8 @name=&quot;custom_sleep_event&quot;, @payload={}, @time=2019-04-11 16:58:52 +0530, @transaction_id=&quot;e82231ab65b7af3c85ec&quot;, @end=2019-04-11 16:58:54 +0530, @children=[], @duration=nil&gt;Started: 2019-04-11 16:58:52 +0530Finished: 2019-04-11 16:58:54 +0530Duration (ms): 2001.287</code></pre><h2>Improvements and additions made to ActiveSupport::Notifications::Event in Rails 6</h2><p>Rails 6 has improved the way an event's duration is computed and also addeduseful information accessible on an event object such as CPU time, idle time andallocations.</p><p>Let's discuss it in more detail.</p><h5>1. <code>CLOCK_MONOTONIC</code> instead of <code>CLOCK_REALTIME</code></h5><p>Before Rails 6, <code>Time.now</code> is used for recording the event's start time and endtime. To avoid<a href="https://github.com/rails/rails/issues/34271">issues with the machine changing time</a>,Rails 6 now uses<a href="https://www.rubydoc.info/gems/concurrent-ruby/Concurrent.monotonic_time"><code>Concurrent.monotonic_time</code></a>instead of <code>Time.now</code> to record the event's both start time and end timeaccurately.</p><p>Initially<a href="https://ruby-doc.org/core-2.5.3/Process.html#method-c-clock_gettime"><code>Process.clock_gettime(Process::CLOCK_MONOTONIC)</code></a>was used which<a href="https://github.com/rails/rails/commit/dda9452314bb904a3e2c850bd23f118eb80e3356#diff-b82336d1f77b84d28210f9b46fcff97d">later modified</a>to use <code>Concurrent.monotonic_time</code>. Note that <code>Concurrent.monotonic_time</code> issame but returns more precise time than<code>Process.clock_gettime(Process::CLOCK_MONOTONIC)</code>.</p><blockquote><p><code>Time.now</code> or <code>Process.clock_gettime(Process::CLOCK_REALTIME)</code> can jumpforwards and backwards as the system time-of-day clock is changed. Whereas,clock time using <code>CLOCK_MONOTONIC</code> returns the absolute wall-clock time sincean unspecified time in the past (for example, system start-up time, or theEpoch). The <code>CLOCK_MONOTONIC</code> does not change with the system time-of-dayclock, it just keeps advances forwards at one tick per tick and resets if thesystem is rebooted. In general, <code>CLOCK_MONOTONIC</code> is recommended to computethe elapsed time between two events. To read more about the differencesbetween <code>CLOCK_REALTIME</code> and <code>CLOCK_MONOTONIC</code>, please check the discussion on[this Stackoverflow thread](https://stackoverflow.com/questions/3523442/&gt;difference-between-clock-realtime-and-clock-monotonic). Another<a href="https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/">article</a>written by Luca Guidi on the same topic is a recommended read.</p></blockquote><h5>2. No need to create hand made event objects on our own</h5><p>Since it is a common practice to initialize an event using<code>ActiveSupport::Notifications::Event.new(*args)</code> in the event subscriber block,<a href="https://github.com/rails/rails/pull/33451">Rails 6 now makes this a bit easy</a>.If the block passed to the subscriber only takes one argument then the ActiveSupport Notification framework now yields an event object to the block.</p><p>Therefore, the subscriber definition below</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe('an_event') do |*args|  event = ActiveSupport::Notifications::Event.new(*args)  puts &quot;Event #{event.name} received.&quot;end</code></pre><p>now can be simplified in Rails 6 as follows.</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe('an_event') do |event|  puts &quot;Event #{event.name} received.&quot;end</code></pre><h5>3. CPU time and idle time</h5><p>Rails 6 now computes elapsed CPU time of an event with the help of<a href="https://ruby-doc.org/core-2.5.3/Process.html#method-c-clock_gettime"><code>Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)</code></a>.</p><p>System (kernel) keeps track of CPU time per process. The clock time returnedusing <code>CLOCK_PROCESS_CPUTIME_ID</code> represents the CPU time that has passed sincethe process started. Since a process may not always get all CPU cycles betweenstart and finish of the process, the process often has to (sleep and) share CPUtime among other processes. If the system puts a process to sleep, then the timespend waiting is not counted in the process' CPU time.</p><p>The CPU time of an event can be fetched using the <code>#cpu_time</code> method.</p><p>Also, Rails 6 now computes the idle time of an event, too. The idle time of anevent represents the difference between the event's <code>#duration</code> and <code>#cpu_time</code>.Note that the <code>#duration</code> is computed using the difference between the event'smonotonic time at the start (<code>#time</code>) and the monotonic time at the end(<code>#end</code>).</p><p>Let's see how to get these time values.</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe('custom_sleep_event') do |event|  puts &quot;Event: #{event.inspect}&quot;  puts &quot;Started: #{event.time}&quot;  puts &quot;Finished: #{event.end}&quot;  puts &quot;Duration (ms): #{event.duration}&quot;  puts &quot;CPU time (ms): #{event.cpu_time}&quot;  puts &quot;Idle time (ms): #{event.idle_time}&quot;endActiveSupport::Notifications.instrument('custom_sleep_event') do  sleep 2end</code></pre><p>It prints this.</p><pre><code class="language-plaintext">Event: #&lt;ActiveSupport::Notifications::Event:0x00007fb02ac72400 @name=&quot;custom_sleep_event&quot;, @payload={}, @time=29514.525707, @transaction_id=&quot;43ca8e1c378b6b00d861&quot;, @end=29516.528971, @children=[], @duration=nil, @cpu_time_start=2.238801, @cpu_time_finish=2.238874, @allocation_count_start=835821, @allocation_count_finish=835821&gt;Started: 29514.525707Finished: 29516.528971Duration (ms): 2003.2639999990351CPU time (ms): 0.07299999999998974Idle time (ms): 2003.190999999035</code></pre><p>Notice the <code>@cpu_time_start</code> and <code>@cpu_time_finish</code> counters in the inspectedevent object representation which are used to calculate the CPU time.</p><h5>4. Allocations</h5><p>We will now know how many objects were allocated between the start and end of anevent using event's <code>#allocations</code> method.</p><pre><code class="language-ruby">ActiveSupport::Notifications.subscribe('custom_sleep_event') do |event|  puts &quot;Event: #{event.inspect}&quot;  puts &quot;Started: #{event.time}&quot;  puts &quot;Finished: #{event.end}&quot;  puts &quot;Duration (ms): #{event.duration}&quot;  puts &quot;CPU time (ms): #{event.cpu_time}&quot;  puts &quot;Idle time (ms): #{event.idle_time}&quot;  puts &quot;# of objects allocated: #{event.allocations}&quot;endActiveSupport::Notifications.instrument('custom_sleep_event') do  sleep 2end</code></pre><p>The above example should print something like this.</p><pre><code class="language-plaintext">Event: #&lt;ActiveSupport::Notifications::Event:0x00007fed8c4e33c0 @name=&quot;custom_sleep_event&quot;, @payload={}, @time=30503.508897, @transaction_id=&quot;5330165dae2b49fbe143&quot;, @end=30505.513547, @children=[], @duration=nil, @cpu_time_start=2.813231, @cpu_time_finish=2.813404, @allocation_count_start=834227, @allocation_count_finish=834228&gt;Started: 30503.508897Finished: 30505.513547Duration (ms): 2004.6499999989464CPU time (ms): 0.17299999999975668Idle time (ms): 2004.4769999989467# of objects allocated: 1</code></pre><p>Notice the <code>@allocation_count_finish</code> and <code>@allocation_count_start</code> counters inthe inspected event object representation which are used to calculate the numberof objects allocated during an event whose difference is (834228 - 834227 = 1).</p><blockquote><p>In case of JRuby, the<a href="https://github.com/rails/rails/blob/3e628c48406440811f9201e2db7ba0df53bb38e1/activesupport/lib/active_support/notifications/instrumenter.rb#L155-L157">allocations would be zero</a>.</p></blockquote><h5>5. <code>#start!</code> and <code>#finish!</code></h5><p>Two public methods <code>#start!</code> and <code>#finish!</code> have been introduced to<code>ActiveSupport::Notifications::Event</code> which can be used to record moreinformation.</p><p>The <code>#start!</code> method<a href="https://github.com/rails/rails/blob/45511fa8b20c621e2cf193ddaeb7d6fbe8432fea/activesupport/lib/active_support/notifications/instrumenter.rb#L79-L83">resets</a>the <code>@time</code>, <code>@cpu_time_start</code> and <code>@allocation_count_start</code> counters.Similarly, the <code>#finish!</code> method also<a href="https://github.com/rails/rails/blob/45511fa8b20c621e2cf193ddaeb7d6fbe8432fea/activesupport/lib/active_support/notifications/instrumenter.rb#L86-L90">resets</a>the <code>@end</code>, <code>@cpu_time_finish</code> and <code>@allocation_count_finish</code> counters.</p><hr><p>To learn more about this feature, please check<a href="https://github.com/rails/rails/pull/33449">rails/rails#33449</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 allows configurable attribute on #has_secure_password]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-allows-configurable-attribute-name-on-has_secure_password"/>
      <updated>2019-04-23T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-allows-configurable-attribute-name-on-has_secure_password</id>
      <content type="html"><![CDATA[<p><a href="https://api.rubyonrails.org/v5.2/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password">has_secure_password</a>is used to encrypt and authenticate passwords using<a href="https://github.com/codahale/bcrypt-ruby">BCrypt</a> . It assumes the model has acolumn named <code>password_digest</code>.</p><p>Before Rails 6,<a href="https://api.rubyonrails.org/v5.2/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password">has_secure_password</a>did not accept any attribute as a parameter. So, if we needed<a href="https://github.com/codahale/bcrypt-ruby">BCrypt</a> encryption on a differentcolumn other than <code>password_digest</code>, we would have to manually encrypt the valuebefore storing it.</p><p>Rails 6 makes it easy and allows custom attributes as a parameter to<code>has_secure_password</code>. <code>has_secure_password</code> still defaults to <code>password</code> so itworks with previous versions of Rails. <code>has_secure_password</code> still needs thecolumn named <code>column_name_digest</code> defined on the model.</p><p><code>has_secure_password</code> also adds the <code>authenticate_column_name</code> method toauthenticate the custom column.</p><p>Let's check out how it works.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   has_secure_password&gt;&gt; end=&gt; [ActiveModel::Validations::ConfirmationValidator]&gt;&gt; user = User.create(email: 'amit.choudhary@bigbinary.com', password: 'amit.choudhary')BEGINUser Create (0.8ms)  INSERT INTO &quot;users&quot; (&quot;email&quot;, &quot;password_digest&quot;, &quot;created_at&quot;, &quot;updated_at&quot;) VALUES ($1, $2, $3, $4) RETURNING &quot;id&quot;  [[&quot;email&quot;, &quot;amit.choudhary@bigbinary.com&quot;], [&quot;password_digest&quot;, &quot;$2a$10$g6ZJNgakn4I1w/qjAx3vM.I76QSNjFCHtTtT9ovko/9Th50SEmIBO&quot;], [&quot;created_at&quot;, &quot;2019-03-17 23:30:13.754379&quot;], [&quot;updated_at&quot;, &quot;2019-03-17 23:30:13.754379&quot;]]COMMIT=&gt; #&lt;User id: 1, email: &quot;amit.choudhary@bigbinary.com&quot;, password_digest: &quot;$2a$10$g6ZJNgakn4I1w/qjAx3vM.I76QSNjFCHtTtT9ovko/9...&quot;, created_at: &quot;2019-03-17 23:30:13&quot;, updated_at: &quot;2019-03-17 23:30:13&quot;&gt;&gt;&gt; user.authenticate('amit.choudhary')=&gt; #&lt;User id: 1, email: &quot;amit.choudhary@bigbinary.com&quot;, password_digest: &quot;$2a$10$g6ZJNgakn4I1w/qjAx3vM.I76QSNjFCHtTtT9ovko/9...&quot;, created_at: &quot;2019-03-17 23:30:13&quot;, updated_at: &quot;2019-03-17 23:30:13&quot;&gt;&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   has_secure_password :transaction_password&gt;&gt; end=&gt; NoMethodError: undefined method 'fetch' for :transaction_password:Symbolfrom (irb):9:in '&lt;class:User&gt;'from (irb):8</code></pre><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   has_secure_password&gt;&gt;   has_secure_password :transaction_password&gt;&gt; end=&gt; [ActiveModel::Validations::ConfirmationValidator]&gt;&gt; user = User.create(email: 'amit.choudhary@bigbinary.com', password: 'amit.choudhary', transaction_password: 'amit.choudhary')BEGINUser Create (0.5ms)  INSERT INTO &quot;users&quot; (&quot;email&quot;, &quot;password_digest&quot;, &quot;transaction_password_digest&quot;, &quot;created_at&quot;, &quot;updated_at&quot;) VALUES ($1, $2, $3, $4, $5) RETURNING &quot;id&quot;  [[&quot;email&quot;, &quot;amit.choudhary@bigbinary.com&quot;], [&quot;password_digest&quot;, &quot;$2a$10$nUiO7E2XrIJx/sSdpG0JAOL00uFvPRH7kXHLk5f/6qA1zLPHIrpPy&quot;], [&quot;transaction_password_digest&quot;, &quot;$2a$10$l6cTpHwV9xOEn2.OumI29OnualGpvr1CgrNrbuMuHyGTltko8eBG2&quot;], [&quot;created_at&quot;, &quot;2019-03-17 23:42:28.723431&quot;], [&quot;updated_at&quot;, &quot;2019-03-17 23:42:28.723431&quot;]]COMMIT=&gt; #&lt;User id: 5, email: &quot;amit.choudhary@bigbinary.com&quot;, password_digest: [FILTERED], transaction_password_digest: [FILTERED], created_at: &quot;2019-03-17 23:42:28&quot;, updated_at: &quot;2019-03-17 23:42:28&quot;&gt;&gt;&gt; user.authenticate('amit.choudhary')=&gt; #&lt;User id: 5, email: &quot;amit.choudhary@bigbinary.com&quot;, password_digest: [FILTERED], transaction_password_digest: [FILTERED], created_at: &quot;2019-03-17 23:42:28&quot;, updated_at: &quot;2019-03-17 23:42:28&quot;&gt;&gt;&gt; user.authenticate_transaction_password('amit.choudhary')=&gt; #&lt;User id: 5, email: &quot;amit.choudhary@bigbinary.com&quot;, password_digest: [FILTERED], transaction_password_digest: [FILTERED], created_at: &quot;2019-03-17 23:42:28&quot;, updated_at: &quot;2019-03-17 23:42:28&quot;&gt;</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/26764">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 allows overriding ActiveModel::Errors#full_message]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-allows-to-override-the-activemodel-errors-full_message-format-at-the-model-level-and-at-the-attribute-level"/>
      <updated>2019-04-22T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-allows-to-override-the-activemodel-errors-full_message-format-at-the-model-level-and-at-the-attribute-level</id>
      <content type="html"><![CDATA[<h2>Before Rails 6</h2><p>Before Rails 6, the default format <code>%{attribute} %{message}</code> is used to displayvalidation error message for a model's attribute.</p><pre><code class="language-ruby">&gt; &gt; article = Article.new&gt; &gt; =&gt; #&lt;Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; article.errors.full_message(:title, &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;Title cannot be blank&quot;&gt; &gt;</code></pre><p>The default format can be overridden globally using a language-specific localefile.</p><pre><code class="language-yaml"># config/locales/en.ymlen:errors:format:&quot;'%{attribute}' %{message}&quot;</code></pre><p>With this change, the full error message is changed for all the attributes ofall models.</p><pre><code class="language-ruby">&gt; &gt; article = Article.new&gt; &gt; =&gt; #&lt;Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; article.errors.full_message(:title, &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;'Title' cannot be blank&quot;&gt; &gt; user = User.new&gt; &gt; =&gt; #&lt;User id: nil, first_name: nil, last_name: nil, country: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; user.errors.full_message(:first_name, &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;'First name' cannot be blank&quot;&gt; &gt;</code></pre><p>This trick works in some cases but it doesn't work if we have to customize theerror messages on the basis of specific models or attributes.</p><p>Before Rails 6, there is no easy way to generate error messages like shownbelow.</p><pre><code class="language-plaintext">The article's title cannot be empty</code></pre><p>or</p><pre><code class="language-plaintext">First name of a person cannot be blank</code></pre><p>If we change the <code>errors.format</code> to <code>The article's %{attribute} %{message}</code> in<code>config/locales/en.yml</code> then that format will be unexpectedly used for othermodels, too.</p><pre><code class="language-yaml"># config/locales/en.ymlen:errors:format:&quot;The article's %{attribute} %{message}&quot;</code></pre><p>This is what will happen if we make such a change.</p><pre><code class="language-ruby">&gt; &gt; article = Article.new&gt; &gt; =&gt; #&lt;Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; article.errors.full_message(:title, &quot;cannot be empty&quot;)&gt; &gt; =&gt; &quot;The article's Title cannot be empty&quot;&gt; &gt; user = User.new&gt; &gt; =&gt; #&lt;User id: nil, first_name: nil, last_name: nil, country: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; user.errors.full_message(:first_name, &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;The article's First name cannot be blank&quot;&gt; &gt;</code></pre><p>Notice the error message generated for the <code>:first_name</code> attribute of Usermodel. This does not look the way we want, right?</p><p>Let's see what is changed in Rails 6 to overcome this problem.</p><h2>Enhancements made to <code>ActiveModel::Errors#full_message</code> in Rails 6</h2><p>Overriding the format of error message globally using <code>errors.format</code> is stillsupported in Rails 6.</p><p>In addition to that, Rails 6 now also supports overriding the error message'sformat at the model level and at the attribute level.</p><p>In order to enable this support, we need to explicitly set<code>config.active_model.i18n_customize_full_message</code> to <code>true</code> in the Railsconfiguration file, preferably in <code>config/application.rb</code> which is implicitlyset to <code>false</code> by default.</p><h5>Overriding model level format</h5><p>We can customize the full error message format for each model separately.</p><pre><code class="language-yaml"># config/locales/en.ymlen:activerecord:errors:models:article:format: &quot;`%{attribute}`: %{message}&quot;user:format: &quot;%{attribute} of the user %{message}&quot;</code></pre><p>The full error messages will look like this.</p><pre><code class="language-ruby">&gt; &gt; article = Article.new&gt; &gt; =&gt; #&lt;Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; article.errors.full_message(:title, &quot;cannot be empty&quot;)&gt; &gt; =&gt; &quot;`Title`: cannot be empty&quot;&gt; &gt; article.valid?&gt; &gt; =&gt; false&gt; &gt; article.errors.full_messages&gt; &gt; =&gt; [&quot;`Title`: can't be blank&quot;]&gt; &gt; user = User.new&gt; &gt; =&gt; #&lt;User id: nil, first_name: nil, last_name: nil, country: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; user.errors.full_message(:first_name, &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;First name of the user cannot be blank&quot;&gt; &gt; comment = Comment.new&gt; &gt; =&gt; #&lt;Comment id: nil, message: nil, author_id: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; comment.errors.full_message(:message, &quot;is required&quot;)&gt; &gt; =&gt; &quot;Message is required&quot;&gt; &gt;</code></pre><p>Notice how the default format <code>%{attribute} %{message}</code> is used for generatingthe full error messages for the <code>Comment</code> model since its format is not beingoverridden.</p><p>Since the other methods such as <code>ActiveModel::Errors#full_messages</code>,<code>ActiveModel::Errors#full_messages_for</code>, <code>ActiveModel::Errors#to_hash</code> etc. usethe <code>ActiveModel::Errors#full_message</code> method under the hood, we get the fullerror messages according to the custom format in the returned values of thesemethods respectively as expected.</p><h5>Overriding attribute level format</h5><p>Similar to customizing format at the model level, we can customize the errorformat for specific attributes of individual models.</p><pre><code class="language-yaml"># config/locales/en.ymlen:activerecord:errors:models:article:attributes:title:format: &quot;The article's title %{message}&quot;user:attributes:first_name:format: &quot;%{attribute} of a person %{message}&quot;</code></pre><p>With such a configuration, we get the customized error message for the <code>title</code>attribute of the Article model.</p><pre><code class="language-ruby">&gt; &gt; article = Article.new&gt; &gt; =&gt; #&lt;Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; article.errors.full_message(:title, &quot;cannot be empty&quot;)&gt; &gt; =&gt; &quot;The article's title cannot be empty&quot;&gt; &gt; article.errors.full_message(:description, &quot;cannot be empty&quot;)&gt; &gt; =&gt; &quot;Description cannot be empty&quot;&gt; &gt; user = User.new&gt; &gt; =&gt; #&lt;User id: nil, first_name: nil, last_name: nil, country: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; user.errors.full_message(:first_name, &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;First name of a person cannot be blank&quot;&gt; &gt; user.errors.full_message(:last_name, &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;Last name cannot be blank&quot;&gt; &gt;</code></pre><p>Note that the error messages for the rest of the attributes were generated usingthe default <code>%{attribute} %{message}</code> format for which we didn't add customformats in the <code>config/locales/en.yml</code> manifest.</p><h5>Overriding model level format of deeply nested models</h5><pre><code class="language-yaml"># config/locales/en.ymlen:activerecord:errors:models:article/comments/attachments:format: &quot;%{message}&quot;</code></pre><pre><code class="language-ruby">&gt; &gt; article = Article.new&gt; &gt; =&gt; #&lt;Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; article.errors.full_message(:'comments/attachments.file_name', &quot;is required&quot;)&gt; &gt; =&gt; &quot;is required&quot;&gt; &gt; article.errors.full_message(:'comments/attachments.path', &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;cannot be blank&quot;&gt; &gt; article.errors.full_message(:'comments.message', &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;Comments message cannot be blank&quot;&gt; &gt;</code></pre><h5>Overriding attribute level format of deeply nested models</h5><pre><code class="language-yaml"># config/locales/en.ymlen:activerecord:errors:models:article/comments/attachments:attributes:file_name:format: &quot;File name of an attachment %{message}&quot;</code></pre><pre><code class="language-ruby">&gt; &gt; article = Article.new&gt; &gt; =&gt; #&lt;Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; article.errors.full_message(:'comments/attachments.file_name', &quot;is required&quot;)&gt; &gt; =&gt; &quot;File name of an attachment is required&quot;&gt; &gt; article.errors.full_message(:'comments/attachments.path', &quot;cannot be blank&quot;)&gt; &gt; =&gt; &quot;Comments/attachments path cannot be blank&quot;&gt; &gt;</code></pre><h2>Precedence</h2><p>The custom formats specified in the locale file has the following precedence inthe high to low order.</p><ul><li><code>activerecord.errors.models.article/comments/attachments.attributes.file_name.format</code></li><li><code>activerecord.errors.models.article/comments/attachments.format</code></li><li><code>activerecord.errors.models.article.attributes.title.format</code></li><li><code>activerecord.errors.models.article.format</code></li><li><code>errors.format</code></li></ul><hr><p>To learn more, please checkout<a href="https://github.com/rails/rails/pull/32956">rails/rails#32956</a> and<a href="https://github.com/rails/rails/pull/35789">rails/rails#35789</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveRecord::Relation#extract_associated]]></title>
       <author><name>Taha Husain</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-extract_associated"/>
      <updated>2019-04-17T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-extract_associated</id>
      <content type="html"><![CDATA[<p>Before Rails 6, if we want to extract associated records from an<code>ActiveRecord::Relation</code>, we would use <code>preload</code> and <code>collect</code>.</p><p>For example, we want to fetch <code>subscriptions</code> of some <code>users</code>. The query wouldlook as shown below.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">User.where(blocked: false).preload(:subscriptions).collect(&amp;:subscriptions)=&gt; # returns collection of subscription records</code></pre><p><a href="https://github.com/rails/rails/pull/35784">ActiveRecord::Relation#extract_associated</a>provides a shorthand to achieve same result and is more readable than former.</p><h4>Rails 6.0.0.beta3</h4><pre><code class="language-ruby">User.where(blocked: false).extract_associated(:subscriptions)=&gt; # returns the same collection of subscription records</code></pre><p>Here's the relevant <a href="https://github.com/rails/rails/pull/35784">pull request</a>for this change.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds implicit_order_column]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-implicit_order_column"/>
      <updated>2019-04-16T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-implicit_order_column</id>
      <content type="html"><![CDATA[<p>Rails 6 added <a href="https://github.com/rails/rails/pull/34480">implicit_order_column</a>on <code>ActiveRecord::ModelSchema</code> which allows us to define a custom column forimplicit ordering on the model level. If there is no <code>implicit_order_column</code>defined, Rails takes a primary key as the implicit order column. Also, beforeRails 6, the primary key was used to order records implicitly by default.</p><p>This has impact on methods like<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/FinderMethods.html#method-i-first">first</a>,<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/FinderMethods.html#method-i-last">last</a>and<a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/FinderMethods.html">many more</a>where implicit ordering is used.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   validates :name, presence: true&gt;&gt; end=&gt; {:presence=&gt;true}&gt;&gt; User.firstSELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;User id: 1, name: &quot;Amit&quot;, created_at: &quot;2019-03-11 00:18:41&quot;, updated_at: &quot;2019-03-11 00:18:41&quot;&gt;&gt;&gt; User.lastSELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; DESC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;User id: 2, name: &quot;Mark&quot;, created_at: &quot;2019-03-11 00:20:42&quot;, updated_at: &quot;2019-03-11 00:20:42&quot;&gt;&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   validates :name, presence: true&gt;&gt;   self.implicit_order_column = &quot;updated_at&quot;&gt;&gt; end=&gt; Traceback (most recent call last):        2: from (irb):10        1: from (irb):12:in '&lt;class:User&gt;'NoMethodError (undefined method 'implicit_order_column=' for #&lt;Class:0x00007faf4d6cb408&gt;)</code></pre><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   validates :name, presence: true&gt;&gt; end=&gt; {:presence=&gt;true}&gt;&gt; User.firstSELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;User id: 1, name: &quot;Amit&quot;, created_at: &quot;2019-03-11 00:18:41&quot;, updated_at: &quot;2019-03-11 00:18:41&quot;&gt;&gt;&gt; User.lastSELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; DESC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;User id: 2, name: &quot;Mark&quot;, created_at: &quot;2019-03-11 00:20:42&quot;, updated_at: &quot;2019-03-11 00:20:42&quot;&gt;&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   validates :name, presence: true&gt;&gt;   self.implicit_order_column = &quot;updated_at&quot;&gt;&gt; end=&gt; &quot;updated_at&quot;&gt;&gt; User.find(1).touchSELECT &quot;users&quot;.* FROM &quot;users&quot; WHERE &quot;users&quot;.&quot;id&quot; = $1 LIMIT $2  [[&quot;id&quot;, 1], [&quot;LIMIT&quot;, 1]]UPDATE &quot;users&quot; SET &quot;updated_at&quot; = $1 WHERE &quot;users&quot;.&quot;id&quot; = $2  [[&quot;updated_at&quot;, &quot;2019-03-11 00:23:33.369021&quot;], [&quot;id&quot;, 1]]=&gt; true&gt;&gt; User.firstSELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;updated_at&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;User id: 2, name: &quot;Mark&quot;, created_at: &quot;2019-03-11 00:20:42&quot;, updated_at: &quot;2019-03-11 00:23:09&quot;&gt;&gt;&gt; User.lastSELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;updated_at&quot; DESC LIMIT $1  [[&quot;LIMIT&quot;, 1]]=&gt; #&lt;User id: 1, name: &quot;Amit&quot;, created_at: &quot;2019-03-11 00:18:41&quot;, updated_at: &quot;2019-03-11 00:23:33&quot;&gt;</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/34480">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Bulk insert support in Rails 6]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/bulk-insert-support-in-rails-6"/>
      <updated>2019-04-15T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/bulk-insert-support-in-rails-6</id>
      <content type="html"><![CDATA[<p>Rails 6 has added support for bulk inserts similar to how bulk update issupported using <code>update_all</code> and bulk delete is supported using <code>delete_all</code>.</p><p>Bulk inserts can be performed using newly added methods: <code>insert_all</code>,<code>insert_all!</code> and <code>upsert_all</code>.</p><p>All of these new methods allow the insertion of multiple records of the samemodel into the database. A single <code>INSERT</code> SQL query is prepared by thesemethods and a single sql statement is sent to the database, withoutinstantiating the model or invoking Active Record callbacks or validations.</p><p>During bulk insertion, violation of primary key, violation of unique indexes,and violation of unique constraints is possible. Rails leveragesdatabase-specific features to either skip, or upsert the duplicates, dependingon the case.</p><p>Let's discuss <code>insert_all</code>, <code>insert_all!</code> and <code>upsert_all</code> methods in detail,which are all used to perform bulk insert.</p><p>We will create an <code>articles</code> table with two unique indexes.</p><pre><code class="language-ruby">create_table :articles do |t|  t.string :title, null: false  t.string :slug, null: false  t.string :author, null: false  t.text :description  t.index :slug, unique: true  t.index [:title, :author], unique: trueend</code></pre><p>Note that we do not allow duplicate <code>slug</code> columns. We also prevent records fromhaving duplicate <code>title</code> and <code>author</code> columns together.</p><p>To try out the examples provided in this blog post, please ensure to alwaysclean up the <code>articles</code> table before running any example.</p><h2>1. Performing bulk inserts by skipping duplicates</h2><p>Let's say we want to insert multiple articles at once into the database. It ispossible that certain records may violate the unique constraint(s) of the table.Such records are considered duplicates.</p><p>In other words, rows or records are determined to be unique by every uniqueindex on the table by default.</p><p>To skip the duplicate rows or records, and insert the rest of the records atonce, we can use <code>ActiveRecord::Persistence#insert_all</code> method.</p><h5>1.1 Behavior with PostgreSQL</h5><p>Let's run the following example on a PostgreSQL database.</p><pre><code class="language-ruby">result = Article.insert_all(  [    { id: 1,      title: 'Handling 1M Requests Per Second',      author: 'John',      slug: '1m-req-per-second' },    { id: 1, # duplicate 'id' here      title: 'Type Safety in Elm',      author: 'George',      slug: 'elm-type-safety' },    { id: 2,      title: 'Authentication with Devise - Part 1',      author: 'Laura',      slug: 'devise-auth-1' },    { id: 3,      title: 'Authentication with Devise - Part 1',      author: 'Laura', # duplicate 'title' &amp; 'author' here      slug: 'devise-auth-2' },    { id: 4,      title: 'Dockerizing and Deploying Rails App to Kubernetes',      author: 'Paul',      slug: 'rails-on-k8s' },    { id: 5,      title: 'Elm on Rails',      author: 'Amanda',      slug: '1m-req-per-second' }, # duplicate 'slug' here    { id: 6,      title: 'Working Remotely',      author: 'Greg',      slug: 'working-remotely' }  ])# Bulk Insert (2.3ms)  INSERT INTO &quot;articles&quot;(&quot;id&quot;,&quot;title&quot;,&quot;author&quot;,&quot;slug&quot;) VALUES (1, 'Handling 1M Requests  [...snip...] 'working-remotely') ON CONFLICT  DO NOTHING RETURNING &quot;id&quot;puts result.inspect#&lt;ActiveRecord::Result:0x00007fb6612a1ad8 @columns=[&quot;id&quot;], @rows=[[1], [2], [4], [6]], @hash_rows=nil, @column_types={&quot;id&quot;=&gt;#&lt;ActiveModel::Type::Integer:0x00007fb65f420078 @precision=nil, @scale=nil, @limit=8, @range=-9223372036854775808...9223372036854775808&gt;}&gt;puts Article.count# 4</code></pre><p>The <code>insert_all</code> method accepts a mandatory argument which should be an array ofhashes with the attributes of the same model. The keys in all hashes should besame.</p><p>Notice the <code>ON CONFLICT DO NOTHING</code> clause in the <code>INSERT</code> query. This clause issupported by PostgreSQL and SQLite databases. This instructs the database thatwhen there is a conflict or a unique key constraint violation during bulk insertoperation, to skip the conflicting record silently and proceed with theinsertion of the next record.</p><p>In the above example, we have exactly 3 records which violate various uniqueconstraints defined on the <code>articles</code> table.</p><p>One of the records being inserted has a duplicate <code>id: 1</code> attribute, whichviolates unique primary key constraint. Another record that has duplicate<code>title: 'Authentication with Devise - Part 1', author: 'Laura'</code> attributesviolates the multi-column unique index defined on <code>title</code> and <code>author</code> columns.Another record has duplicate <code>slug: '1m-req-per-second'</code> attributes violates theunique index defined on the <code>slug</code> column.</p><p>All of these records that violate any unique constraint or unique index areskipped and are not inserted into the database.</p><p>If successful, <code>ActiveRecord::Persistence#insert_all</code> returns an instance of<code>ActiveRecord::Result</code>. The contents of the result vary per database. In case ofPostgreSQL database, this result instance holds information about thesuccessfully inserted records such as the chosen column names, values of thethose columns in each successfully inserted row, etc.</p><p>For PostgreSQL, by default, <code>insert_all</code> method appends <code>RETURNING &quot;id&quot;</code> clauseto the SQL query where <code>id</code> is the primary key(s). This clause instructs thedatabase to return the <code>id</code> of every successfully inserted record. By inspectingthe result, especially the <code>@columns=[&quot;id&quot;], @rows=[[1], [2], [4], [6]]</code>attributes of the result instance, we can see that the records having <code>id</code>attribute with values <code>1, 2, 4 and 6</code> were successfully inserted.</p><p>What if we want to see more attributes and not just the <code>id</code> attribute of thesuccessfully inserted records in the result?</p><p>We should use the optional <code>returning</code> option, which accepts an array ofattribute names, which should be returned for all successfully inserted records!</p><pre><code class="language-ruby">result = Article.insert_all(  [    { id: 1,      title: 'Handling 1M Requests Per Second',      author: 'John',      slug: '1m-req-per-second' },    #...snip...  ],  returning: %w[ id title ])# Bulk Insert (2.3ms)  INSERT INTO &quot;articles&quot;(&quot;id&quot;,&quot;title&quot;,&quot;author&quot;,&quot;slug&quot;) VALUES (1, 'Handling 1M Requests  [...snip...] 'working-remotely') ON CONFLICT  DO NOTHING RETURNING &quot;id&quot;,&quot;title&quot;puts result.inspect#&lt;ActiveRecord::Result:0x00007f902a1196f0 @columns=[&quot;id&quot;, &quot;title&quot;], @rows=[[1, &quot;Handling 1M Requests Per Second&quot;], [2, &quot;Authentication with Devise - Part 1&quot;], [4, &quot;Dockerizing and Deploying Rails App to Kubernetes&quot;], [6, &quot;Working Remotely&quot;]], @hash_rows=nil, @column_types={&quot;id&quot;=&gt;#&lt;ActiveModel::Type::Integer:0x00007f90290ca8d0 @precision=nil, @scale=nil, @limit=8, @range=-9223372036854775808...9223372036854775808&gt;, &quot;title&quot;=&gt;#&lt;ActiveModel::Type::String:0x00007f9029978298 @precision=nil, @scale=nil, @limit=nil&gt;}&gt;puts result.pluck(&quot;id&quot;, &quot;title&quot;).inspect#[[1, &quot;Handling 1M Requests Per Second&quot;], [2, &quot;Authentication with Devise - Part 1&quot;], [4, &quot;Dockerizing and Deploying Rails App to Kubernetes&quot;], [6, &quot;Working Remotely&quot;]]</code></pre><p>Notice how the <code>INSERT</code> query appends <code>RETURNING &quot;id&quot;,&quot;title&quot;</code> clause and theresult now holds the <code>id</code> and <code>title</code> attributes of the successfully insertedrecords.</p><h5>1.2 Behavior with SQLite</h5><p>Similar to PostgreSQL, the violating records are skipped during the bulk insertoperation performed using <code>insert_all</code> when we run our example on a SQLitedatabase.</p><pre><code class="language-ruby">result = Article.insert_all(  [    { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' },    { id: 1, title: 'Type Safety in Elm', author: 'George', slug: 'elm-type-safety' }, # duplicate 'id' here    #...snip...  ])# Bulk Insert (1.6ms)  INSERT INTO &quot;articles&quot;(&quot;id&quot;,&quot;title&quot;,&quot;author&quot;,&quot;slug&quot;) VALUES (1, 'Handling 1M Requests [...snip...] 'working-remotely') ON CONFLICT  DO NOTHINGputs result.inspect#&lt;ActiveRecord::Result:0x00007fa9df448ff0 @columns=[], @rows=[], @hash_rows=nil, @column_types={}&gt;puts Article.pluck(:id, :title)#[[1, &quot;Handling 1M Requests Per Second&quot;], [2, &quot;Authentication with Devise - Part 1&quot;], [4, &quot;Dockerizing and Deploying Rails App to Kubernetes&quot;], [6, &quot;Working Remotely&quot;]]puts Article.count# 4</code></pre><p>Note that since SQLite does not support <code>RETURING</code> clause, it is not being addedto the SQL query. Therefore, the returned <code>ActiveRecord::Result</code> instance doesnot contain any useful information.</p><p>If we try to explicitly use the <code>returning</code> option when the database being usedis SQLite, then the <code>insert_all</code> method throws an error.</p><pre><code class="language-ruby">Article.insert_all(  [    { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' },    #...snip...  ],  returning: %w[ id title ])# ActiveRecord::ConnectionAdapters::SQLite3Adapter does not support :returning (ArgumentError)</code></pre><h5>1.3 Behavior with MySQL</h5><p>The records that violate primary key, unique key constraints, or unique indexesare skipped during bulk insert operation performed using <code>insert_all</code> on a MySQLdatabase.</p><pre><code class="language-ruby">result = Article.insert_all(  [    { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' },    { id: 1, title: 'Type Safety in Elm', author: 'George', slug: 'elm-type-safety' }, # duplicate 'id' here    #...snip...  ])# Bulk Insert (20.3ms)  INSERT INTO `articles`(`id`,`title`,`author`,`slug`) VALUES (1, 'Handling 1M Requests [...snip...] 'working-remotely') ON DUPLICATE KEY UPDATE `id`=`id`puts result.inspect#&lt;ActiveRecord::Result:0x000055d6cfea7580 @columns=[], @rows=[], @hash_rows=nil, @column_types={}&gt;puts Article.pluck(:id, :title)#[[1, &quot;Handling 1M Requests Per Second&quot;], [2, &quot;Authentication with Devise - Part 1&quot;], [4, &quot;Dockerizing and Deploying Rails App to Kubernetes&quot;], [6, &quot;Working Remotely&quot;]]puts Article.count# 4</code></pre><p>Here, the <code>ON DUPLICATE KEY UPDATE 'id'='id'</code> clause in the <code>INSERT</code> query isessentially doing the same thing as the <code>ON CONFLICT DO NOTHING</code> clausesupported by PostgreSQL and SQLite.</p><p>Since MySQL does not support <code>RETURING</code> clause, it is not being included in theSQL query and therefore, the result doesn't contain any useful information.</p><p>Explicitly trying to use <code>returning</code> option with <code>insert_all</code> method on a MySQLdatabase throws<code>ActiveRecord::ConnectionAdapters::Mysql2Adapter does not support :returning</code>error.</p><h2>2. Performing bulk inserts by skipping duplicates on a specified unique constraint but raising exception if records violate other unique constraints</h2><p>In the previous case, we were skipping the records that were violating anyunique constraints. In some case, we may want to skip duplicates caused by onlya specific unique index but abort transaction if the other records violate anyother unique constraints.</p><p>The optional <code>unique_by</code> option of the <code>insert_all</code> method allows to define sucha unique constraint.</p><h5>2.1 Behavior with PostgreSQL and SQLite</h5><p>Let's see an example to skip duplicate records that violate only the specifiedunique index <code>:index_articles_on_title_and_author</code> using <code>unique_by</code> option. Theduplicate records that do not violate <code>index_articles_on_title_and_author</code> indexare not skipped, and therefore throw an error.</p><pre><code class="language-ruby">result = Article.insert_all(  [    { .... },    { .... }, # duplicate 'id' here    { .... },    { .... }, # duplicate 'title' and 'author' here    { .... },    { .... }, # duplicate 'slug' here    { .... }  ],  unique_by: :index_articles_on_title_and_author)# PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint &quot;articles_pkey&quot; (ActiveRecord::RecordNotUnique)# DETAIL:  Key (id)=(1) already exists.</code></pre><p>In case of SQLite, the error appears as shown below.</p><pre><code class="language-text"># SQLite3::ConstraintException: UNIQUE constraint failed: articles.id (ActiveRecord::RecordNotUnique)</code></pre><p>In this case we get <code>ActiveRecord::RecordNotUnique</code> error which is caused by theviolation of primary key constraint on the <code>id</code> column.</p><p>It didn't skip the second record in the example above which violated the uniqueindex on primary key <code>id</code> since the <code>unique_by</code> option was specified with adifferent unique index.</p><p>When an exception occurs, no record persists to the database since <code>insert_all</code>executes just a single SQL query.</p><p>The <code>unique_by</code> option can be identified by columns or a unique index name.</p><p><code>~~~ruby</code> unique_by: :index_articles_on_title_and_author</p><h2>is same as</h2><p>unique_by: %i[ title author ]</p><h2>Also,</h2><p>unique_by: :slug</p><h2>is same as</h2><p>unique_by: %i[ :slug ]</p><h2>and also same as</h2><p>unique_by: :index_articles_on_slug</p><pre><code class="language-ruby">Let's remove (or fix) the record that has duplicate primary keyandre-run the above example.~~~rubyresult = Article.insert_all(  [    { .... },    { .... },    { .... },    { .... }, # duplicate 'title' and 'author' here    { .... },    { .... }, # duplicate 'slug' here    { .... }  ],  unique_by: :index_articles_on_title_and_author)# PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint &quot;index_articles_on_slug&quot; (ActiveRecord::RecordNotUnique)# DETAIL:  Key (slug)=(1m-req-per-second) already exists.</code></pre><p>In case of SQLite, the error looks appears as shown below.</p><pre><code class="language-ruby"># SQLite3::ConstraintException: UNIQUE constraint failed: articles.slug (ActiveRecord::RecordNotUnique)</code></pre><p>The <code>ActiveRecord::RecordNotUnique</code> error in the example above now says the<code>index_articles_on_slug</code> unique constraint is violated. Note how itintentionally didn't raise an error for the unique constraint violated on the<code>title</code> and <code>author</code> columns by the fourth record in the examples above.</p><p>Now we will remove (or fix) the record that has same slug.</p><pre><code class="language-ruby">result = Article.insert_all(  [    { .... },    { .... },    { .... },    { .... }, # duplicate 'title' and 'author' here    { .... },    { .... },    { .... }  ],  unique_by: :index_articles_on_title_and_author)# Bulk Insert (2.5ms)  INSERT INTO &quot;articles&quot;(&quot;id&quot;,&quot;title&quot;,&quot;author&quot;,&quot;slug&quot;) VALUES (1, 'Handling 1M Requests Per Second', [...snip...] 'working-remotely') ON CONFLICT (&quot;title&quot;,&quot;author&quot;) DO NOTHING RETURNING &quot;id&quot;puts result.inspect#&lt;ActiveRecord::Result:0x00007fada2069828 @columns=[&quot;id&quot;], @rows=[[1], [7], [2], [4], [5], [6]], @hash_rows=nil, @column_types={&quot;id&quot;=&gt;#&lt;ActiveModel::Type::Integer:0x00007fad9fdb9df0 @precision=nil, @scale=nil, @limit=8, @range=-9223372036854775808...9223372036854775808&gt;}&gt;</code></pre><p>Here, the fourth record was skipped since that record violates the unique index<code>index_articles_on_title_and_author</code> specified by the <code>unique_by</code> option.</p><p>Similarly, we can specify a different unique index using the <code>unique_by</code> option.For example, if we specify <code>unique_by: :slug</code> option then the records containingduplicate <code>slug</code> columns will be skipped, but would raise<code>ActiveRecord::RecordNotUnique</code> exception if any record violates other uniqueconstraints.</p><h5>2.2 Behavior with MySQL</h5><p>The <code>unique_by</code> option is not supported when the database is MySQL.</p><h2>3. Raising exception if any of the records being bulk inserted violate any unique constraints</h2><p>The <code>insert_all!</code> method (with bang version) never skips a duplicate record. Ifa record violates any unique constraints, then <code>insert_all!</code> method would simplythrow an <code>ActiveRecord::RecordNotUnique</code> error.</p><p>When database is PostgreSQL, <code>insert_all!</code> method can accept optional<code>returning</code> option, which we discussed in depth in 1.1 section above.</p><p>The <code>unique_by</code> option is not supported by the <code>insert_all!</code> method.</p><h2>4. Performing bulk upserts (updates or inserts)</h2><p>So far, in the sections 1, 2 and 3 above, we discussed either skipping theduplicates or raising an exception if a duplicate is encountered during bulkinserts. Sometimes, we want to update the existing record when a duplicateoccurs otherwise insert a new record. This operation is called upsert becauseeither it tries to update the record, or if there is no record to update, thenit tries to insert.</p><p>The <code>upsert_all</code> method in Rails 6 allows performing bulk upserts.</p><p>Let's see it's usage and behavior with different database systems.</p><h6>4.1 <code>upsert_all</code> in MySQL</h6><p>Let's try to bulk upsert multiple articles containing some duplicates.</p><pre><code class="language-ruby">result = Article.upsert_all(  [    { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' },    { id: 1, .... }, # duplicate 'id' here    { id: 2, .... },    { id: 3, .... }, # duplicate 'title' and 'author' here    { id: 4, .... },    { id: 5, .... }, # duplicate 'slug' here    { id: 6, .... }  ])# Bulk Insert (26.3ms)  INSERT INTO `articles`(`id`,`title`,`author`,`slug`) VALUES (1, 'Handling 1M Requests Per Second', 'John', [...snip...] 'working-remotely') ON DUPLICATE KEY UPDATE `title`=VALUES(`title`),`author`=VALUES(`author`),`slug`=VALUES(`slug`)puts result.inspect#&lt;ActiveRecord::Result:0x000055a43c1fae10 @columns=[], @rows=[], @hash_rows=nil, @column_types={}&gt;puts Article.count# 5puts Article.all#&lt;ActiveRecord::Relation [#&lt;Article id: 1, title: &quot;Type Safety in Elm&quot;, slug: &quot;elm-type-safety&quot;, author: &quot;George&quot;, description: nil&gt;, #&lt;Article id: 2, title: &quot;Authentication with Devise - Part 1&quot;, slug: &quot;devise-auth-2&quot;, author: &quot;Laura&quot;, description: nil&gt;, #&lt;Article id: 4, title: &quot;Dockerizing and Deploying Rails App to Kubernetes&quot;, slug: &quot;rails-on-k8s&quot;, author: &quot;Paul&quot;, description: nil&gt;, #&lt;Article id: 5, title: &quot;Elm on Rails&quot;, slug: &quot;1m-req-per-second&quot;, author: &quot;Amanda&quot;, description: nil&gt;, #&lt;Article id: 6, title: &quot;Working Remotely&quot;, slug: &quot;working-remotely&quot;, author: &quot;Greg&quot;, description: nil&gt;]&gt;</code></pre><p>The persisted records in the database look exactly as intended. Let's discuss itin detail.</p><p>The second row in the input array that has the <code>id: 1</code> attribute replaced thefirst row, which also had the duplicate <code>id: 1</code> attribute.</p><p>The fourth row that has <code>id: 3</code> replaced the attributes of the third row sinceboth had duplicate &quot;title&quot; and &quot;author&quot; attributes.</p><p>The rest of the rows were not duplicates or no longer became duplicates, andtherefore were inserted without any issues.</p><p>Note that the <code>returning</code> and <code>unique_by</code> options are not supported in the<code>upsert_all</code> method when the database is MySQL.</p><h6>4.2 <code>upsert_all</code> in SQLite</h6><p>Let's try to execute the same example from the above section 4.1 when databaseis SQLite.</p><pre><code class="language-ruby">result = Article.upsert_all(  [    { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' },    { id: 1, title: 'Type Safety in Elm', author: 'George', slug: 'elm-type-safety' }, # duplicate 'id' here    { id: 2, title: 'Authentication with Devise - Part 1', author: 'Laura', slug: 'devise-auth-1' },    { id: 3, title: 'Authentication with Devise - Part 1', author: 'Laura', slug: 'devise-auth-2' }, # duplicate 'title' and 'author' here    { id: 4, title: 'Dockerizing and Deploying Rails App to Kubernetes', author: 'Paul', slug: 'rails-on-k8s' },    { id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }, # duplicate 'slug' here    { id: 6, title: 'Working Remotely', author: 'Greg', slug: 'working-remotely' }  ])# Bulk Insert (4.0ms)  INSERT INTO &quot;articles&quot;(&quot;id&quot;,&quot;title&quot;,&quot;author&quot;,&quot;slug&quot;) VALUES (1, 'Handling 1M Requests Per Second', [...snip...] 'working-remotely') ON CONFLICT (&quot;id&quot;) DO UPDATE SET &quot;title&quot;=excluded.&quot;title&quot;,&quot;author&quot;=excluded.&quot;author&quot;,&quot;slug&quot;=excluded.&quot;slug&quot;# SQLite3::ConstraintException: UNIQUE constraint failed: articles.title, articles.author (ActiveRecord::RecordNotUnique)</code></pre><p>The bulk upsert operation failed in the above example due to<code>ActiveRecord::RecordNotUnique</code> exception.</p><p>Why it didn't work similar to MySQL?</p><p>As per the documentation of MySQL, an upsert operation takes place if a newrecord violates any unique constraint.</p><p>Whereas, in case of SQLite, by default, new record replaces existing record whenboth the existing and new record have the same primary key. If a record violatesany other unique constraints other than the primary key, it then raises<code>ActiveRecord::RecordNotUnique</code> exception.</p><p>The <code>ON CONFLICT (&quot;id&quot;) DO UPDATE</code> clause in the SQL query above conveys thesame intent.</p><p>Therefore, <code>upsert_all</code> in SQLite doesn't behave exactly same as in MySQL.</p><p>As a workaround, we will need to upsert records with the help of multiple<code>upsert_all</code> calls with the usage of <code>unique_by</code> option.</p><p>If a duplicate record is encountered during the upsert operation, which violatesthe unique index specified using <code>unique_by</code> option then it will replace theattributes of the existing matching record.</p><p>Let's try to understand this workaround with another example.</p><pre><code class="language-ruby">Article.upsert_all(  [    { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' },    { id: 1, title: 'Type Safety in Elm', author: 'George', slug: 'elm-type-safety' }, # duplicate 'id' here  ],  unique_by: :id)Article.upsert_all(  [    { id: 2, title: 'Authentication with Devise - Part 1', author: 'Laura', slug: 'devise-auth-1' },    { id: 3, title: 'Authentication with Devise - Part 1', author: 'Laura', slug: 'devise-auth-2' }, # duplicate 'title' and 'author' here    { id: 4, title: 'Dockerizing and Deploying Rails App to Kubernetes', author: 'Paul', slug: 'rails-on-k8s' },    { id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }, # duplicate 'slug' here    { id: 6, title: 'Working Remotely', author: 'Greg', slug: 'working-remotely' }  ],  unique_by: %i[ title author ])puts Article.count# 5puts Article.all#&lt;ActiveRecord::Relation [#&lt;Article id: 1, title: &quot;Type Safety in Elm&quot;, slug: &quot;elm-type-safety&quot;, author: &quot;George&quot;, description: nil&gt;, #&lt;Article id: 2, title: &quot;Authentication with Devise - Part 1&quot;, slug: &quot;devise-auth-2&quot;, author: &quot;Laura&quot;, description: nil&gt;, #&lt;Article id: 4, title: &quot;Dockerizing and Deploying Rails App to Kubernetes&quot;, slug: &quot;rails-on-k8s&quot;, author: &quot;Paul&quot;, description: nil&gt;, #&lt;Article id: 5, title: &quot;Elm on Rails&quot;, slug: &quot;1m-req-per-second&quot;, author: &quot;Amanda&quot;, description: nil&gt;, #&lt;Article id: 6, title: &quot;Working Remotely&quot;, slug: &quot;working-remotely&quot;, author: &quot;Greg&quot;, description: nil&gt;]&gt;</code></pre><p>Here, we first tried to upsert all the records which violated the unique primarykey index on <code>id</code> column. Later, we upsert successfully all the remainingrecords, which violated the unique index on the <code>title</code> and <code>author</code> columns.</p><p>Note that since the first record's <code>slug</code> attribute was already replaced withthe second record's <code>slug</code> attribute; the last second record having <code>id: 5</code>didn't raise an exception because of duplicate <code>slug</code> column.</p><h6>4.3 <code>upsert_all</code> in PostgreSQL</h6><p>We will run the same example in the 4.1 section above with PostgreSQL database.</p><pre><code class="language-ruby">result = Article.upsert_all(  [    { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' },    { id: 1, title: 'Type Safety in Elm', author: 'George', slug: 'elm-type-safety' }, # duplicate 'id' here    { id: 2, title: 'Authentication with Devise - Part 1', author: 'Laura', slug: 'devise-auth-1' },    { id: 3, title: 'Authentication with Devise - Part 1', author: 'Laura', slug: 'devise-auth-2' }, # duplicate 'title' and 'author' here    { id: 4, title: 'Dockerizing and Deploying Rails App to Kubernetes', author: 'Paul', slug: 'rails-on-k8s' },    { id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }, # duplicate 'slug' here    { id: 6, title: 'Working Remotely', author: 'Greg', slug: 'working-remotely' }  ])# PG::CardinalityViolation: ERROR:  ON CONFLICT DO UPDATE command cannot affect row a second time (ActiveRecord::StatementInvalid)# HINT:  Ensure that no rows proposed for insertion within the same command have duplicate constrained values.</code></pre><p>The bulk upsert operation failed in the above example due to<code>ActiveRecord::StatementInvalid</code> exception which was caused by another<code>PG::CardinalityViolation</code> exception.</p><p>The <code>PG::CardinalityViolation</code> exception originates from<a href="https://github.com/postgres/postgres/blob/beeb8e2e0717065296dc7b32daba2d66f0f931dd/src/backend/executor/nodeModifyTable.c#L1335-L1355">here</a>.</p><p>The <code>PG::CardinalityViolation</code> exception occurs when a row cannot be updated asecond time in the same <code>ON CONFLICT DO UPDATE</code> SQL query. PostgreSQL assumesthis behavior would lead the same row to updated a second time in the same SQLquery, in unspecified order, non-deterministically.</p><p>PostgreSQL recommends it is the developer's responsibility to prevent thissituation from occurring.</p><p>Here's more discussion about this issue -<a href="https://github.com/rails/rails/issues/35519">rails/rails#35519</a>.</p><p>Therefore, the <code>upsert_all</code> method doesn't work as intended due to the abovelimitation in PostgreSQL.</p><p>As a workaround, we can divide the single <code>upsert_all</code> query into multiple<code>upsert_all</code> queries with the use of <code>unique_by</code> option similar to how we did incase of SQLite in the 4.2 section above.</p><pre><code class="language-ruby">Article.insert_all(  [    { id: 1, title: 'Handling 1M requests per second', author: 'John', slug: '1m-req-per-second' },    { id: 2, title: 'Authentication with Devise - Part 1', author: 'Laura', slug: 'devise-auth-1' },    { id: 4, title: 'Dockerizing and deploy Rails app to Kubernetes', author: 'Paul', slug: 'rails-on-k8s' },    { id: 6, title: 'Working Remotely', author: 'Greg', slug: 'working-remotely' }  ])Article.upsert_all(  [    { id: 1, title: 'Type Safety in Elm', author: 'George', slug: 'elm-type-safety' }, # duplicate 'id' here  ])Article.upsert_all(  [    { id: 3, title: 'Authentication with Devise - Part 1', author: 'Laura', slug: 'devise-auth-2' }, # duplicate 'title' and 'author' here  ],  unique_by: :index_articles_on_title_and_author)Article.upsert_all(  [    { id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }, # duplicate 'slug' here  ])puts Article.count# 5puts Article.all#&lt;ActiveRecord::Relation [#&lt;Article id: 1, title: &quot;Type Safety in Elm&quot;, slug: &quot;elm-type-safety&quot;, author: &quot;George&quot;, description: nil&gt;, #&lt;Article id: 2, title: &quot;Authentication with Devise - Part 1&quot;, slug: &quot;devise-auth-2&quot;, author: &quot;Laura&quot;, description: nil&gt;, #&lt;Article id: 4, title: &quot;Dockerizing and deploy Rails app to Kubernetes&quot;, slug: &quot;rails-on-k8s&quot;, author: &quot;Paul&quot;, description: nil&gt;, #&lt;Article id: 5, title: &quot;Elm on Rails&quot;, slug: &quot;1m-req-per-second&quot;, author: &quot;Amanda&quot;, description: nil&gt;, #&lt;Article id: 6, title: &quot;Working Remotely&quot;, slug: &quot;working-remotely&quot;, author: &quot;Greg&quot;, description: nil&gt;]&gt;</code></pre><p>For reference, note that the <code>upsert_all</code> method also accepts <code>returning</code> optionfor PostgreSQL which we have already discussed in the 1.1 section above.</p><h2>5. <code>insert</code>, <code>insert!</code> and <code>upsert</code></h2><p>Rails 6 has also introduced three more additional methods namely <code>insert</code>,<code>insert!</code> and <code>upsert</code> for convenience.</p><p>The <code>insert</code> method inserts a single record into the database. If that recordviolates a uniqueness constrain, then the <code>insert</code> method will skip insertingrecord into the database without raising an exception.</p><p>Similarly, the <code>insert!</code> method also inserts a single record into the database,but will raise <code>ActiveRecord::RecordNotUnique</code> exception if that record violatesa uniqueness constraint.</p><p>The <code>upsert</code> method inserts or updates a single record into the database similarto how <code>upsert_all</code> does.</p><p>The methods <code>insert</code>, <code>insert!</code> and <code>upsert</code> are wrappers around <code>insert_all</code>,<code>insert_all!</code> and <code>upsert_all</code> respectively.</p><p>Let's see some examples to understand the usage of these methods.</p><pre><code class="language-ruby">Article.insert({ id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }, unique_by: :slug)# is same asArticle.insert_all([{ id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }], unique_by: :slug)</code></pre><pre><code class="language-ruby">Article.insert!({ id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }, returning: %w[ id title ])# is same asArticle.insert_all!([{ id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }], returning: %w[ id title ])</code></pre><pre><code class="language-ruby">Article.upsert({ id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }, unique_by: :slug, returning: %w[ id title ])# is same asArticle.upsert_all([{ id: 5, title: 'Elm on Rails', author: 'Amanda', slug: '1m-req-per-second' }], unique_by: :slug, returning: %w[ id title ])</code></pre><hr><p>To learn more about the bulk insert feature and its implementation, please check<a href="https://github.com/rails/rails/pull/35077">rails/rails#35077</a>,<a href="https://github.com/rails/rails/pull/35546">rails/rails#35546</a> and<a href="https://github.com/rails/rails/pull/35854">rails/rails#35854</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 drops support for PostgreSQL version less than 9.3]]></title>
       <author><name>Prathamesh Sonpatki</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-drops-support-for-postgresql-less-than-9-3"/>
      <updated>2019-04-10T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-drops-support-for-postgresql-less-than-9-3</id>
      <content type="html"><![CDATA[<p>Before Rails 6, Rails was supporting PostgreSQL from version 9.1 and above. Butin Rails 6,<a href="https://github.com/rails/rails/pull/34520">support for versions less than 9.3 is dropped</a>.If your PostgreSQL version is less than 9.3 then an<a href="https://travis-ci.org/codetriage/codetriage/jobs/506656780#L1591">error</a> isshown as follows.</p><pre><code class="language-text">Your version of PostgreSQL (90224) is too old. Active Record supports PostgreSQL &gt;= 9.3.</code></pre><p>Travis CI uses<a href="https://docs.travis-ci.com/user/database-setup/#postgresql">PostgreSQL 9.2</a> bydefault in their images. So this error can occur while testing the app on TravisCI with Rails 6. It can be resolved by using an<a href="https://github.com/codetriage/codetriage/blob/rails-6/.travis.yml#L12-L13">add-on for PostgreSQL</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 requires Ruby 2.5 or newer]]></title>
       <author><name>Vishal Telangre</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-requires-ruby-2-5-or-newer"/>
      <updated>2019-04-09T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-requires-ruby-2-5-or-newer</id>
      <content type="html"><![CDATA[<p>As per <a href="https://github.com/rails/rails/pull/34754">rails/rails#34754</a>, a Rails 6app requires Ruby version 2.5 or newer.</p><p>Let's discuss what we need to know if we are dealing with Rails 6.</p><h2>Ensuring a valid Ruby version is set while creating a new Rails 6 app</h2><p>While creating a new Rails 6 app, we need to ensure that the current Rubyversion in the shell is set to 2.5 or newer.</p><p>If it is set to an older version then the same version will be used by the<code>rails new</code> command to set the Ruby version in <code>.ruby-version</code> and in <code>Gemfile</code>respectively in the created Rails app.</p><pre><code class="language-plaintext">\$ ruby -vruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]\$ rails new meme-wizardcreatecreate README.mdcreate Rakefilecreate .ruby-versioncreate config.rucreate .gitignorecreate Gemfile[...] omitted the rest of the output\$ cd meme-wizard &amp;&amp; grep -C 2 -Rn -a &quot;2.3.1&quot; ../.ruby-version:1:2.3.1----./Gemfile-2-git_source(:github) { |repo| &quot;https://github.com/#{repo}.git&quot; }./Gemfile-3-./Gemfile:4:ruby '2.3.1'./Gemfile-5-./Gemfile-6-# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'</code></pre><p>An easy fix for this is to install a Ruby version 2.5 or newer and use thatversion prior to running the <code>rails new</code> command.</p><pre><code class="language-plaintext">\$ ruby -vruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]\$ rbenv local 2.6.0\$ ruby -vruby 2.6.0p0 (2018-12-25 revision 66547) [x86_64-darwin18]\$ rails new meme-wizard\$ cd meme-wizard &amp;&amp; grep -C 2 -Rn -a &quot;2.6.0&quot; ../.ruby-version:1:2.6.0----./Gemfile-2-git_source(:github) { |repo| &quot;https://github.com/#{repo}.git&quot; }./Gemfile-3-./Gemfile:4:ruby '2.6.0'./Gemfile-5-./Gemfile-6-# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'</code></pre><h2>Upgrading an older Rails app to Rails 6</h2><p>While upgrading an older Rails app to Rails 6, we need to update the Rubyversion to 2.5 or newer in <code>.ruby-version</code> and <code>Gemfile</code> files respectively.</p><h2>What else do we need to know?</h2><p>Since<a href="https://blog.bigbinary.com/2018/02/06/ruby-2-5-added-hash-slice-method.html">Ruby 2.5 has added <code>Hash#slice</code> method</a>,the extension method with the same name defined by<code>activesupport/lib/active_support/core_ext/hash/slice.rb</code> has been<a href="https://github.com/rails/rails/pull/34754">removed from Rails 6</a>.</p><p>Similarly, Rails 6 has also <a href="https://github.com/rails/rails/pull/32034">removed</a>the extension methods <code>Hash#transform_values</code> and <code>Hash#transform_values!</code> fromActive Support in favor of the native methods with the same names which exist inRuby. These methods were<a href="https://blog.bigbinary.com/2017/06/14/ruby-2-4-added-hash-transform-values-and-its-destructive-version-from-active-support.html">introduced in Ruby 2.4 natively</a>.</p><p>If we try to explicitly require <code>active_support/core_ext/hash/transform_values</code>then it would print a deprecation warning.</p><pre><code class="language-ruby">&gt; &gt; require &quot;active_support/core_ext/hash/transform_values&quot;# DEPRECATION WARNING: Ruby 2.5+ (required by Rails 6) provides Hash#transform_values natively, so requiring active_support/core_ext/hash/transform_values is no longer necessary. Requiring it will raise LoadError in Rails 6.1. (called from irb_binding at (irb):1)=&gt; true</code></pre>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 database seed uses inline Active Job adapter]]></title>
       <author><name>Prathamesh Sonpatki</name></author>
      <link href="https://www.bigbinary.com/blog/database-seeding-task-uses-inline-active-job-adapter-in-rails-6"/>
      <updated>2019-04-03T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/database-seeding-task-uses-inline-active-job-adapter-in-rails-6</id>
      <content type="html"><![CDATA[<p>We use the <code>db:seed</code> task to seed the database in Rails apps. Recently an<a href="https://github.com/rails/rails/issues/34939">issue</a> was reported on Rails issuetracker where the <code>db:seed</code> task was not finishing.</p><p>In development environment, Rails uses<a href="https://api.rubyonrails.org/v5.2/classes/ActiveJob/QueueAdapters/AsyncAdapter.html">async adapter</a>as the default Active Job adapter. The Async adapter runs jobs with anin-process thread pool.</p><p>This specific issue was happening because the seed task was trying to attach afile using Active Storage. Active Storage<a href="https://github.com/rails/rails/blob/9e34df00039d63b5672315419e76f06f80ef3dc4/activestorage/app/models/active_storage/attachment.rb#L37">adds a job in the background</a>during the attachment process. This task was not getting executed properly usingthe async adapter and it was causing the seed task to hang without exiting.</p><p>It was found out that by using the<a href="https://api.rubyonrails.org/v5.2/classes/ActiveJob/QueueAdapters/InlineAdapter.html">inline</a>adapter in development environment, this issue goes away. But making a wholesalechange of making the default adapter in development environment as inlineadapter defeats the purpose of having the async adapter as default in the firstplace.</p><p>Instead a change is made to<a href="https://github.com/rails/rails/pull/34953">execute all the code related to seeding using inline adapter</a>.The inline adapter makes sure that all the code will be executed immediately.</p><p>As the inline adapter does not allow queuing up the jobs in future, this canresult into an error if the seeding code somehow triggers such jobs. This<a href="https://github.com/rails/rails/issues/35812#issuecomment-479385857">issue</a> isalready reported on Github.</p><h3>Update</h3><p>Active Job is optional framework and we can skip it completely. Now that seedingdepends on presence of Active Job, it was throwing an error when Active Job wasnot part of the application. Also, executing the jobs inline automatically, whenusers has set the Active Job queue adapter to something of their choice wassurprising for the users. So a<a href="https://github.com/rails/rails/pull/35896">change</a> has been made to load theseeds inline only when Active Job is included in the application and the queueadapter is <code>async</code>. This makes it backward compatible as well it does not changeuser's choice of queue adapter automatically.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveRecord::Relation#reselect]]></title>
       <author><name>Abhay Nikam</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-reselect"/>
      <updated>2019-04-02T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-reselect</id>
      <content type="html"><![CDATA[<p>Rails have<a href="https://apidock.com/rails/ActiveRecord/QueryMethods/rewhere"><code>rewhere</code></a> and<a href="https://apidock.com/rails/ActiveRecord/QueryMethods/reorder"><code>reorder</code></a> methodsto change the previously set conditions attributes to new attributes which aregiven as an argument to method.</p><p>Before Rails 6, if you want to change the previously set <code>select</code> statementattributes to new attributes, it was done as follows.</p><pre><code class="language-ruby">&gt;&gt; Post.select(:title, :body).unscope(:select).select(:views)   SELECT &quot;posts&quot;.&quot;views&quot; FROM &quot;posts&quot; LIMIT ? [&quot;LIMIT&quot;, 1]]</code></pre><p>In Rails 6, <code>ActiveRecord::Relation#reselect</code> method is added.</p><p>The <code>reselect</code> method is similar to <code>rewhere</code> and <code>reorder</code>. <code>reselect</code> is ashort-hand for <code>unscope(:select).select(fields)</code>.</p><p>Here is how <code>reselect</code> method can be used.</p><pre><code class="language-ruby">&gt;&gt; Post.select(:title, :body).reselect(:views)   SELECT &quot;posts&quot;.&quot;views&quot; FROM &quot;posts&quot; LIMIT ? [&quot;LIMIT&quot;, 1]]</code></pre><p>Check out the <a href="https://github.com/rails/rails/pull/33611">pull request</a> for moredetails on this.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveModel::Errors#of_kind?]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activemodel-errors-of_kind-"/>
      <updated>2019-04-01T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activemodel-errors-of_kind-</id>
      <content type="html"><![CDATA[<p>Rails 6 added <a href="https://github.com/rails/rails/pull/34866">of_kind?</a> on<code>ActiveModel::Errors</code>. It returns true if the <code>ActiveModel::Errors</code> object hasprovided a key and message associated with it. The default message is<code>:invalid</code>.</p><p><a href="https://github.com/rails/rails/pull/34866">of_kind?</a> is same as<a href="https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-added-3F">ActiveModel::Errors#added?</a>but, it doesn't take extra options as a parameter.</p><p>Let's checkout how it works.</p><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt;&gt; class User &lt; ApplicationRecord&gt;&gt;   validates :name, presence: true&gt;&gt; end&gt;&gt; user = User.new=&gt; =&gt; #&lt;User id: nil, name: nil, password: nil, created_at: nil, updated_at: nil&gt;&gt;&gt; user.valid?=&gt; false&gt;&gt; user.errors=&gt; #&lt;ActiveModel::Errors:0x00007fc462a1d140 @base=#&lt;User id: nil, name: nil, password: nil, created_at: nil, updated_at: nil&gt;, @messages={:name=&gt;[&quot;can't be blank&quot;]}, @details={:name=&gt;[{:error=&gt;:blank}]}&gt;&gt;&gt; user.errors.of_kind?(:name)=&gt; false&gt;&gt; user.errors.of_kind?(:name, :blank)=&gt; true&gt;&gt; user.errors.of_kind?(:name, &quot;can't be blank&quot;)=&gt; true&gt;&gt; user.errors.of_kind?(:name, &quot;is blank&quot;)=&gt; false</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/34866">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 shows routes in expanded format]]></title>
       <author><name>Prathamesh Sonpatki</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-shows-routes-in-expanded-format"/>
      <updated>2019-03-27T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-shows-routes-in-expanded-format</id>
      <content type="html"><![CDATA[<p>The output of <code>rails routes</code> is in the table format.</p><pre><code class="language-bash">$ rails routes   Prefix Verb   URI Pattern               Controller#Action    users GET    /users(.:format)          users#index          POST   /users(.:format)          users#create new_user GET    /users/new(.:format)      users#newedit_user GET    /users/:id/edit(.:format) users#edit     user GET    /users/:id(.:format)      users#show          PATCH  /users/:id(.:format)      users#update          PUT    /users/:id(.:format)      users#update          DELETE /users/:id(.:format)      users#destroy</code></pre><p>If we have long route names, they don't fit on the terminal window as the outputlines wrap with each other.</p><p><img src="/blog_images/2019/rails-6-shows-routes-in-expanded-format/overlapping_routes.png" alt="Example of overlapping routes"></p><p>Rails 6 has added a way to display the routes in an<a href="https://github.com/rails/rails/pull/32130">expanded format</a>.</p><p>We can pass <code>--expanded</code> switch to the <code>rails routes</code> command to see this inaction.</p><pre><code class="language-bash">$ rails routes --expanded--[ Route 1 ]--------------------------------------------------------------Prefix            | usersVerb              | GETURI               | /users(.:format)Controller#Action | users#index--[ Route 2 ]--------------------------------------------------------------Prefix            |Verb              | POSTURI               | /users(.:format)Controller#Action | users#create--[ Route 3 ]--------------------------------------------------------------Prefix            | new_userVerb              | GETURI               | /users/new(.:format)Controller#Action | users#new--[ Route 4 ]--------------------------------------------------------------Prefix            | edit_userVerb              | GETURI               | /users/:id/edit(.:format)Controller#Action | users#edit</code></pre><p>This shows the output of the routes command in much more user friendly manner.</p><p>The <code>--expanded</code> switch can be used in conjunction with<a href="https://blog.bigbinary.com/2016/02/16/rails-5-options-for-rake-routes.html">other switches for searching specific routes</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveModel::Errors#slice!]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activemodel-errors-slice"/>
      <updated>2019-03-26T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activemodel-errors-slice</id>
      <content type="html"><![CDATA[<p>Rails 6 added <a href="https://github.com/rails/rails/pull/34489">slice!</a> on<code>ActiveModel::Errors</code>. With this addition, it becomes quite easy to select justa few keys from errors and show or return them. Before Rails 6, we needed toconvert the <code>ActiveModel::Errors</code> object to a hash before slicing the keys.</p><p>Let's checkout how it works.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">&gt;&gt; user = User.new=&gt; #&lt;User id: nil, email: nil, password: nil, created_at: nil, updated_at: nil&gt;&gt;&gt; user.valid?=&gt; false&gt;&gt; user.errors=&gt; #&lt;ActiveModel::Errors:0x00007fc46700df10 @base=#&lt;User id: nil, email: nil, password: nil, created_at: nil, updated_at: nil&gt;, @messages={:email=&gt;[&quot;can't be blank&quot;], :password=&gt;[&quot;can't be blank&quot;]}, @details={:email=&gt;[{:error=&gt;:blank}], :password=&gt;[{:error=&gt;:blank}]}&gt;&gt;&gt; user.errors.slice!=&gt; Traceback (most recent call last):        1: from (irb):16NoMethodError (undefined method 'slice!' for #&lt;ActiveModel::Errors:0x00007fa1f0e46eb8&gt;)Did you mean?  slice_when&gt;&gt; errors = user.errors.to_h&gt;&gt; errors.slice!(:email)=&gt; {:password=&gt;[&quot;can't be blank&quot;]}&gt;&gt; errors=&gt; {:email=&gt;[&quot;can't be blank&quot;]}</code></pre><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt;&gt; user = User.new=&gt; #&lt;User id: nil, email: nil, password: nil, created_at: nil, updated_at: nil&gt;&gt;&gt; user.valid?=&gt; false&gt;&gt; user.errors=&gt; #&lt;ActiveModel::Errors:0x00007fc46700df10 @base=#&lt;User id: nil, email: nil, password: nil, created_at: nil, updated_at: nil&gt;, @messages={:email=&gt;[&quot;can't be blank&quot;], :password=&gt;[&quot;can't be blank&quot;]}, @details={:email=&gt;[{:error=&gt;:blank}], :password=&gt;[{:error=&gt;:blank}]}&gt;&gt;&gt; user.errors.slice!(:email)=&gt; {:password=&gt;[&quot;can't be blank&quot;]}&gt;&gt; user.errors=&gt; #&lt;ActiveModel::Errors:0x00007fc46700df10 @base=#&lt;User id: nil, email: nil, password: nil, created_at: nil, updated_at: nil&gt;, @messages={:email=&gt;[&quot;can't be blank&quot;]}, @details={:email=&gt;[{:error=&gt;:blank}]}&gt;</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/34489">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds create_or_find_by and create_or_find_by!]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-create_or_find_by"/>
      <updated>2019-03-25T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-create_or_find_by</id>
      <content type="html"><![CDATA[<p>Rails 6 added <a href="https://github.com/rails/rails/pull/31989">create_or_find_by</a> and<a href="https://github.com/rails/rails/pull/31989">create_or_find_by!</a>. Both of thesemethods rely on unique constraints on the database level. If creation fails, itis because of the unique constraints on one or all of the given columns, and itwill try to find the record using <code>find_by!</code>.</p><p><a href="https://github.com/rails/rails/pull/31989">create_or_find_by</a> is an improvementover<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/Relation.html#method-i-find_or_create_by">find_or_create_by</a>because<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/Relation.html#method-i-find_or_create_by">find_or_create_by</a>first queries for the record, and then inserts it if none is found. This couldlead to a race condition.</p><p>As mentioned by DHH in the pull request,<a href="https://github.com/rails/rails/pull/31989">create_or_find_by</a> has a few constoo:</p><ul><li>The table must have unique constraints on the relevant columns.</li><li>This method relies on exception handling, which is generally slower.</li></ul><p><a href="https://github.com/rails/rails/pull/31989">create_or_find_by!</a> raises anexception when creation fails because of the validations.</p><p>Let's see how both methods work in Rails 6.0.0.beta2.</p><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt; &gt; class CreateUsers &lt; ActiveRecord::Migration[6.0]&gt; &gt; def change&gt; &gt; create_table :users do |t|&gt; &gt; t.string :name, index: { unique: true }&gt; &gt;&gt; &gt;       t.timestamps&gt; &gt;     end&gt; &gt;&gt; &gt; end&gt; &gt; end&gt; &gt; class User &lt; ApplicationRecord&gt; &gt; validates :name, presence: true&gt; &gt; end&gt; &gt; User.create_or_find_by(name: 'Amit')&gt; &gt; BEGIN&gt; &gt; INSERT INTO &quot;users&quot; (&quot;name&quot;, &quot;created_at&quot;, &quot;updated_at&quot;) VALUES ($1, $2, \$3) RETURNING &quot;id&quot; [[&quot;name&quot;, &quot;Amit&quot;], [&quot;created_at&quot;, &quot;2019-03-07 09:33:23.391719&quot;], [&quot;updated_at&quot;, &quot;2019-03-07 09:33:23.391719&quot;]]&gt; &gt; COMMIT=&gt; #&lt;User id: 1, name: &quot;Amit&quot;, created_at: &quot;2019-03-07 09:33:23&quot;, updated_at: &quot;2019-03-07 09:33:23&quot;&gt;&gt; &gt; User.create_or_find_by(name: 'Amit')&gt; &gt; BEGIN&gt; &gt; INSERT INTO &quot;users&quot; (&quot;name&quot;, &quot;created_at&quot;, &quot;updated_at&quot;) VALUES ($1, $2, \$3) RETURNING &quot;id&quot; [[&quot;name&quot;, &quot;Amit&quot;], [&quot;created_at&quot;, &quot;2019-03-07 09:46:37.189068&quot;], [&quot;updated_at&quot;, &quot;2019-03-07 09:46:37.189068&quot;]]&gt; &gt; ROLLBACK=&gt; #&lt;User id: 1, name: &quot;Amit&quot;, created_at: &quot;2019-03-07 09:33:23&quot;, updated_at: &quot;2019-03-07 09:33:23&quot;&gt;&gt; &gt; User.create_or_find_by(name: nil)&gt; &gt; BEGIN&gt; &gt; COMMIT=&gt; #&lt;User id: nil, name: nil, created_at: nil, updated_at: nil&gt;&gt; &gt; User.create_or_find_by!(name: nil)=&gt; Traceback (most recent call last):1: from (irb):2ActiveRecord::RecordInvalid (Validation failed: Name can't be blank)</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/31989">pull request</a>.</p><p>Also note, <a href="https://github.com/rails/rails/pull/31989">create_or_find_by</a> canlead to primary keys running out, if the type of primary key is <code>int</code>. Thishappens because each time<a href="https://github.com/rails/rails/pull/31989">create_or_find_by</a> hits<code>ActiveRecord::RecordNotUnique</code>, it does not rollback auto-increment of theprimary key. The problem is discussed in this<a href="https://github.com/rails/rails/issues/35543">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 raises ActiveModel::MissingAttributeError]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-raises-activemodel-missingattributeerror-when-update_columns-is-used-with-non-existing-attribute"/>
      <updated>2019-03-20T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-raises-activemodel-missingattributeerror-when-update_columns-is-used-with-non-existing-attribute</id>
      <content type="html"><![CDATA[<p>Rails 6 raises <code>ActiveModel::MissingAttributeError</code> when<a href="https://github.com/rails/rails/commit/b63701e272">update_columns</a> is used witha non-existing attribute. Before Rails 6,<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/Persistence.html#method-i-update_columns">update_columns</a>raised an <code>ActiveRecord::StatementInvalid</code> error.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">&gt; &gt; User.first.update_columns(email: 'amit@bigbinary.com')&gt; &gt; SELECT &quot;users&quot;.\* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; ASC LIMIT $1  [[&quot;LIMIT&quot;, 1]]UPDATE &quot;users&quot; SET &quot;email&quot; = $1 WHERE &quot;users&quot;.&quot;id&quot; = \$2 [[&quot;email&quot;, &quot;amit@bigbinary.com&quot;], [&quot;id&quot;, 1]]=&gt; Traceback (most recent call last):1: from (irb):8ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column &quot;email&quot; of relation &quot;users&quot; does not exist)LINE 1: UPDATE &quot;users&quot; SET &quot;email&quot; = $1 WHERE &quot;users&quot;.&quot;id&quot; = $2^: UPDATE &quot;users&quot; SET &quot;email&quot; = $1 WHERE &quot;users&quot;.&quot;id&quot; = $2</code></pre><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt; &gt; User.first.update_columns(email: 'amit@bigbinary.com')&gt; &gt; SELECT &quot;users&quot;.\* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; ASC LIMIT ? [[&quot;LIMIT&quot;, 1]]Traceback (most recent call last):1: from (irb):1ActiveModel::MissingAttributeError (can't write unknown attribute `email`)</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/commit/b63701e272">commit</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 ActiveRecord::Base.configurations]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-changed-activerecord-base-configurations-result-to-an-object"/>
      <updated>2019-03-19T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-changed-activerecord-base-configurations-result-to-an-object</id>
      <content type="html"><![CDATA[<p>Rails 6 changed the return value of<a href="https://github.com/rails/rails/pull/33637">ActiveRecord::Base.configurations</a>to an object of <code>ActiveRecord::DatabaseConfigurations</code>. Before Rails 6,<a href="https://api.rubyonrails.org/v5.2.2/classes/ActiveRecord/Core.html#method-c-configurations">ActiveRecord::Base.configurations</a>returned a hash with all the database configurations. We can call <code>to_h</code> on theobject of <code>ActiveRecord::DatabaseConfigurations</code> to get a hash.</p><p>A method named <a href="https://github.com/rails/rails/pull/33637">configs_for</a> has alsobeen added on to fetch configurations for a particular environment.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">&gt;&gt; ActiveRecord::Base.configurations=&gt; {&quot;development&quot;=&gt;{&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/development.sqlite3&quot;}, &quot;test&quot;=&gt;{&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/test.sqlite3&quot;}, &quot;production&quot;=&gt;{&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/production.sqlite3&quot;}}</code></pre><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt;&gt; ActiveRecord::Base.configurations=&gt; #&lt;ActiveRecord::DatabaseConfigurations:0x00007fc18274f9f0 @configurations=[#&lt;ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fc18274f680 @env_name=&quot;development&quot;, @spec_name=&quot;primary&quot;, @config={&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/development.sqlite3&quot;}&gt;, #&lt;ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fc18274f608 @env_name=&quot;test&quot;, @spec_name=&quot;primary&quot;, @config={&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/test.sqlite3&quot;}&gt;, #&lt;ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fc18274f590 @env_name=&quot;production&quot;, @spec_name=&quot;primary&quot;, @config={&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/production.sqlite3&quot;}&gt;]&gt;&gt;&gt; ActiveRecord::Base.configurations.to_h=&gt; {&quot;development&quot;=&gt;{&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/development.sqlite3&quot;}, &quot;test&quot;=&gt;{&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/test.sqlite3&quot;}, &quot;production&quot;=&gt;{&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/production.sqlite3&quot;}}&gt;&gt; ActiveRecord::Base.configurations['development']=&gt; {&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/development.sqlite3&quot;}&gt;&gt; ActiveRecord::Base.configurations.configs_for(env_name: &quot;development&quot;)=&gt; [#&lt;ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fc18274f680 @env_name=&quot;development&quot;, @spec_name=&quot;primary&quot;, @config={&quot;adapter&quot;=&gt;&quot;sqlite3&quot;, &quot;pool&quot;=&gt;5, &quot;timeout&quot;=&gt;5000, &quot;database&quot;=&gt;&quot;db/development.sqlite3&quot;}&gt;]</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/33637">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 shows unpermitted params in logs in color]]></title>
       <author><name>Prathamesh Sonpatki</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-shows-unpermitted-params-in-logs-in-color"/>
      <updated>2019-03-18T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-shows-unpermitted-params-in-logs-in-color</id>
      <content type="html"><![CDATA[<p>Strong parameters allow us to control the user input in our Rails app. Indevelopment environment the unpermitted parameters are shown in the log asfollows.</p><p><img src="/blog_images/2019/rails-6-shows-unpermitted-params-in-logs-in-color/before.png" alt="Unpermitted params before Rails 6"></p><p>It is easy to miss this message in the flurry of other messages.</p><p>Rails 6 has added a change to<a href="https://github.com/rails/rails/pull/34617">show these params in red color</a> forbetter visibility.</p><p><img src="/blog_images/2019/rails-6-shows-unpermitted-params-in-logs-in-color/after.png" alt="Unpermitted params after Rails 6"></p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 delete_by, destroy_by ActiveRecord::Relation]]></title>
       <author><name>Abhay Nikam</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-delete_by-and-activerecord-relation-destroy_by"/>
      <updated>2019-03-13T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-delete_by-and-activerecord-relation-destroy_by</id>
      <content type="html"><![CDATA[<p>As described by DHH in <a href="https://github.com/rails/rails/issues/35304">the issue</a>,Rails has <code>find_or_create_by</code>, <code>find_by</code> and similar methods to create and findthe records matching the specified conditions. Rails was missing similar featurefor deleting/destroying the record(s).</p><p>Before Rails 6, deleting/destroying the record(s) which are matching the givencondition was done as shown below.</p><pre><code class="language-ruby"># Example to destroy all authors matching the given conditionAuthor.find_by(email: &quot;abhay@example.com&quot;).destroyAuthor.where(email: &quot;abhay@example.com&quot;, rating: 4).destroy_all# Example to delete all authors matching the given conditionAuthor.find_by(email: &quot;abhay@example.com&quot;).deleteAuthor.where(email: &quot;abhay@example.com&quot;, rating: 4).delete_all</code></pre><p>The above examples were missing the symmetry like <code>find_or_create_by</code> and<code>find_by</code> methods.</p><p>In Rails 6, the new <code>delete_by</code> and <code>destroy_by</code> methods have been added asActiveRecord::Relation methods. <code>ActiveRecord::Relation#delete_by</code> is short-handfor <code>relation.where(conditions).delete_all</code>. Similarly,<code>ActiveRecord::Relation#destroy_by</code> is short-hand for<code>relation.where(conditions).destroy_all</code>.</p><p>Here is how it can be used.</p><pre><code class="language-ruby"># Example to destroy all authors matching the given condition using destroy_byAuthor.destroy_by(email: &quot;abhay@example.com&quot;)Author.destroy_by(email: &quot;abhay@example.com&quot;, rating: 4)# Example to destroy all authors matching the given condition using delete_byAuthor.delete_by(email: &quot;abhay@example.com&quot;)Author.delete_by(email: &quot;abhay@example.com&quot;, rating: 4)</code></pre><p>Check out the <a href="https://github.com/rails/rails/pull/35316">pull request</a> for moredetails on this.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveRecord::Relation#touch_all]]></title>
       <author><name>Amit Choudhary</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-touch-all"/>
      <updated>2019-03-12T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-touch-all</id>
      <content type="html"><![CDATA[<p>Before moving forward, we need to understand what the<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/Persistence.html#method-i-touch">touch</a>method does.<a href="https://api.rubyonrails.org/v5.2/classes/ActiveRecord/Persistence.html#method-i-touch">touch</a>is used to update the <code>updated_at</code> timestamp by defaulting to the current time.It also takes custom time or different columns as parameters.</p><p>Rails 6 has added <a href="https://github.com/rails/rails/pull/31513">touch_all</a> onActiveRecord::Relation to touch multiple records in one go. Before Rails 6, weneeded to iterate all records using an iterator to achieve this result.</p><p>Let's take an example in which we call<a href="https://github.com/rails/rails/pull/31513">touch_all</a> on all user records.</p><h4>Rails 5.2</h4><pre><code class="language-ruby">&gt;&gt; User.countSELECT COUNT(\*) FROM &quot;users&quot;=&gt; 3&gt;&gt; User.all.touch_all=&gt; Traceback (most recent call last):1: from (irb):2NoMethodError (undefined method 'touch_all' for #&lt;User::ActiveRecord_Relation:0x00007fe6261f9c58&gt;)&gt;&gt; User.all.each(&amp;:touch)SELECT &quot;users&quot;.* FROM &quot;users&quot;begin transaction  UPDATE &quot;users&quot; SET &quot;updated_at&quot; = ? WHERE &quot;users&quot;.&quot;id&quot; = ?  [[&quot;updated_at&quot;, &quot;2019-03-05 17:45:51.495203&quot;], [&quot;id&quot;, 1]]commit transactionbegin transaction  UPDATE &quot;users&quot; SET &quot;updated_at&quot; = ? WHERE &quot;users&quot;.&quot;id&quot; = ?  [[&quot;updated_at&quot;, &quot;2019-03-05 17:45:51.503415&quot;], [&quot;id&quot;, 2]]commit transactionbegin transaction  UPDATE &quot;users&quot; SET &quot;updated_at&quot; = ? WHERE &quot;users&quot;.&quot;id&quot; = ?  [[&quot;updated_at&quot;, &quot;2019-03-05 17:45:51.509058&quot;], [&quot;id&quot;, 3]]commit transaction=&gt; [#&lt;User id: 1, name: &quot;Sam&quot;, created_at: &quot;2019-03-05 16:09:29&quot;, updated_at: &quot;2019-03-05 17:45:51&quot;&gt;, #&lt;User id: 2, name: &quot;John&quot;, created_at: &quot;2019-03-05 16:09:43&quot;, updated_at: &quot;2019-03-05 17:45:51&quot;&gt;, #&lt;User id: 3, name: &quot;Mark&quot;, created_at: &quot;2019-03-05 16:09:45&quot;, updated_at: &quot;2019-03-05 17:45:51&quot;&gt;]</code></pre><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt;&gt; User.countSELECT COUNT(*) FROM &quot;users&quot;=&gt; 3&gt;&gt; User.all.touch_allUPDATE &quot;users&quot; SET &quot;updated_at&quot; = ?  [[&quot;updated_at&quot;, &quot;2019-03-05 16:08:47.490507&quot;]]=&gt; 3</code></pre><p><a href="https://github.com/rails/rails/pull/31513">touch_all</a> returns count of therecords on which it is called.</p><p><a href="https://github.com/rails/rails/pull/31513">touch_all</a> also takes a custom timeor different columns as parameters.</p><h4>Rails 6.0.0.beta2</h4><pre><code class="language-ruby">&gt;&gt; User.countSELECT COUNT(*) FROM &quot;users&quot;=&gt; 3&gt;&gt; User.all.touch_all(time: Time.new(2019, 3, 2, 1, 0, 0))UPDATE &quot;users&quot; SET &quot;updated_at&quot; = ?  [[&quot;updated_at&quot;, &quot;2019-03-02 00:00:00&quot;]]=&gt; 3&gt;&gt; User.all.touch_all(:created_at)UPDATE &quot;users&quot; SET &quot;updated_at&quot; = ?, &quot;created_at&quot; = ?  [[&quot;updated_at&quot;, &quot;2019-03-05 17:55:41.828347&quot;], [&quot;created_at&quot;, &quot;2019-03-05 17:55:41.828347&quot;]]=&gt; 3</code></pre><p>Here is the relevant <a href="https://github.com/rails/rails/pull/31513">pull request</a>.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds negative scopes on enum]]></title>
       <author><name>Abhay Nikam</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-negative-scopes-on-enum"/>
      <updated>2019-03-06T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-negative-scopes-on-enum</id>
      <content type="html"><![CDATA[<p>When an enum attribute is defined on a model, Rails adds some default scopes tofilter records based on values of enum on enum field.</p><p>Here is how enum scope can be used.</p><pre><code class="language-ruby">class Post &lt; ActiveRecord::Base  enum status: %i[drafted active trashed]endPost.drafted # =&gt; where(status: :drafted)Post.active  # =&gt; where(status: :active)</code></pre><p>In Rails 6, negative scopes are added on the enum values.</p><p>As mentioned by DHH in the pull request,</p><blockquote><p>these negative scopes are convenient when you want to disallow access incontrollers</p></blockquote><p>Here is how they can be used.</p><pre><code class="language-ruby">class Post &lt; ActiveRecord::Base  enum status: %i[drafted active trashed]endPost.not_drafted # =&gt; where.not(status: :drafted)Post.not_active  # =&gt; where.not(status: :active)</code></pre><p>Check out the <a href="https://github.com/rails/rails/pull/35381">pull request</a> for moredetails on this.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Rails 6 adds ActiveRecord::Relation#pick]]></title>
       <author><name>Prathamesh Sonpatki</name></author>
      <link href="https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-pick"/>
      <updated>2019-01-16T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/rails-6-adds-activerecord-relation-pick</id>
      <content type="html"><![CDATA[<p>Before Rails 6,selecting only the first value for a columnfrom a set of records was cumbersome.Let's say we want only the first namefrom all the posts with category &quot;Rails 6&quot;.</p><pre><code class="language-ruby">&gt;&gt; Post.where(category: &quot;Rails 6&quot;).limit(1).pluck(:name).first   SELECT &quot;posts&quot;.&quot;name&quot;   FROM &quot;posts&quot;   WHERE &quot;posts&quot;.&quot;category&quot; = ?   LIMIT ?  [[&quot;category&quot;, &quot;Rails 6&quot;], [&quot;LIMIT&quot;, 1]]=&gt; &quot;Rails 6 introduces awesome shiny features!&quot;</code></pre><p>In Rails 6, the new<a href="https://github.com/rails/rails/pull/31941">ActiveRecord::Relation#pick</a>method has been added which provides a shortcut to select the first value.</p><pre><code class="language-ruby">&gt;&gt; Post.where(category: &quot;Rails 6&quot;).pick(:name)   SELECT &quot;posts&quot;.&quot;name&quot;   FROM &quot;posts&quot;   WHERE &quot;posts&quot;.&quot;category&quot; = ?   LIMIT ?  [[&quot;category&quot;, &quot;Rails 6&quot;], [&quot;LIMIT&quot;, 1]]=&gt; &quot;Rails 6 introduces awesome shiny features!&quot;</code></pre><p>This method <a href="https://github.com/rails/rails/blob/45b898afc07dca936df13795dd5179bff5ae9a90/activerecord/lib/active_record/relation/calculations.rb#L203-L219">internally applies</a> <code>limit(1)</code> on the relation beforepicking up the first value.So it is usefulwhen the relation is already reduced to a single row.</p><p>It can also select values for multiple columns.</p><pre><code class="language-ruby">&gt;&gt; Post.where(category: &quot;Rails 6&quot;).pick(:name, :author)   SELECT &quot;posts&quot;.&quot;name&quot;, &quot;posts&quot;.&quot;author&quot;   FROM &quot;posts&quot;   WHERE &quot;posts&quot;.&quot;category&quot; = ?   LIMIT ?  [[&quot;category&quot;, &quot;Rails 6&quot;], [&quot;LIMIT&quot;, 1]]=&gt; [&quot;Rails 6.0 new features&quot;, &quot;prathamesh&quot;]</code></pre>]]></content>
    </entry>
     </feed>