<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alessandro Rodi</title>
    <description>The latest articles on DEV Community by Alessandro Rodi (@coorasse).</description>
    <link>https://dev.to/coorasse</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F288528%2F7f5e16c3-3595-43f9-9958-46807290e35c.jpeg</url>
      <title>DEV Community: Alessandro Rodi</title>
      <link>https://dev.to/coorasse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coorasse"/>
    <language>en</language>
    <item>
      <title>Rails Blog with DatoCMS</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Sat, 25 Jan 2025 22:30:11 +0000</pubDate>
      <link>https://dev.to/coorasse/rails-blog-with-datocms-2h1e</link>
      <guid>https://dev.to/coorasse/rails-blog-with-datocms-2h1e</guid>
      <description>&lt;p&gt;DatoCMS is an amazing Headless CMS. I wrote already in the past &lt;a href="https://dev.to/renuo/datocms-with-ruby-on-rails-3ae5"&gt;how to integrate it into your Ruby On Rails application&lt;/a&gt;, and today I want to show you how to implement a real blog mimicking what is already done for other frameworks, for example &lt;a href="https://nextjs-demo-bay.vercel.app/" rel="noopener noreferrer"&gt;nextjs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Dato project
&lt;/h2&gt;

&lt;p&gt;You can &lt;a href="https://www.datocms.com/marketplace/starters/nextjs-template-blog" rel="noopener noreferrer"&gt;clone the existing template&lt;/a&gt; for this tutorial. This template will setup all the models you need on Dato and create some blog posts.&lt;/p&gt;

&lt;p&gt;Head to the Project Settings and save your GraphQL API Token for later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Rails App
&lt;/h2&gt;

&lt;p&gt;Our Rails 8 application is set up using a simple &lt;code&gt;rails new dato-blog&lt;/code&gt; command. In a few seconds you'll have your Rails 8 app ready and you can start it with &lt;code&gt;bin/dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can now include the &lt;a href="https://github.com/renuo/dato-rails" rel="noopener noreferrer"&gt;&lt;code&gt;dato-rails&lt;/code&gt;&lt;/a&gt; gem in your Gemfile and set the API token in your credentials as &lt;code&gt;dato.api_token&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;You can finally write a simple test to verify that the installation is successful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/models/dato_queries_test.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'test_helper'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatoQueriesTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_homepage_query&lt;/span&gt;
    &lt;span class="n"&gt;homepage_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="no"&gt;GQLi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;allPosts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;title&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homepage_query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;assert_not_empty&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allPosts&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this test does, is to run a GraphQL Query and test the connection to DatoCMS.&lt;/p&gt;

&lt;p&gt;You can always head to the CDA Playground in DatoCMS to create new queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple styling
&lt;/h2&gt;

&lt;p&gt;Let's also set up Bootstrap for some simple, default styles. The Dato Rails integration does not provide any CSS, so you can easily integrate your favorite CSS Framework.&lt;/p&gt;

&lt;p&gt;You can add Bootstrap in your &lt;code&gt;application.html.erb&lt;/code&gt; to have some styling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/layouts/application.html.erb

&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;


&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Blog Post Page
&lt;/h2&gt;

&lt;p&gt;Head to the &lt;a href="https://nextjs-demo-bay.vercel.app/" rel="noopener noreferrer"&gt;Blog Example&lt;/a&gt; to see a Preview of the website we want to build. We will start with the single Blog Post page and then we will implement the Homepage.&lt;/p&gt;

&lt;p&gt;The first thing you need is the query to fetch a single blog post.&lt;/p&gt;

&lt;p&gt;You can create a &lt;code&gt;models/dato_queries.rb&lt;/code&gt; file to include your queries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# models/dato_queries.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;DatoQueries&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;homepage_query&lt;/span&gt;
    &lt;span class="no"&gt;GQLi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;allPosts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;        
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;blog_post_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;GQLi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;filter: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;slug: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;eq: &lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;
        &lt;span class="n"&gt;slug&lt;/span&gt;
        &lt;span class="n"&gt;date&lt;/span&gt;
        &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nb"&gt;name&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;  
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before digging further, let's test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/models/dato_queries_test.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'test_helper'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatoQueriesTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;DatoQueries&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_homepage_query&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homepage_query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;assert_not_empty&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allPosts&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_blog_post_query&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blog_post_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mistakes-tourists-make-on-their-first-trip-abroad'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;assert_not_empty&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the queries, we need to implement the page. &lt;/p&gt;

&lt;p&gt;Head to your routes.rb and add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:blog_posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;param: :slug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and our controller will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/blog_posts_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogPostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;DatoQueries&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blog_post_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;BlogPostComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;here is a simple implementation of the BlogPostComponent that renders the blog post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/blog_post_component.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogPostComponent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseComponent&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/components/blog_post_component.html.erb

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now head to &lt;a href="http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad" rel="noopener noreferrer"&gt;http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad&lt;/a&gt; to see your post title.&lt;/p&gt;

&lt;p&gt;You now rendered content from DatoCMS into your Rails Application. Let's add the header image now.&lt;/p&gt;

&lt;p&gt;The first thing we need to do is to fetch the image from Dato. Our query will become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;blog_post_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;GQLi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;filter: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;slug: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;eq: &lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;title&lt;/span&gt;
      &lt;span class="n"&gt;slug&lt;/span&gt;
      &lt;span class="n"&gt;date&lt;/span&gt;
      &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;name&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;coverImage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;responsiveImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;imgixParams: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;fm: :jpg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;fit: :crop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;w: &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;h: &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;___&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Fragments&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResponsiveImage&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dato-rails&lt;/code&gt; offers a GraphQL fragment to fetch images from DatoCMS in the right format to be displayed. &lt;a href="https://www.datocms.com/blog/best-way-for-handling-react-images#putting-it-all-together-introducing-the-responsiveimage-query" rel="noopener noreferrer"&gt;Read more about it on DatoCMS&lt;/a&gt;. We are fetching the coverImage and using the existing ResponsiveImage fragment provided by &lt;code&gt;dato-rails&lt;/code&gt; to get all the data we need.&lt;/p&gt;

&lt;p&gt;With this updated query, we can now display the header image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/components/blog_post_component.html.erb

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResponsiveImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;coverImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;responsiveImage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;From now on, you can look at the &lt;a href="https://github.com/renuo/dato-blog-example/tree/main/app/components" rel="noopener noreferrer"&gt;component code&lt;/a&gt; and the query code on the &lt;a href="https://github.com/renuo/dato-blog-example" rel="noopener noreferrer"&gt;Github Repository&lt;/a&gt; to see the final version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Homepage
&lt;/h2&gt;

&lt;p&gt;Rendering the Homepage has nothing special at this point. You can check the &lt;a href="https://github.com/renuo/dato-blog-example" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt; for the final code, but you basically need a root in your routes.rb, a controller, a new component, and a query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/config/routes.rb&lt;/span&gt;

&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="s2"&gt;"homepage#show"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/homepage_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomepageController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;DatoQueries&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;HomepageComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homepage_query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preview mode
&lt;/h2&gt;

&lt;p&gt;We have the following concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;graphQL Query: logic to fetch data&lt;/li&gt;
&lt;li&gt;controller: responsible for choosing a query and using it to renderer a component&lt;/li&gt;
&lt;li&gt;component: responsible for the view logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DatoCMS offers a preview mode. You are responsible of deciding how and when a preview is displayed, this can be enabled for specific users of your application, or with a secret token in the URL. In our example we will use the preview parameter of the URL.&lt;/p&gt;

&lt;p&gt;You can change the BlogPostsController as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;BlogPostComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blog_post_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="ss"&gt;preview: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:preview&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to enable preview rendering when the preview parameter is passed.&lt;/p&gt;

&lt;p&gt;Head to &lt;a href="http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad?preview=true" rel="noopener noreferrer"&gt;http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad?preview=true&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and you will see the Draft version of your Blog Post. You can edit it on DatoCMS and see the new version until you publish it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live mode
&lt;/h2&gt;

&lt;p&gt;The second, interesting feature is live mode so you don't need to manually refresh your Browser window anymore. Similar to what we did before change your controller in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;BlogPostComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blog_post_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="ss"&gt;preview: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:preview&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;live: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:live&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to enable preview rendering when the preview parameter is passed.&lt;/p&gt;

&lt;p&gt;Head to &lt;a href="http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad?preview=true&amp;amp;live=true" rel="noopener noreferrer"&gt;http://localhost:3000/blog_posts/mistakes-tourists-make-on-their-first-trip-abroad?preview=true&amp;amp;live=true&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching
&lt;/h2&gt;

&lt;p&gt;This is a very important topic since we don't want to hit DatoCMS every time we render a Blog Post page. The &lt;code&gt;Dato::Wrapper&lt;/code&gt; component that you used previously will take care also of this and will cache the entire rendered component so that subsequent calls to the same page will be instant and will not require invoking DatoCMS again.&lt;/p&gt;

&lt;p&gt;There are cases where you might not be using &lt;code&gt;Dato::Wrapper&lt;/code&gt;, for such cases you can use the method &lt;code&gt;data = execute_dato_query(your_query)&lt;/code&gt; to cache your query results automatically or directly &lt;code&gt;Dato::Cache.fetch { ... }&lt;/code&gt; method similar to how you would use the Rails cache.&lt;/p&gt;

&lt;p&gt;You should then configure Dato to automatically expire the cache when you publish your changes. &lt;a href="https://github.com/renuo/dato-rails?tab=readme-ov-file#publish-endpoint" rel="noopener noreferrer"&gt;Read more about it the README&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>datocms</category>
      <category>rails</category>
    </item>
    <item>
      <title>The Full-Stack development experience</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Thu, 12 Oct 2023 12:48:13 +0000</pubDate>
      <link>https://dev.to/renuo/the-full-stack-development-experience-297n</link>
      <guid>https://dev.to/renuo/the-full-stack-development-experience-297n</guid>
      <description>&lt;p&gt;&lt;a href="https://rubyonrails.org/world" rel="noopener noreferrer"&gt;Rails World 2023&lt;/a&gt; closes many doors that have been opened recently and marks a turning point for Ruby On Rails. That is why this year's motto can be summarized as "&lt;a href="https://world.hey.com/dhh/the-one-person-framework-711e6318" rel="noopener noreferrer"&gt;The One Person Framework&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;The Web has certainly been a complex environment in recent years. This complexity was necessary to push browsers to the limit and use the most advanced features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But in 2023, this is no longer necessary!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Assets distribution
&lt;/h2&gt;

&lt;p&gt;Let's think about CSS. For years, we used SASS to take advantage of variables and nested rules. Well, that is no longer necessary. These are both features made available directly by CSS now. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffne6srd4azvap3dne1pg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffne6srd4azvap3dne1pg.png" alt="CSS variables availability on browsers" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgh9l3h7x5cthamjgizr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgh9l3h7x5cthamjgizr9.png" alt="CSS nesting availability on browser" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we can say goodbye to compiling SASS into CSS and enjoy the pleasure of writing directly in CSS. This makes us much faster in development and requires one less knowledge (SASS) for each Full-stack developer.&lt;/p&gt;

&lt;p&gt;A full-Stack developer is precisely what our profession provided for before the advent of powerful javascript frameworks for creating truly fluid web interfaces! However, &lt;strong&gt;it is time to simplify&lt;/strong&gt;! It is time to stop being Partial-stack developers and focus on productivity again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's talk about JavaScript&lt;/strong&gt;. The language has matured so much in the last few years, and it is (almost) a joy to write in JavaScript. The consolidation of import maps for the distribution of JavaScript and CSS assets finally allows us to remove another element from our stack: bundling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksjbq7sec2v2wobuztyo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksjbq7sec2v2wobuztyo.png" alt="Import maps availability on browsers" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In 2023, thanks to importmaps, providing the browser with dozens of separate JavaScript files instead of a single file makes no difference! No more bundling! It is not only unnecessary but even &lt;strong&gt;harmful&lt;/strong&gt;! If we modify just one of our files, we can serve the browser a new version of that one file instead of the entire bundle. &lt;strong&gt;Long live simplicity!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An additional element that we can finally remove from our stack is the minification of JavaScript and CSS files. Thanks to algorithms like &lt;a href="https://github.com/google/brotli" rel="noopener noreferrer"&gt;brotli&lt;/a&gt; (with a very Swiss flavour) we no longer need to minify and compress our files before distributing them. Cloudflare, Nginx, or Apache will take care of everything for us. &lt;/p&gt;

&lt;p&gt;And there goes our stack thinning, and with that, our Full-stack developers can focus again on productivity instead of technicalities about how to distribute their work to Browsers.&lt;/p&gt;

&lt;p&gt;Ruby On Rails, thanks to &lt;a href="https://github.com/rails/propshaft" rel="noopener noreferrer"&gt;propshaft&lt;/a&gt;, closes a chapter. Welcome to 2023, where deploying JavaScript and CSS is a breeze. Welcome to the &lt;a href="https://world.hey.com/dhh/you-can-t-get-faster-than-no-build-7a44131c" rel="noopener noreferrer"&gt;no-build&lt;/a&gt; era.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h0sfdfocgub8bwtjucv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h0sfdfocgub8bwtjucv.png" alt="DHH showing no-build speed comparison graph" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard drives are cool
&lt;/h2&gt;

&lt;p&gt;Another element that has radically changed in recent years is the speed of drives. With the advent of NVMe disks, the speed is ten times what we had ten years ago. That is why our stack can be further streamlined. To start with a new web application, we can use the disk for databases (SQLite), caches, WebSockets, and background jobs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsjg46yvf13tk3gnmicu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsjg46yvf13tk3gnmicu.png" alt="Speed of Hard Drives over the years from chatGPT so it must be true" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! It means you can have a web app with all the latest and most complex features...&lt;strong&gt;and zero dependencies&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;When your application is used by millions of users worldwide, you can think about further optimizations, a better-performing database, or gain performance with more efficient caching systems. Until then, productivity is the key word. No frills and no dependencies. You can eliminate SQL and NoSQL databases from your stack and focus on development.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML-First architecture
&lt;/h2&gt;

&lt;p&gt;Let's get to the last chapter. The most controversial one, the one most discussed. The &lt;a href="https://hotwired.dev/" rel="noopener noreferrer"&gt;Hotwire&lt;/a&gt; architecture: HTML Over The Wire. Ruby On Rails started down this path in 2015 with Turbolinks, but it was not until 2021, with the arrival of Turbo, that years of work was consolidated.&lt;/p&gt;

&lt;p&gt;Today, we can confidently say that HTML Over The Wire is the right technology for maximizing productivity. When we maximize productivity, this is always at the expense of performance. With this technology, we will never have the performance of a JavaScript-First architecture. But what is it all about?&lt;/p&gt;

&lt;p&gt;When discussing &lt;strong&gt;JavaScript-First Architecture&lt;/strong&gt;, I have in mind applications where the backend and frontend, properly separated, communicate via JSON. The frontend uses JavaScript, and the backend also, possibly. The server and client communicate via JSON. The server is "dumb", it provides data to the client, which, thanks to the Frontend Framework from screaming, "builds" the entire web interface.&lt;/p&gt;

&lt;p&gt;The client then has the entire responsibility of building web pages.&lt;/p&gt;

&lt;p&gt;A truly efficient architecture that takes full advantage of all the capacity of modern devices, which have no difficulty in taking on the "onerous task." An architecture that maximizes performance...at the expense of productivity, of course. Not coincidentally, the first frameworks in this universe, Angular and React, were deployed by Google and Facebook, which have their main focus on performance. And we could do with more!&lt;/p&gt;

&lt;p&gt;GitHub itself, which is based on Ruby On Rails, now adopts React &lt;a href="https://news.ycombinator.com/item?id=33576722" rel="noopener noreferrer"&gt;in many parts of their frontend&lt;/a&gt; to be able to offer the best experience to its customers.&lt;/p&gt;

&lt;p&gt;When I discuss &lt;strong&gt;HTML-First Architecture&lt;/strong&gt;, however, I have in mind the Web of twenty years ago. The server, "intelligent", no longer solely transmits data to clients but directly HTML pages.&lt;/p&gt;

&lt;p&gt;The client/server communication protocol is based on HTML, and the client goes back to being "stupid."&lt;/p&gt;

&lt;p&gt;What changes today is that the client and server can communicate via HTML but update individual page elements instead of simply navigating to the next one.&lt;/p&gt;

&lt;p&gt;This concept of partial updates allows us to rethink very complex pages into smaller elements (just as React taught us!) and update only the parts we care about.&lt;/p&gt;

&lt;p&gt;The implementation offered by Ruby On Rails is called Turbo, and it allows for SPA-like user interfaces and experiences without having to learn a new framework.&lt;/p&gt;


    
    Your browser does not support the video tag.


&lt;p&gt;This element allows a Full-Stack developer to build applications with a modern flavour within a &lt;a href="https://m.signalvnoise.com/the-majestic-monolith/" rel="noopener noreferrer"&gt;majestic monolith&lt;/a&gt;, while continuing to work only with HTML at all times. No more client/server separation and complex synchronization to achieve modern, fluid interfaces. Just one application, the stack of which can fit entirely in one person's head.&lt;/p&gt;

&lt;p&gt;When they tell you about Multiple Page Apps vs. Single Page Apps, you will know &lt;strong&gt;this comparison no longer makes sense&lt;/strong&gt;. There are JavaScript-First Frameworks that can safely create MPA's via Server-side rendering, and there are HTML-First Frameworks which can provide the same experience as an SPA.&lt;/p&gt;

&lt;p&gt;HTML-First vs Javacript-First architecture is the distinction that needs to be made today.&lt;/p&gt;

&lt;h2&gt;
  
  
  App Store, Android Store
&lt;/h2&gt;

&lt;p&gt;Having come to this point, to think that one person can manage the entire stack for Web application development &lt;strong&gt;is not only likely, it is reality!&lt;/strong&gt; One person can develop a feature from start to finish: from database design, background processes, business logic, to frontend design. The Web stack was complex, but we finally simplified it!&lt;/p&gt;

&lt;p&gt;Yes, a Full-Stack developer in 2023 has the tools to build a feature from start to finish, without waiting for his colleague to "expose" the API.&lt;/p&gt;

&lt;p&gt;But there is a world that has always been quite obscure to us web developers: native iOS and Android app development. Native apps do not allow us all the simplifications we discussed above. Native app development follows other rules, and here I throw up my hands and give up.&lt;/p&gt;

&lt;p&gt;But Ruby On Rails, since a couple of weeks ago, has released the missing piece of the stack for creating hybrid applications: &lt;a href="https://strada.hotwired.dev/" rel="noopener noreferrer"&gt;Strada&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to Turbo and Strada, even our Full-Stack developer can make something that will be approved within the App Store. A hybrid app allows you to start from your existing Web application and release it as a native App once it is properly "wrapped" in a WebView. Turbo-ios and turbo-android, together with Strada, make it very easy to add all the native elements needed to optimize the user experience.&lt;/p&gt;

&lt;p&gt;To date, dozens of apps are already made with this technology in the App Store, and I challenge anyone to distinguish them from a native app. Web View indeed means Browser, and Browsers are really powerful today! With good design and good animations, they are indistinguishable from fully native apps. And the beauty is that it is still your Web app. You don't need to expose APIs, and rebuild your iOS and Android interfaces. Just reuse your existing ones! &lt;strong&gt;This is an incredible boost to productivity!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;At the beginning of the article, I wrote that this is a turning point, but what's next? The next few years will be focused on this simplification of the stack and on improving these new tools at our disposal. Improving productivity is also done through developer tools. We need time to improve the whole ecosystem around these technologies. There are many new things, and it takes time to absorb and digest them.&lt;/p&gt;

&lt;p&gt;Ruby On Rails continues to point the way, often independently and controversially, and we, after our recent membership in the Rails Foundation, are intent on following it.&lt;/p&gt;

&lt;p&gt;Check the &lt;a href="https://www.youtube.com/watch?v=iqXjGiQ_D-A" rel="noopener noreferrer"&gt;Keynote of Rails World 2023&lt;/a&gt;, to hear from the creator of Ruby On Rails, the concepts I took inspiration from, to write this blog post.&lt;/p&gt;

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

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Rubyday 2023 - A great comeback!</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Sat, 17 Jun 2023 11:03:07 +0000</pubDate>
      <link>https://dev.to/coorasse/rubyday-2023-a-great-come-back-15am</link>
      <guid>https://dev.to/coorasse/rubyday-2023-a-great-come-back-15am</guid>
      <description>&lt;p&gt;The &lt;a href="https://2023.rubyday.it/" rel="noopener noreferrer"&gt;&lt;strong&gt;Rubyday 2023&lt;/strong&gt;&lt;/a&gt; has just concluded, and it was as amazing as I remembered it from 2019. Yes, sure, we had 2020 and 2021 online editions, but they don't really count to me.&lt;br&gt;
The conferences are grand, especially for everything that happens between the official talks.&lt;br&gt;
And Rubyday has something even more special: it may be the wonderful location, Verona, or perhaps it's the fact that you hear so many people speaking Italian, which gives a stronger sense of community and a desire to meet, or perhaps it's the small size, which allows you to approach so many people and spend many informal moments together.&lt;/p&gt;

&lt;p&gt;The organizers have succeeded in a very difficult feat: organizing a conference with a &lt;strong&gt;new flavor&lt;/strong&gt;, with many different insights, a great mix of technical and non-technical talks, speakers with various backgrounds from all over the world, but also, and above all, many who live and work in Italy, thus contributing to making this event unique and special.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;ruby community&lt;/strong&gt; once again proves to be very close-knit, and I can't wait to see many people again, and meet new ones, at the numerous conferences we will have in Europe this year.&lt;br&gt;
I go home with so many inputs and ideas to work on in the coming months.&lt;br&gt;
Let's see which ones:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paolo Perrotta&lt;/strong&gt; explained to us how chatGPT works. Paolo has an innate ability to explain complex technologies in simple words. There couldn't have been a better keynote in my opinion: Welcome to Rubyday! An Italian rubyist will explain the secrets behind one of the most fascinating technologies of recent years, and it doesn't matter if it's not strictly related to ruby, because the insights for our work are immense. As developers, how will we be able to take full advantage of this technology in the coming years? Will Cucumber become relevant again? Or will we just end up writing tickets on Jira? The topic was so interesting that the discussion continued even after dinner.&lt;/p&gt;

&lt;p&gt;Right after, here's a talk strictly related to ruby: &lt;strong&gt;Alessandro Rizzo&lt;/strong&gt; showed us how to implement custom cops in rubocop and how &lt;a href="https://www.treatwell.it/" rel="noopener noreferrer"&gt;Treatwell&lt;/a&gt; approaches the problem at the company level. Personally, it was really interesting to see that other companies, besides ours, &lt;a href="https://www.renuo.ch/" rel="noopener noreferrer"&gt;Renuo&lt;/a&gt;, follow the same approach. Treatwell has released rubocop-treatwell, while Renuo uses renuocop. Each company creates its own set of rules to "maximize the happiness" of its developers.&lt;/p&gt;

&lt;p&gt;And this connects to &lt;strong&gt;Bozhidar Batsov&lt;/strong&gt;'s talk, who with his sharp humor, made a beautiful journey through the latest versions of ruby and the many, new, features that have been released in the last four years, since 2019, which was the last Rubyday he spoke at.&lt;br&gt;
A talk with many personal perspectives and quite philosophical, that tries to understand and reason around the motto of ruby: a programming language optimized for programmer happiness. Ruby has a really varied syntax, and that's why each work group has the opportunity, thanks to Rubocop, to configure the rules to its own liking.&lt;br&gt;
A heartfelt thanks to Bug, who spends much of his time receiving insults on github 😀&lt;/p&gt;

&lt;p&gt;And then another non-technical but highly effective talk, on how to handle one-on-one, &lt;strong&gt;Giulia Mialich&lt;/strong&gt;, Engineering Manager at Nebulab, had a brilliant idea: to bring the point of view of a manager, to an audience predominantly of developers, on why one-on-one are organized in the company, why they are so important, and why they are completely in favor of the employees. The fact that a company does one-on-one should be considered a benefit. It's the most effective tool in the company to grow in your role, and an opportunity to have help and guidance in that sense. Take home message: &lt;strong&gt;being a manager means being a servant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The next talk is from &lt;strong&gt;Frederick Cheung&lt;/strong&gt;, who courageously brought many slides with Typescript code to a Rubyist audience 😂&lt;br&gt;
This says a lot about the confidence you must have on stage to do something like that! 😆&lt;br&gt;
We learned, if we didn't already know, JSON Schema and how to use it to reduce the possibility of errors when implementing our APIs and communicating with them from our frontend. JSON Schema can be used to test and validate what is returned from our Ruby-written APIs, but at the same time allows us to automatically generate Type Definitions in Typescript. In this way, if our APIs change, we will have errors already at compile-time on our frontend. 🤯&lt;br&gt;
I can't wait to use this strategy in the future to have even more stable APIs and even safer integration with the frontend.&lt;/p&gt;

&lt;p&gt;A brief lightning talk from &lt;strong&gt;Sergey Sergyenko&lt;/strong&gt; to invite us to Euruko 2023 and then another technical talk from &lt;strong&gt;Ju Liu&lt;/strong&gt;. His talks are always very fun and he's one of the few I know who still has the courage to do live-coding at a conference. His talk made us think and remember that the functional approach to programming exists and is often a much more elegant and readable solution than the imperative approach.&lt;br&gt;
Take home message: &lt;strong&gt;functional programming is about structure transformation&lt;/strong&gt;. When you program in a functional way, you have to think in terms of "how do I need to transform my data structure in order to achieve my goal?".&lt;br&gt;
Thank you Ju!&lt;/p&gt;

&lt;p&gt;And if the conference was not technical enough until now, here's another interesting talk from &lt;strong&gt;Ridhwana Khan&lt;/strong&gt; on the caching techniques adopted by dev.to, the same platform on which you are reading this article. Did this article load instantly? Well, it's largely thanks to the work done by Ridhwana and other developers at dev.to. The techniques they use are truly cutting-edge and it's wonderful to see the myth that Rails=slow debunked once again. I love having insights on globally reaching software with millions of users. It's really interesting to see how the challenges I face every day can have a much wider scale and how Rails offers all the tools to do it. It's also very interesting to have small sneak peaks from the Network Inspector to understand how Response Headers are used to manage the cache at the browser level.&lt;/p&gt;

&lt;p&gt;And to conclude, &lt;strong&gt;Hana Harencarova&lt;/strong&gt;, with her talk, finally gave many useful tips on how to onboard Junior figures within a company, what our role as senior developers and CTOs is. It was wonderful to see how this talk met the needs of many companies present at the conference, looking for Ruby On Rails developers. I heard more than one person leave saying "maybe we could revise that job offer for Senior Developer, and open up to the possibility of Junior figures". With the advice that Hana gave us during this talk, hiring Juniors is no longer so scary, and also has the advantage of allowing our community to grow further.&lt;/p&gt;

&lt;p&gt;Finally, grazie a Monica Giambitto, who it was really nice to see again after so many years and to the entire &lt;a href="https://www.grusp.org/en/" rel="noopener noreferrer"&gt;Grusp&lt;/a&gt; staff 👏. Thanks also to &lt;a href="https://www.renuo.ch/" rel="noopener noreferrer"&gt;Renuo&lt;/a&gt; for giving me the opportunity to attend.&lt;/p&gt;

&lt;p&gt;See you at the next conference!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Preloading associations on an Array of Objects</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Wed, 04 Jan 2023 14:43:49 +0000</pubDate>
      <link>https://dev.to/renuo/preloading-associations-on-an-array-of-objects-4d81</link>
      <guid>https://dev.to/renuo/preloading-associations-on-an-array-of-objects-4d81</guid>
      <description>&lt;p&gt;It might happen that your initial array of objects is not an ActiveRecord Relation.&lt;br&gt;
You cannot use &lt;code&gt;preload&lt;/code&gt; on an array of objects and therefore you suffer of N+1 queries.&lt;/p&gt;

&lt;p&gt;Imagine we have a Restaurants list. The Restaurants are fetched from a remote Service and therefore we have a simple array of objects.&lt;/p&gt;

&lt;p&gt;In our system we have &lt;code&gt;Customer&lt;/code&gt;s and they reference the &lt;code&gt;Restaurant&lt;/code&gt; by &lt;code&gt;external_restaurant_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the setup. There might be a lot of reasons why you cannot cache the restaurants locally of course.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Restaurant&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:code&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the partial that renders our restaurants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;restaurants.each&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;restaurant&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h5&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;restaurant.name&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h5&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;Customers list&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;Customer.where&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;external_restaurant_code:&lt;/span&gt; &lt;span class="na"&gt;restaurant.code&lt;/span&gt;&lt;span class="err"&gt;).&lt;/span&gt;&lt;span class="na"&gt;each&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;customer.name&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This causes clearly N+1 queries (more correctly just N, because there's no +1, since the restaurants are fetched remotely)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer Load (0.1ms)  SELECT "customers".* FROM "customers" WHERE "customers"."external_restaurant_code" = ?  [["external_restaurant_code", 1]]
  ↳ app/views/restaurants/index.html.erb:7
Customer Load (0.0ms)  SELECT "customers".* FROM "customers" WHERE "customers"."external_restaurant_code" = ?  [["external_restaurant_code", 2]]
  ↳ app/views/restaurants/index.html.erb:7
Customer Load (0.0ms)  SELECT "customers".* FROM "customers" WHERE "customers"."external_restaurant_code" = ?  [["external_restaurant_code", 3]]
  ↳ app/views/restaurants/index.html.erb:7
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How can we avoid performing multiple queries? The Restaurant is not an ActiveRecord model, therefore we cannot do something like &lt;code&gt;Restaurant.includes(:customers).each&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our first approach was to just reimplement the preloading and do the following in the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@restaurants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Restaurant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_api&lt;/span&gt;
&lt;span class="vi"&gt;@customers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;external_restaurant_code: &lt;/span&gt;&lt;span class="vi"&gt;@restaurants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:code&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:external_restaurant_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This solves already our problem and removes the repeated queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer Load (0.7ms)  SELECT "customers".* FROM "customers" WHERE "customers"."external_restaurant_code" IN (?, ?, ?)  [["external_restaurant_code", 1], ["external_restaurant_code", 2], ["external_restaurant_code", 3]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the code smells quite a bit: we are basically re-implementing eager loading ourselves and we are moving in the controller a lot of business logic. The example is rather easy, but it might get much more complex very fast.&lt;/p&gt;

&lt;p&gt;Our solution consists of making the Restaurant an ActiveRecord object. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WrappedRestaurant&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:restaurant&lt;/span&gt;

  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:customers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;foreign_key: :external_restaurant_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;primary_key: :code&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;restaurant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@restaurant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;restaurant&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;code: &lt;/span&gt;&lt;span class="n"&gt;restaurant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;readonly?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@restaurants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Restaurant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;WrappedRestaurant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and render our view like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;restaurants.each&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;restaurant&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;restaurant.name&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h5&amp;gt;&lt;/span&gt;Customers list&lt;span class="nt"&gt;&amp;lt;/h5&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;restaurant.customers.each&lt;/span&gt; &lt;span class="na"&gt;do&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%=&lt;/span&gt; &lt;span class="na"&gt;customer.name&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is much more Rails-alike, but we are back to the initial issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer Load (0.1ms)  SELECT "customers".* FROM "customers" WHERE "customers"."external_restaurant_code" = ?  [["external_restaurant_code", 1]]
  ↳ app/views/restaurants/index.html.erb:7
Customer Load (0.0ms)  SELECT "customers".* FROM "customers" WHERE "customers"."external_restaurant_code" = ?  [["external_restaurant_code", 2]]
  ↳ app/views/restaurants/index.html.erb:7
Customer Load (0.0ms)  SELECT "customers".* FROM "customers" WHERE "customers"."external_restaurant_code" = ?  [["external_restaurant_code", 3]]
  ↳ app/views/restaurants/index.html.erb:7
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final step is to use the Rails preloader. This luckily accepts an array, so we can write the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Associations&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Preloader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;records: &lt;/span&gt;&lt;span class="vi"&gt;@restaurants&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;associations: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:customers&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and our &lt;code&gt;customers&lt;/code&gt; association will be preloaded. 🪄&lt;/p&gt;

&lt;p&gt;We like to wrap-up everything in a nice method in the wrapper class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# wrapped_restaurant.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;restaurants&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;preload: &lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="n"&gt;restaurants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;restaurant&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;WrappedRestaurant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;restaurant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;tap&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;wrapped_restaurants&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Associations&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Preloader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;records: &lt;/span&gt;&lt;span class="n"&gt;wrapped_restaurants&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;associations: &lt;/span&gt;&lt;span class="n"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now preload associations on your Restaurant and keep the logic within the WrappedRestaurant model. The only cons is that you need a "fake" table on the database, nothing that a good old comment cannot solve 😉.&lt;/p&gt;

&lt;p&gt;Find the whole code at &lt;a href="https://github.com/coorasse/array_preloading_example" rel="noopener noreferrer"&gt;https://github.com/coorasse/array_preloading_example&lt;/a&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>DatoCMS with Ruby on Rails ️</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Mon, 04 Apr 2022 19:42:50 +0000</pubDate>
      <link>https://dev.to/renuo/datocms-with-ruby-on-rails-3ae5</link>
      <guid>https://dev.to/renuo/datocms-with-ruby-on-rails-3ae5</guid>
      <description>&lt;p&gt;At &lt;a href="https://renuo.ch" rel="noopener noreferrer"&gt;Renuo&lt;/a&gt; we often need an HeadlessCMS in our Ruby On Rails projects, and in the last years, we had a great experience using &lt;a href="https://www.datocms.com/" rel="noopener noreferrer"&gt;Dato CMS&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;So we decided to release our &lt;a href="https://github.com/renuo/dato-rails" rel="noopener noreferrer"&gt;DatoCMS client open source&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We extracted and isolated the features that we considered most useful and this tutorial will explain you, step-by-step, how to start using it.&lt;/p&gt;

&lt;h1&gt;
  
  
  DatoCMS setup
&lt;/h1&gt;

&lt;p&gt;We provide a template for a dato backend that you can use for this tutorial:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dashboard.datocms.com/clone?projectId=57262&amp;amp;name=dato-rails" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdashboard.datocms.com%2Fclone%2Fbutton.svg" alt="Clone DatoCMS project" width="208" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the button above to setup a first DatoCMS project, proceed to the Settings-&amp;gt;API Tokens and fetch your ReadOnly API token that we'll use afterwards.&lt;/p&gt;

&lt;h1&gt;
  
  
  Project setup
&lt;/h1&gt;

&lt;p&gt;The gem expects you to have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Rails app 😄 &lt;/li&gt;
&lt;li&gt;&lt;code&gt;turbo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ViewComponents&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only Rails is an actual requirement, but to use 100% of the features provided by the gem, you'll need also &lt;code&gt;view_components&lt;/code&gt; and &lt;code&gt;turbo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Given that you are using Rails &amp;gt;= 7, you can follow these commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails new dato-rails-demo &lt;span class="nt"&gt;--skip-active-record&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;dato-rails-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to your Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'dotenv-rails'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;groups: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:development&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'view_component'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'dato-rails'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;require: &lt;/span&gt;&lt;span class="s1"&gt;'dato'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then run again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;install
echo&lt;/span&gt; &lt;span class="s2"&gt;"DATO_API_TOKEN=&amp;lt;YOUR_DATO_API_TOKEN&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to install the new libraries and set your Api Token.&lt;/p&gt;

&lt;p&gt;Well done. You should be able to simply run &lt;code&gt;bin/rails s&lt;/code&gt; and access your application.&lt;/p&gt;

&lt;h1&gt;
  
  
  Your first query
&lt;/h1&gt;

&lt;p&gt;The provided project contains Homepage. We'll try to fetch it. DatoCMS provides a GraphQL API Explorer that is very useful to generate queries. &lt;/p&gt;

&lt;p&gt;We'll start simple with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/dato/queries.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Dato&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Queries&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;homepage_query&lt;/span&gt;
      &lt;span class="no"&gt;GQLi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nb"&gt;id&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where we define a query to fetch the homepage id.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need to restart your server here, since we added a new services folder.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We should always have tests for our queries. Such a test looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/services/dato/queries_test.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatoQueriesTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Queries&lt;/span&gt;

  &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'homepage_query'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homepage_query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;assert_not_nil&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have a green test, it means your are already successfully fetching your homepage from DatoCMS. &lt;/p&gt;

&lt;h1&gt;
  
  
  Render fetched data
&lt;/h1&gt;

&lt;p&gt;We can now generate a controller and a component and we'll render the data that we just fetched.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g controller HomepageController
rails g component Homepage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A component is not mandatory, you can also use standard partials, but it will be necessary to benefit of more advanced features like live reloading afterwards.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/homepage_component.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomepageComponent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ViewComponent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="vi"&gt;@data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/components/homepage_component.html.erb

&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;This is the homepage id: &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/homepage_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomepageController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Queries&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homepage_query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;HomepageComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/routes.rb&lt;/span&gt;

&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="s2"&gt;"homepage#show"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Structured Text
&lt;/h1&gt;

&lt;p&gt;One of the most important fields that you are going to use on DatoCMS is the &lt;a href="https://www.datocms.com/blog/introducing-structured-text" rel="noopener noreferrer"&gt;Structured Text&lt;/a&gt;.&lt;br&gt;
You can easily fetch and render it using &lt;code&gt;dato-rails&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Update your homepage query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;homepage_query&lt;/span&gt;
  &lt;span class="no"&gt;GQLi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;id&lt;/span&gt;
      &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="n"&gt;blocks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;__typename&lt;/span&gt;
          &lt;span class="nb"&gt;id&lt;/span&gt;
          &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;responsiveImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;imgixParams: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;fm: &lt;/span&gt;&lt;span class="n"&gt;__enum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"png"&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;___&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Fragments&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResponsiveImage&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the code above fetches also the &lt;code&gt;content&lt;/code&gt; field that we use in the homepage and we pre-filled in the example.&lt;/p&gt;

&lt;p&gt;Here is how you render a Structured Text field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;This is the homepage id: &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt;&lt;span class="vi"&gt;@data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StructuredText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library provides you a component that you can use to render already everything. You will now see the following:&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Custom blocks
&lt;/h1&gt;

&lt;p&gt;In the example above, you see that a block was defined in the blocks library of the example. Such blocks are created by you, so the library cannot provide a component to render them, but you need to create one yourself, with the information available. Read the output of the page for more information and instructions on how to define your component.&lt;/p&gt;

&lt;p&gt;We will have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/components/dato/image_block_record.html.erb

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResponsiveImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;responsiveImage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/dato/image_block_record.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Dato&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageBlockRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Node&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can now simply reload the page, and your block will be rendered correctly with a &lt;a href="https://www.datocms.com/blog/best-way-for-handling-react-images" rel="noopener noreferrer"&gt;responsive image&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Customizations
&lt;/h1&gt;

&lt;p&gt;Let's start by adding simple.css to our &lt;code&gt;application.html.erb&lt;/code&gt; header so that it looks better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.simplecss.org/simple.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to override how a Structured Text node is rendered you have two possibilities:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSS only&lt;/strong&gt;&lt;br&gt;
The nodes contain HTML classes that you can use to customize the look and feel of each node by working only with CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.dato-cms-paragraph&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will add 30px margin top to all the paragraphs nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Override the component&lt;/strong&gt;&lt;br&gt;
We cannot unfortunately override &lt;a href="https://github.com/github/view_component/issues/411" rel="noopener noreferrer"&gt;only the template&lt;/a&gt; at the moment. So if you want to customize your node further, you'll need to do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define a new component. Let's define a new component for the cite block
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/dato/fancy_cite.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dato::FancyCite&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DastNode&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"blockquote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/components/dato/fancy_cite.html.erb
&lt;span class="nt"&gt;&amp;lt;blockquote&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;cite&amp;gt;&lt;/span&gt;– &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attribution&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/cite&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/blockquote&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/dato.rb&lt;/span&gt;
&lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;overrides&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;blockquote: &lt;/span&gt;&lt;span class="s1"&gt;'Dato::FancyCite'&lt;/span&gt;
&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;with_indifferent_access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Since we added an initializer, restart the server.&lt;br&gt;
This is how you override completely a node.&lt;/p&gt;
&lt;h1&gt;
  
  
  Preview mode
&lt;/h1&gt;

&lt;p&gt;When using the &lt;a href="https://www.datocms.com/docs/general-concepts/draft-published" rel="noopener noreferrer"&gt;draft/published feature of Dato CMS&lt;/a&gt;, you can fetch the draft version of your model when using the client. We will change the controller as follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;preview: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:preview&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and enable Draft/Preview for our Homepage model.&lt;/p&gt;

&lt;p&gt;Proceed to the Settings of the project, and then models, select the Homepage model, and click Edit Model.&lt;/p&gt;

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

&lt;p&gt;Now you can check how the page looks like when passing the &lt;code&gt;preview&lt;/code&gt; parameter in the URL and you perform changes on the Homepage. Head to &lt;a href="http://localhost:3000?preview=true" rel="noopener noreferrer"&gt;http://localhost:3000?preview=true&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Real Time Mode
&lt;/h1&gt;

&lt;p&gt;Working on DatoCMS to edit your homepage and going back and forth to your website and hit "refresh" every time, might be tedious and time consuming. Real Time Mode is one of the most interesting features of Dato and you can benefit from it also when using Rails.&lt;/p&gt;

&lt;p&gt;The only thing you need to do is to wrap your component into a &lt;code&gt;Dato::Live&lt;/code&gt; component and mount the routes. &lt;/p&gt;

&lt;p&gt;We'll change the controller action as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Live&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;HomepageComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                        &lt;span class="n"&gt;homepage_query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                        &lt;span class="ss"&gt;preview: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:preview&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="ss"&gt;live: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:live&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And mount the engine routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/routes.rb&lt;/span&gt;

&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="s2"&gt;"homepage#show"&lt;/span&gt;

  &lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;Dato&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/dato'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head to your page and add the parameters &lt;code&gt;?live=true&amp;amp;preview=true&lt;/code&gt; to the URL and enjoy live reloading.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmux698un6zb54119g8n2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmux698un6zb54119g8n2.gif" alt="Image description" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>cms</category>
    </item>
    <item>
      <title>Rails 7, Bootstrap 5 and importmaps without nodejs</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Wed, 12 Jan 2022 20:01:48 +0000</pubDate>
      <link>https://dev.to/renuo/rails-7-bootstrap-5-and-importmaps-without-nodejs-4g8</link>
      <guid>https://dev.to/renuo/rails-7-bootstrap-5-and-importmaps-without-nodejs-4g8</guid>
      <description>&lt;h2&gt;
  
  
  Our goal: remove nodejs
&lt;/h2&gt;

&lt;p&gt;As many other people in the Rails community, I started setting up brand new Rails 7 projects, and I need to re-learn, at least partially, how to bundle the assets and distribute them.&lt;/p&gt;

&lt;p&gt;I never fell in love with TailwindCSS, and therefore I usually setup my Rails apps to use Bootstrap as default.&lt;/p&gt;

&lt;p&gt;But what I really like about Rails 7, is the idea of being able to get rid of not only webpack, but of nodejs entirely. The new importmaps feature is really appealing to me and I'd like to use it as long as I don't need to bundle my javascript.&lt;/p&gt;

&lt;p&gt;I have to say that &lt;code&gt;esbuild&lt;/code&gt; does already a pretty cool job compared to &lt;code&gt;webpack&lt;/code&gt; to simplify our lives, and make the process faster, but as long as I don't need bundling, I'd like to not have a package.json file and being dependent on nodejs for my Rails app. &lt;/p&gt;

&lt;p&gt;A pure and simple sprockets + importmaps app with no Foreman, no &lt;code&gt;bin/dev&lt;/code&gt;, no &lt;code&gt;yarn build --watch&lt;/code&gt; stuff.&lt;/p&gt;

&lt;p&gt;Bootstrap is made of two parts: CSS and javascript. So I want to use importmaps for the javascript part and rely on sprockets for the CSS compilation from SCSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails default
&lt;/h2&gt;

&lt;p&gt;By default, Rails 7 provides a new option &lt;code&gt;--css=bootstrap&lt;/code&gt;, &lt;br&gt;
but with my great surprise, this option adds both &lt;code&gt;jsbundling-rails&lt;/code&gt;, &lt;code&gt;cssbundling-rails&lt;/code&gt;, a &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;esbuild&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not as expected. Not what I want.&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  How to configure Rails and Bootstrap without nodejs
&lt;/h1&gt;

&lt;p&gt;Default is not what I want, but I can still reach the goal and here I'll explain how:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stick with just &lt;code&gt;rails new myapp&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This will setup exactly the tools I want: &lt;code&gt;sprockets&lt;/code&gt; and &lt;code&gt;importmaps&lt;/code&gt;. It will also setup automatically for me stimulus and turbo, which is great because I use them most of the time anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add &lt;code&gt;bootstrap&lt;/code&gt; gem&lt;/strong&gt; and enable the gem &lt;code&gt;sassc-rails&lt;/code&gt; in the Gemfile. This will allow us to compile bootstrap from SCSS without node.&lt;/p&gt;

&lt;p&gt;You can simply import Bootstrap styles in &lt;code&gt;app/assets/stylesheets/application.scss&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// here your custom bootstrap variables...&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it for the CSS part. Running &lt;code&gt;rails assets:precompile&lt;/code&gt; will generate what you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For the javascript part&lt;/strong&gt; we need to do three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precompile the bootstrap.min.js that comes with the gem, by adding to &lt;code&gt;config/initializers/assets.rb&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;precompile&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sx"&gt;%w( bootstrap.min.js popper.js )&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;pin the compiled asset in &lt;code&gt;config/importmap.rb&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="s2"&gt;"popper"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'popper.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;preload: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="s2"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'bootstrap.min.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;preload: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Include bootstrap in your &lt;code&gt;app/javascript/application.js&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;popper&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bootstrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefer this approach rather than pinning a CDN because we avoid diverging versions of Bootstrap.&lt;/p&gt;

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

&lt;p&gt;This is all you need to have Bootstrap fully working on Rails 7 without using node. &lt;/p&gt;

&lt;p&gt;If you like this guide you can &lt;a href="https://twitter.com/coorasse" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>turbo</category>
      <category>bootstrap</category>
    </item>
    <item>
      <title>Mugshot Bot Acquisition: our side of the story</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Wed, 12 Jan 2022 12:56:29 +0000</pubDate>
      <link>https://dev.to/coorasse/mugshot-bot-acquisition-our-side-of-the-story-57</link>
      <guid>https://dev.to/coorasse/mugshot-bot-acquisition-our-side-of-the-story-57</guid>
      <description>&lt;p&gt;What an amazing story &lt;a href="https://masilotti.com/idea-to-sold-in-14-months/" rel="noopener noreferrer"&gt;the one from Joe&lt;/a&gt; where he tells us how he built, scaled, and sold &lt;a href="https://mugshotbot.com" rel="noopener noreferrer"&gt;MugShotBot&lt;/a&gt; in 14 months!&lt;/p&gt;

&lt;p&gt;Well, I thought it might be interesting as well how it happened that we decided to buy it :)&lt;/p&gt;

&lt;p&gt;So here is our side of the story.&lt;/p&gt;

&lt;h2&gt;
  
  
  It all starts with Rails
&lt;/h2&gt;

&lt;p&gt;I have worked on &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Ruby On Rails&lt;/a&gt; for 10 years now, and I also became the maintainer of one of the most historical gems, &lt;a href="https://github.com/CanCanCommunity/cancancan" rel="noopener noreferrer"&gt;CanCanCan&lt;/a&gt;. Ruby On Rails was my bet ten years ago, when I quit my previous job, to switch from Java to Ruby, and to move from Italy to Switzerland at the same time.&lt;/p&gt;

&lt;p&gt;Here in Switzerland 🇨🇭 I started working at &lt;a href="https://renuo.ch/" rel="noopener noreferrer"&gt;Renuo&lt;/a&gt;, exactly because it was a Web Agency and it was working mainly with Ruby On Rails.&lt;/p&gt;

&lt;p&gt;A few years later I bought shares of the company and became one of the three shareholders.&lt;/p&gt;

&lt;p&gt;At Renuo we have worked with Rails for &lt;a href="https://www.renuo.ch/en/10years" rel="noopener noreferrer"&gt;10 years now&lt;/a&gt;, it's our main framework to bring our customers' ideas to life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Joe
&lt;/h2&gt;

&lt;p&gt;And along this journey, I of course started following the &lt;a href="https://www.getrevue.co/profile/hotwire" rel="noopener noreferrer"&gt;Hotwired Dev Newsletter&lt;/a&gt; from Joe Masilotti and started following &lt;a href="https://twitter.com/joemasilotti" rel="noopener noreferrer"&gt;him on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a big fan of Hotwire, I also started creating the &lt;a href="https://turbo-showcase.herokuapp.com/" rel="noopener noreferrer"&gt;Turbo Showcase&lt;/a&gt;, where we train ourselves on Turbo and collect all possible components and ideas (by the way, it is open-source, so contributions are welcome!)&lt;/p&gt;

&lt;p&gt;I already knew MugShotBot and when Joe &lt;a href="https://twitter.com/joemasilotti/status/1460706255874318338" rel="noopener noreferrer"&gt;decided to sell it&lt;/a&gt;, I immediately contacted him to ask a few questions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9os0s1qo86fgf1hkadm.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9os0s1qo86fgf1hkadm.jpeg" alt="Tweet from Joe, selling Mugshotbot" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick decision
&lt;/h2&gt;

&lt;p&gt;The nice thing about being a 20 people company with just three partners, is that you can make decisions very fast. I wrote to my partners right away and organised a meeting a few days later to discuss if we wanted to acquire MugShotBot. I sent them all the necessary information: estimated number of customers, monthly revenues, estimated price on Microacquire, technology stack. Each one of us had to answer the following questions (copy-pasted from our internal slack): &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;each one of us looks into it and makes a list of possible questions&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;each one of us also estimates “if I’d buy, I much would I pay for it”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;we sit together 1h and compare what we have and get back to him with questions, offers, or a no&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few days later we had the meeting, which lasted way less than one hour. Main points that lead to the decision to make Joe an offer were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s a lovely tool and a smart idea&lt;/li&gt;
&lt;li&gt;We can perfectly sell this to our existing Web Agency customers. Many of them will benefit from it&lt;/li&gt;
&lt;li&gt;It’s in Ruby On Rails, so we can easily maintain it and improve it. The company costs are at zero for the acquisition of a Ruby On Rails platform&lt;/li&gt;
&lt;li&gt;We never acquired a SaaS before so this is going to be a very good chance to learn something&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s make him an offer!&lt;/p&gt;

&lt;h2&gt;
  
  
  The offer
&lt;/h2&gt;

&lt;p&gt;I prepared a single-page offer for Joe, that was following the “no bullshit” principle that usually drives us at our daily work at Renuo. It consisted of the following parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What we offer&lt;/strong&gt;: the price. Right up here. First information. The most important.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why we want to buy&lt;/strong&gt;: we know that selling a product is like saying goodbye to your child, so it’s important for Joe to know why we want to acquire the platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What’s our plan&lt;/strong&gt;: same as above, explain to him our plans&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who are we&lt;/strong&gt;: if all the above look good, here is who we are, and what we do&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also highlighted some points that I felt could have been important, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;10% of our earnings goes to all employees&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;10% of our earnings goes to charities and open source&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;We work and invest daily in Ruby On Rails and Open Source and sponsor different projects like cancancan or rubocop&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The sale
&lt;/h2&gt;

&lt;p&gt;Joe accepted our offer! It was an amazing day, on the same day I acquired MugShotBot my second child, Pietro, was born! What a day! 🤣&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzltm5znc5fbkkrisw8k1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzltm5znc5fbkkrisw8k1.png" alt="Me and Pietro" width="584" height="782"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a few days, we had a one-page contract ready, performed the payment to Joe, and started transferring ownership.&lt;/p&gt;

&lt;p&gt;The process was very smooth, Joe was very professional and willing to help us in the process. The whole coordination happened through a single Google Drive document and a few Github issues.&lt;/p&gt;

&lt;p&gt;The migration happened in just a few days, and MugShotBot had a new maintainer!&lt;/p&gt;

&lt;h2&gt;
  
  
  The future of MugShotBot
&lt;/h2&gt;

&lt;p&gt;What will happen now? We started with very simple changes on the platform, where we updated references, email addresses, and logos to point to Renuo. &lt;/p&gt;

&lt;p&gt;During these days we are also taking care of updating ruby, rails, and all dependencies, to their latest version. The project was already in very good shape, so this won’t take us long. &lt;/p&gt;

&lt;p&gt;We plan to also replace turbolinks with turbo, and webpacker with esbuild. But this is all about the technical part. &lt;/p&gt;

&lt;p&gt;We already started reaching out to our existing customers to show them the tool and make proposals about how they can use it. We plan to expand beyond blogs and aim to a wider variety of customers. Some examples?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E-commerce&lt;/strong&gt;: we believe that an e-commerce platform will benefit a lot from being able to share rich previews of their products, including prices and discounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Online courses platforms&lt;/strong&gt;: the customer likes the idea that when a course is shared, there is a full title, a small description, and the school logo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auction Houses&lt;/strong&gt;: and what about sharing an item in an auction, with a nice picture and the start bidding price and the actual offer?&lt;/p&gt;

&lt;p&gt;Escape Rooms, textures, second-hand shops, newspapers, and plugins for existing platforms! We have so many ideas! Let’s see where this will bring us.&lt;/p&gt;

&lt;p&gt;If you are interested, have ideas or know anyone who would be interested in the upcoming changes and would like to test it out reach us at &lt;a href="//mailto:info@mugshotbot.com"&gt;info@mugshotbot.com&lt;/a&gt; or over Twitter &lt;a href="https://twitter.com/themugshotbot" rel="noopener noreferrer"&gt;@TheMugShotBot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Aaaand...to celebrate the acquisition, and since you read until here, we are offering the first month for free to new subscribers until the end of January! 🎉 Use the discount code &lt;code&gt;JANUARY2022ONEOFF&lt;/code&gt; during the checkout.&lt;/p&gt;

</description>
      <category>mugshotbot</category>
      <category>rails</category>
      <category>ruby</category>
      <category>saas</category>
    </item>
    <item>
      <title>The Turbo Showcase 🚀</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Mon, 08 Mar 2021 08:46:51 +0000</pubDate>
      <link>https://dev.to/renuo/the-turbo-showcase-4e7h</link>
      <guid>https://dev.to/renuo/the-turbo-showcase-4e7h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykxpvvkp9s93okn8yxwq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykxpvvkp9s93okn8yxwq.gif" alt="Alt Text" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was getting a bit lost in the documentation of &lt;a href="https://hotwire.dev/" rel="noopener noreferrer"&gt;Turbo&lt;/a&gt;, so I thought "Why not creating a showcase of what you can actually do with it?"&lt;/p&gt;

&lt;p&gt;So here it is: &lt;a href="https://turbo-showcase.herokuapp.com/" rel="noopener noreferrer"&gt;https://turbo-showcase.herokuapp.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently, we have some examples of Turbo Drive, Turbo Frame and Turbo Stream, and a first combination of what you can achieve with ViewComponent + Turbo + Stimulus.&lt;/p&gt;

&lt;p&gt;The Application is fully inspectable and you can enjoy the beautiful Source Code directly in the Browser or on the &lt;a href="https://github.com/renuo/turbo-showcase" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please feel free to contribute or make requests! At &lt;a href="https://renuo.ch" rel="noopener noreferrer"&gt;Renuo&lt;/a&gt; we will keep adding more and more examples while exploring the possibilities of this new Framework.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>turbo</category>
      <category>stimulus</category>
    </item>
    <item>
      <title>Log API requests in Rails</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Wed, 24 Feb 2021 09:39:19 +0000</pubDate>
      <link>https://dev.to/renuo/log-api-requests-in-rails-4di</link>
      <guid>https://dev.to/renuo/log-api-requests-in-rails-4di</guid>
      <description>&lt;p&gt;When exposing or consuming APIs, one of the most important things to remember is to log the calls you perform.&lt;/p&gt;

&lt;p&gt;It will not take long until you will have to answer the question "What happened? And why?".&lt;/p&gt;

&lt;p&gt;Having a proper log of the API requests you performed or received is the only way to answer these questions.&lt;/p&gt;

&lt;p&gt;That's why at &lt;a href="https://renuo.ch" rel="noopener noreferrer"&gt;Renuo&lt;/a&gt;, we extracted &lt;a href="https://github.com/renuo/rails_api_logger" rel="noopener noreferrer"&gt;rails_api_logger&lt;/a&gt; from our projects and made it open source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;rails_api_logger&lt;/strong&gt; is a library that can be used for logging both inbound requests and outbound requests.&lt;/p&gt;

&lt;p&gt;Please read the README file for a full documentation. Here I will sum it up quickly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Log Inbound Requests
&lt;/h1&gt;

&lt;p&gt;If you are exposing APIs you can log the requests you receive. Use the following Middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_before&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;InboundRequestLoggerMiddleware&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Check the &lt;a href="https://github.com/renuo/rails_api_logger" rel="noopener noreferrer"&gt;README&lt;/a&gt; for further options.&lt;/p&gt;

&lt;h1&gt;
  
  
  Log Outbound Requests
&lt;/h1&gt;

&lt;p&gt;Given the following outbound request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://example.com/some_path?query=string'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can log it by doing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://example.com/some_path?query=string'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RailsApiLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gem comes with a first code example to integrate these logs in RailsAdmin. It the future we will release more and more features.&lt;/p&gt;

&lt;p&gt;Head to &lt;a href="https://github.com/renuo/rails_api_logger" rel="noopener noreferrer"&gt;rails_api_logger&lt;/a&gt; on GitHub and give it a ⭐️!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>api</category>
    </item>
    <item>
      <title>From Turbolinks to Turbo</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Thu, 24 Dec 2020 17:32:22 +0000</pubDate>
      <link>https://dev.to/coorasse/from-turbolinks-to-turbo-31jl</link>
      <guid>https://dev.to/coorasse/from-turbolinks-to-turbo-31jl</guid>
      <description>&lt;p&gt;I was eager to start using Hotwire in a project that I thought was a perfect fit. Here I will show you what I had to do, to migrate from Turbolinks to Turbo, and keep using Webpacker as main assets bundler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove turbolinks. Add turbo.
&lt;/h2&gt;

&lt;p&gt;Turbo is basically the new version of Turbolinks, so we need to remove the old npm package and add the new one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn remove turbolinks
yarn add @hotwired/turbo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will replace the package with the new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turbo Drive
&lt;/h2&gt;

&lt;p&gt;Turbo Drive is that part of Turbo that replaces the functionalities provided before by Turbolinks.&lt;/p&gt;

&lt;p&gt;We need to remove the previous initialization of turbolinks and initialize turbo instead.&lt;br&gt;
In your &lt;code&gt;application.js&lt;/code&gt; (or wherever you initialized Turbolinks), replace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;turbolinks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
 with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Turbo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hotwired/turbo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, you will already have Turbo up and running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replace old Turbolinks code.
&lt;/h2&gt;

&lt;p&gt;The event &lt;code&gt;turbolinks:load&lt;/code&gt; is now called &lt;code&gt;turbo:load&lt;/code&gt;. Replace them wherever you are using them.&lt;/p&gt;

&lt;p&gt;If you were using &lt;code&gt;data-turbolinks&lt;/code&gt; attributes, they are now called &lt;code&gt;data-turbo&lt;/code&gt;. You can replace them as well.&lt;/p&gt;

&lt;p&gt;Basically you can search for &lt;code&gt;turbolinks&lt;/code&gt; within your project and replace it with &lt;code&gt;turbo&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it
&lt;/h2&gt;

&lt;p&gt;I started writing this post thinking "This is going to take a while. Such a guide will be useful for sure!". But it ended up being way easier than I thought 😅&lt;br&gt;
In the next posts, I will show you my experiments with Hotwire and Turbo and show how I refactored the code to use the new framework. As of now, Turbo is up and running! 🥳&lt;/p&gt;

</description>
      <category>rails</category>
      <category>turbolinks</category>
      <category>turbo</category>
      <category>hotwire</category>
    </item>
    <item>
      <title>CanCanCan 3.2.0</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Sat, 12 Dec 2020 21:25:12 +0000</pubDate>
      <link>https://dev.to/coorasse/cancancan-3-2-0-23cj</link>
      <guid>https://dev.to/coorasse/cancancan-3-2-0-23cj</guid>
      <description>&lt;p&gt;Hi all 👋,&lt;br&gt;
a new version of CanCanCan is out!&lt;/p&gt;

&lt;p&gt;Again, we have a minor release but it includes a bunch of very important features, next to support for Rails 6.1.0.&lt;/p&gt;

&lt;p&gt;I have &lt;a href="https://github.com/sponsors/coorasse" rel="noopener noreferrer"&gt;opened a sponsorship program&lt;/a&gt;, please consider supporting the project if you use CanCanCan. It really helps! &lt;/p&gt;
&lt;h2&gt;
  
  
  Switching query strategy
&lt;/h2&gt;

&lt;p&gt;Since version 3.0.0 we started changing the way we perform the queries when using &lt;code&gt;accessible_by&lt;/code&gt;, in order to be more performant and reliable. &lt;/p&gt;

&lt;p&gt;As expected, this new way of performing the queries didn't fit everyone, so in version 3.1.0 we switched from &lt;code&gt;left_joins&lt;/code&gt; to &lt;code&gt;subqueries&lt;/code&gt; (&lt;a href="https://github.com/CanCanCommunity/cancancan/pull/605" rel="noopener noreferrer"&gt;see relevant PR&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This again didn't make everyone happy 😃 , so we decided, in the version 3.2.0 to allow to configure the preferred query mechanism: left joins or inner queries. &lt;/p&gt;

&lt;p&gt;You can now setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;CanCan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessible_by_strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:subquery&lt;/span&gt; &lt;span class="c1"&gt;# or :left_join&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to change it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support for Single Table Inheritance
&lt;/h2&gt;

&lt;p&gt;Single Table Inheritance is now supported. Given the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Vehicle&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Motorbike&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Vehicle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can play with rules by defining:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Vehicle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and query for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessible_by&lt;/span&gt;
&lt;span class="c1"&gt;# or&lt;/span&gt;
&lt;span class="no"&gt;Motorbike&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessible_by&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Motorbike&lt;/span&gt;

&lt;span class="no"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessible_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; returns only motorbikes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;a href="https://github.com/CanCanCommunity/cancancan/pull/649/files" rel="noopener noreferrer"&gt;relevant PR&lt;/a&gt; for more examples and note that &lt;a href="https://github.com/CanCanCommunity/cancancan/pull/663" rel="noopener noreferrer"&gt;there are currently some minor issues&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Support for associations in rules definition
&lt;/h2&gt;

&lt;p&gt;When using associations in rules definition you always had to use column names. Now, &lt;a href="https://github.com/CanCanCommunity/cancancan/pull/650" rel="noopener noreferrer"&gt;thanks to this PR&lt;/a&gt; you can also use the association name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# previously you had to define:&lt;/span&gt;
&lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;author_id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;

&lt;span class="c1"&gt;# now you can also write:&lt;/span&gt;
&lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;author: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enjoy! And to the next one...&lt;/p&gt;

</description>
      <category>rails</category>
      <category>cancancan</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Rails API: be nice to your clients</title>
      <dc:creator>Alessandro Rodi</dc:creator>
      <pubDate>Tue, 20 Oct 2020 15:49:29 +0000</pubDate>
      <link>https://dev.to/coorasse/rails-api-be-nice-to-your-clients-1aep</link>
      <guid>https://dev.to/coorasse/rails-api-be-nice-to-your-clients-1aep</guid>
      <description>&lt;p&gt;Since at &lt;a href="https://renuo.ch" rel="noopener noreferrer"&gt;Renuo&lt;/a&gt; we recently worked a lot on implementing APIs for third parties and we received strong compliments and comments like &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Oh! Thank god! Finally a well-made API! &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I decided to share with you the decisions we took and re-used and refined among three different projects, so you can also be nice to your clients ❤️&lt;/p&gt;

&lt;h1&gt;
  
  
  Errors
&lt;/h1&gt;

&lt;h2&gt;
  
  
  ActiveRecord errors
&lt;/h2&gt;

&lt;p&gt;We read through the &lt;a href="https://jsonapi.org/" rel="noopener noreferrer"&gt;jsonapi standard&lt;/a&gt; and took out what we consider are the good parts, and removed everything that we didn't need.&lt;/p&gt;

&lt;p&gt;That's the structure of our errors when we return them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pointer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blank"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"First Name can't be blank"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pointer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blank"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Last Name can't be blank"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This structure has the following advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gives you the possibility to define multiple errors, and not a single one.&lt;/li&gt;
&lt;li&gt;Each error is structured separately and contains: 

&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;pointer&lt;/code&gt; to the place where the error happened, &lt;/li&gt;
&lt;li&gt;a &lt;code&gt;code&lt;/code&gt;, readable by a machine, that defines a unique kind of error, &lt;/li&gt;
&lt;li&gt;a &lt;code&gt;detail&lt;/code&gt;, that contains text, easy to read by humans, to understand what is wrong.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These errors have all the characteristics necessary to be easily understood, debugged and solved by your clients.&lt;/p&gt;

&lt;p&gt;We even return such errors:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pointer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gender"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"inclusion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gender is not included in the list. Allowed values: male, female, company, other"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;by giving a hint on how to solve the problem in the detail itself.&lt;/p&gt;

&lt;p&gt;The controller implementation is as easy as&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;errors: &lt;/span&gt;&lt;span class="no"&gt;ErrorsMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can find the implementation of the &lt;code&gt;ErrorsMapper&lt;/code&gt; in this gist:&lt;/p&gt;


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



&lt;h2&gt;
  
  
  Generic errors
&lt;/h2&gt;

&lt;p&gt;How do we keep the same structure when an unexpected error happens? We use a custom middleware that is configured as exceptions_app in application.rb as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/application.rb&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exceptions_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;JsonApiPublicExceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;public_path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;here is an example implementation of such middleware:&lt;/p&gt;


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



&lt;p&gt;it has two characteristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It hides details in case of 500&lt;/li&gt;
&lt;li&gt;It shows the content of the attribute &lt;code&gt;reason&lt;/code&gt; in the Exception, if present, allowing us to define custom errors and returning custom messages.&lt;/li&gt;
&lt;li&gt;It re-uses the &lt;code&gt;ErrorsMapper&lt;/code&gt; seen above to keep the same errors structure.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is an example of error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal_server_error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An internal error occurred. We have been notified and we will tackle the problem as soon as possible."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Custom errors
&lt;/h2&gt;

&lt;p&gt;When we need to display a custom error, we can now rely on Rails &lt;code&gt;exceptions_app&lt;/code&gt; configuration. If, for example, we want to show an error when the provided api key is missing or wrong we define our custom Exception:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnauthorizedError&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:reason&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and we instruct Rails on how to treat this exception:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/application.rb&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_dispatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rescue_responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s1"&gt;'UnauthorizedError'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:unauthorized&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;we can then raise an Exception and specify also the reason why we raised it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;UnauthorizedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:missing_api_key&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;UnauthorizedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:wrong_api_key&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Api-Key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'API_KEY'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's all regarding the errors part. Let's now save some of our clients time 😉&lt;/p&gt;
&lt;h1&gt;
  
  
  fresh_when
&lt;/h1&gt;

&lt;p&gt;I won't go deep in this blog post regarding the usage of &lt;code&gt;fresh_when&lt;/code&gt;, since you can read everything about it &lt;a href="https://apidock.com/rails/ActionController/ConditionalGet/fresh_when" rel="noopener noreferrer"&gt;in the documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I encourage you to use it when possible but do not abuse it and be careful. If, for example, in the response, you return nested resources, you should keep this in consideration when implementing the &lt;code&gt;fresh_when&lt;/code&gt;. As always: caching is hard and adds complexity to the system. Do it wisely and document it.&lt;/p&gt;
&lt;h1&gt;
  
  
  Swagger
&lt;/h1&gt;

&lt;p&gt;Provide a nice and up-to-date swagger documentation of your APIs. The gem &lt;a href="https://github.com/rswag/rswag" rel="noopener noreferrer"&gt;rswag&lt;/a&gt; is able to publish a nice, clickable, documentation, generated from the swagger, and also to generate the documentation directly from your tests. Give it a try!&lt;/p&gt;
&lt;h1&gt;
  
  
  Strong Parameters
&lt;/h1&gt;

&lt;p&gt;Last suggestion, with also another bit of code that you might re-use. How do you behave when a client sends an unknown parameter? By using StrongParameters you have, in general, two choices:&lt;/p&gt;
&lt;h2&gt;
  
  
  You raise an exception
&lt;/h2&gt;

&lt;p&gt;You can configure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_on_unpermitted_parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and every time you receive an unknown parameter, your application will raise an exception. This is an ok behaviour, but it might not suite all the situations, that's also why the default is the next one:&lt;/p&gt;
&lt;h2&gt;
  
  
  You ignore them
&lt;/h2&gt;

&lt;p&gt;By default, unpermitted_parameters are simply ignored and skipped, but this might lead to a problem when the client sends a non-mandatory field and commits a typo. ouch!&lt;br&gt;
You defined the optional field as &lt;code&gt;zip_code&lt;/code&gt; and they sent &lt;code&gt;zip&lt;/code&gt;. Since is not mandatory, your API will simply ignore the field and return a nice &lt;code&gt;201&lt;/code&gt; to the clients, informing them that the record has been saved.&lt;/p&gt;

&lt;p&gt;You can be nice to your clients and still return a &lt;code&gt;201&lt;/code&gt; but also giving them an hint that something might be wrong. We implemented and use the following concern in our controllers:&lt;/p&gt;


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



&lt;p&gt;This concern will add a &lt;code&gt;{"meta": {"hints": [...]}}&lt;/code&gt; part to your response, with the list of attributes sent in the request and not accepted by the API. By default, simply including this concern, you will obtain a response like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"zip is not a valid parameter"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but you can also do one step more and set the list of allowed attributes with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;model_params&lt;/span&gt;
 &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permitted_action_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%i[zip_code first_name last_name]&lt;/span&gt;
 &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:model_name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;permitted_action_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the error will magically be even more detailed. for the customer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"zip is not a valid parameter. Did you mean zip_code?"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Versioning
&lt;/h1&gt;

&lt;p&gt;There are different ways how you can version your APIs for breaking changes. The solution we adopt at Renuo is the &lt;code&gt;Api-Version&lt;/code&gt; header. We went through all other possibilities before deciding that a version header is our first choice. Shortly: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URL versioning sucks, you need to define all new routes every time you need to release a new version, and do weird customizations to redirect v2 endpoints to v1 controllers if they don't have a v2 implementation. Also, your clients will need to invoke new endpoints 🤮.&lt;/li&gt;
&lt;li&gt;Versioning via query parameter might work but you don't want to mix "meta" parameters with your actual ones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We usually implement a very easy method that fetches the current wished version by the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;api_version&lt;/span&gt;
  &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Api-Version'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and what might sound weird but is actually really effective, is that at the very beginning, you can simply write something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_something&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;api_version&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;do_something_new&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
   &lt;span class="n"&gt;do_something_old&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you will cover already 80% of your needs.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;p&gt;I hope the tips above will help you with your work and to implement better APIs. Since it will happen that I am on the client-side, I hope that the developer on the server-side read this blog post. &lt;/p&gt;

&lt;p&gt;If you need to implement APIs or need help with your Rails app &lt;a href="https://www.renuo.ch/en/contact" rel="noopener noreferrer"&gt;get in touch with us at Renuo&lt;/a&gt;. We will be happy to help!&lt;/p&gt;

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