<?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>Forem: Paweł Dąbrowski</title>
    <description>The latest articles on Forem by Paweł Dąbrowski (@paweldabrowski).</description>
    <link>https://forem.com/paweldabrowski</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%2F299971%2Fce37567e-c67e-4498-967b-cc04aa285e90.png</url>
      <title>Forem: Paweł Dąbrowski</title>
      <link>https://forem.com/paweldabrowski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/paweldabrowski"/>
    <language>en</language>
    <item>
      <title>Security Best Practices for Your Rails Application</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Wed, 12 Oct 2022 11:36:28 +0000</pubDate>
      <link>https://forem.com/appsignal/security-best-practices-for-your-rails-application-2f99</link>
      <guid>https://forem.com/appsignal/security-best-practices-for-your-rails-application-2f99</guid>
      <description>&lt;p&gt;Alongside performance and usability, you should always focus on security when creating any web application. Keep in mind that hacking techniques are constantly evolving, just as fast as technology is. So you must know how to secure your users and their data.&lt;/p&gt;

&lt;p&gt;This article will show you how to create a secure Rails application. The framework is known to be secure by default, but the default configuration is not enough to let you sleep well at night.&lt;/p&gt;

&lt;p&gt;We will share some best coding practices and a few habits in the development process that can help you create secure code.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Security in Your App: Best Coding Practices
&lt;/h2&gt;

&lt;p&gt;Modern web applications are often complex. They utilize multiple data sources and handle custom authorization rules as well as different methods of authentication. Knowing how to avoid SQL injections or ensuring that users can only read their data is not enough.&lt;/p&gt;

&lt;p&gt;When you build a Rails application, you have to configure it properly, design the application securely, and write code that makes it bulletproof.&lt;/p&gt;

&lt;p&gt;We'll look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Application configuration&lt;/strong&gt; - The default configuration is exemplary, but we can make things even better with a few extra steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business logic&lt;/strong&gt; - Applications should be secure by design, not only by code. This principle is essential but is often ignored when an MVP has to be delivered quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code in controllers&lt;/strong&gt; - These classes are the entry point to our application, so an extra dose of attention is always needed to design a reliable application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code in models&lt;/strong&gt; - Many issues are related to a database, so it is essential to design and perform secure communication with the primary source of the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code in views&lt;/strong&gt; - The point where we expose data to the browser is also a popular target for hackers, so we have to ensure that we don't render anything that risks our users' data or privacy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Application Configuration
&lt;/h3&gt;

&lt;p&gt;It all starts here, in the configuration files. In most cases, unless you restart the application, the rules will remain the same. Rails' creators made an effort to create secure defaults, but we can still improve them with some extra steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Force SSL
&lt;/h3&gt;

&lt;p&gt;You can force your Rails application to always use a secure connection with the HTTPS protocol. To do this, open the &lt;code&gt;config/environments/production.rb&lt;/code&gt; file and set the following line:&lt;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;force_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setting does a few things to the application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every time there is a request to the HTTP version of the application, Rails will redirect the request to the HTTPS protocol.&lt;/li&gt;
&lt;li&gt;It sets a secure flag on cookies. Thanks to this, browsers won't send cookies with HTTP requests.&lt;/li&gt;
&lt;li&gt;It tells the browser to remember your application as TLS-only (TLS is Transport Layer Security, an extension of the HTTPS protocol).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CORS
&lt;/h3&gt;

&lt;p&gt;Cross-Origin Resource Sharing (CORS) is a security mechanism that defines which website can interact with your application's API. Of course, if you build a monolith app, you don't need to care about this protection.&lt;/p&gt;

&lt;p&gt;If you build an API application, you can configure CORS by installing an additional gem called &lt;code&gt;rack-cors&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once you have done that, create a file called &lt;code&gt;cors.rb&lt;/code&gt; in the initializers directory &lt;code&gt;config/initializers&lt;/code&gt;. Define what endpoints a website can access (including request methods):&lt;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;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;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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&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;Cors&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
 &lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="n"&gt;origins&lt;/span&gt; &lt;span class="s1"&gt;'https://your-frontend.com'&lt;/span&gt;
   &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s1"&gt;'/users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;:headers&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;:methods&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s1"&gt;'/articles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;headers: :any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;methods: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:get&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;In the above example, we allow the your-frontend.com website to call the &lt;code&gt;/users&lt;/code&gt; endpoint (using only the POST method) and the &lt;code&gt;/articles&lt;/code&gt; endpoint (using only the GET method).&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure Environment Variables
&lt;/h3&gt;

&lt;p&gt;You should never hardcode your API keys, passwords, or other sensitive credentials in the source code. You might accidentally make them public or give someone who's not authorized access to some sensitive application resources.&lt;/p&gt;

&lt;p&gt;The Rails framework itself provides a way to store credentials securely. However, the implementation varies depending on the framework version:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rails 4&lt;/strong&gt; - The feature is called 'secrets'. You store your sensitive information in a &lt;code&gt;config/secrets.yml&lt;/code&gt; file that is not tracked in the git repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rails 5&lt;/strong&gt; - The feature is called 'credentials'. Your sensitive information is stored encrypted in &lt;code&gt;config/credentials.yml.enc&lt;/code&gt; — you can edit it with the &lt;code&gt;config/master.key&lt;/code&gt; file. So while the YAML configuration file can be stored in the repository because it’s encrypted, you don’t track the &lt;code&gt;master.key&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rails 6&lt;/strong&gt; - Also called 'credentials', you can store credentials per environment. Because of that, for every environment, you have the encrypted YAML file and key to decrypt it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alternatively, you can always set the values on the server level, so that they only load in the server environment. Locally, each developer sets them individually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Business Logic
&lt;/h3&gt;

&lt;p&gt;You should think about security not only when you write code, but also when you design the processes in your application.&lt;/p&gt;

&lt;p&gt;Business logic is a set of rules that apply in the real world, and your goal is to map them in your code. Unfortunately, mapping them can cause weak points in your application and lead to security issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strong Authentication and Authorization Rules
&lt;/h3&gt;

&lt;p&gt;Let's first explain the difference between authentication and authorization, as these terms are sometimes misinterpreted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; - You validate a user's login and password against an application's database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt; - You validate the role of a signed-in user and, based on that, render different information for different users. For example, a user with an admin role can access a list of users in an application, while in most cases, the typical user can't.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To improve the security level of your authentication, you can set high standards for your end-users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strong passwords&lt;/strong&gt; - Set a strict password policy that doesn't allow for passwords that are too simple or weak. Of course, you can't control if your users share their passwords, but you can make their passwords hard to guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-factor authentication&lt;/strong&gt; - Another layer of protection that will secure an account even if someone else knows the password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted passwords&lt;/strong&gt; - Never store passwords in your database as plain text. Then, even if your database is exposed, a hacker won't be able to get your users' passwords.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Authorization Best Practices
&lt;/h3&gt;

&lt;p&gt;If your application is complex, you probably need different roles for users to manage their data. As business logic grows, it may be hard to control everything without making a mistake that leads to an information leak.&lt;/p&gt;

&lt;p&gt;You can avoid issues by following some well-known good practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep authorization logic in one place&lt;/strong&gt; - It's hard to modify business logic correctly if you have to change multiple areas in the code. Storing the rules in one file makes it easy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set clear rules&lt;/strong&gt; - You won't be able to tell whether your application is secure if you can't validate the business logic against well-defined rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set roles based on the group, not a single user&lt;/strong&gt; - It is easier to control the authorization process if you have groups of permissions instead of defining rules per user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, good code reviews and well-written tests are also a must here to avoid introducing bugs to existing business logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code in Controllers
&lt;/h3&gt;

&lt;p&gt;Controllers are the first layer of MVC architecture and handle requests coming from users. Therefore, it is essential to filter incoming information correctly as it will be propagated on other application layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filter Incoming Parameters
&lt;/h3&gt;

&lt;p&gt;You should never pass a raw &lt;code&gt;params&lt;/code&gt; value to your application. Thanks to the strong parameters feature, it is easy to control the data that we'd like to pass along.&lt;/p&gt;

&lt;p&gt;Imagine that we have a &lt;code&gt;User&lt;/code&gt; model we want to update:&lt;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;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
    &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user&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;Someone can manipulate the params, and it would be possible to update other &lt;code&gt;User&lt;/code&gt; model attributes (for example, the &lt;code&gt;admin&lt;/code&gt; flag if you have one). To avoid such a situation, filter params that you pass to your model:&lt;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;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
    &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_params&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_name&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;h3&gt;
  
  
  Use Scopes to Avoid Data Leaking
&lt;/h3&gt;

&lt;p&gt;Usually, we don't want to show the user data that does not belong to them. We may inadvertently expose some records for URL address manipulation due to the wrong code design. Let's consider the following case:&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;MessagesController&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:authenticate_user!&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;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;Everything seems fine; the controller is protected from guests, and we assign the message object. However, a user just needs to change the id in the URL address to get a message that does not belong to them. This is a very dangerous situation.&lt;/p&gt;

&lt;p&gt;We can avoid it by using scopes within the given context. In the mentioned example, the current user is the scope:&lt;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;MessagesController&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:authenticate_user!&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Avoid Insecure URL Redirects
&lt;/h3&gt;

&lt;p&gt;Let's consider a straightforward example of a redirect based on the user input:&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;redirect_to&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;:url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should never do that, as we expose our user to a redirect that can take them everywhere. The simplest solution to prevent this security issue is to avoid using redirects with user input. If you can't, you can always redirect to a path without the host:&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;path&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="nf"&gt;parse&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;:url&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
&lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code in Models
&lt;/h3&gt;

&lt;p&gt;Once Rails parses code in a controller, you will probably call models to receive the information from the database. Since models are responsible for communicating with your database as well as performing important computations, they are also often targeted by hackers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid SQL Injection
&lt;/h3&gt;

&lt;p&gt;SQL injection is one of the most popular techniques used to access database information from the outside. The Rails framework tries to protect us from that threat, but we also need to write code that won't let this happen.&lt;/p&gt;

&lt;p&gt;In general, we should avoid passing user input directly as a part of a query:&lt;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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&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;:table&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id DESC'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to, always validate the user input and assign a default value when the input is invalid:&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;valid_tables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%w[articles posts messages]&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;valid_tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&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;:table&lt;/span&gt;&lt;span class="p"&gt;])&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;:table&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"articles"&lt;/span&gt;
&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id DESC'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://rails-sqli.org/"&gt;Check out this extensive set of examples of what &lt;em&gt;not&lt;/em&gt; to do when it comes to SQL injection&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prevent Command Injection
&lt;/h3&gt;

&lt;p&gt;Avoid using methods that allow a program to execute any code. Such methods include &lt;code&gt;exec&lt;/code&gt;, &lt;code&gt;system&lt;/code&gt;, &lt;code&gt;`&lt;/code&gt;, and &lt;code&gt;eval&lt;/code&gt;. You should never pass user input to those functions.&lt;/p&gt;

&lt;p&gt;If your application allows users to execute code, you should run their code in separate Docker containers. In addition, you can remove dangerous methods before executing user input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Kernel&lt;/span&gt;
 &lt;span class="n"&gt;remove_method&lt;/span&gt; &lt;span class="ss"&gt;:exec&lt;/span&gt;
 &lt;span class="n"&gt;remove_method&lt;/span&gt; &lt;span class="ss"&gt;:system&lt;/span&gt;
 &lt;span class="n"&gt;remove_method&lt;/span&gt; &lt;span class="ss"&gt;:`&lt;/span&gt;
 &lt;span class="n"&gt;remove_method&lt;/span&gt; &lt;span class="ss"&gt;:eval&lt;/span&gt;
 &lt;span class="n"&gt;remove_method&lt;/span&gt; &lt;span class="ss"&gt;:open&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt; &lt;span class="ss"&gt;:remove_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:eval&lt;/span&gt;
&lt;span class="no"&gt;RubyVM&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InstructionSequence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt; &lt;span class="ss"&gt;:remove_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:eval&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Avoid Unsafe Data Serialization
&lt;/h3&gt;

&lt;p&gt;Insecure deserialization can lead to the execution of arbitrary code in your application. If you plan to serialize JSON:&lt;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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"value"&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "{\"key\":\"value\"}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of using &lt;code&gt;load&lt;/code&gt; or &lt;code&gt;restore&lt;/code&gt; methods, use &lt;code&gt;parse&lt;/code&gt;:&lt;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;# bad&lt;/span&gt;
&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&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="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restore&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="c1"&gt;# good&lt;/span&gt;
&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you plan to serialize YAML:&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"key"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"value"&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_yaml&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "---\nkey: value\n"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of using &lt;code&gt;load&lt;/code&gt; or &lt;code&gt;restore&lt;/code&gt; methods from the &lt;code&gt;Marshal&lt;/code&gt; module, use the &lt;code&gt;safe_load&lt;/code&gt; method from &lt;code&gt;Psych&lt;/code&gt;:&lt;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;# bad&lt;/span&gt;
&lt;span class="no"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&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="no"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restore&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="c1"&gt;# good&lt;/span&gt;
&lt;span class="no"&gt;Psych&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code in Views
&lt;/h3&gt;

&lt;p&gt;Whatever you render in views is visible to the end-user. When malicious code is rendered, it will affect your users directly by exposing them to untrusted websites and data sources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid CSS Injection
&lt;/h3&gt;

&lt;p&gt;It would appear that CSS code is always secure; it only modifies the styles of a website. However, if you allow for user-defined styles in your application, there is always a risk of CSS code injection.&lt;/p&gt;

&lt;p&gt;One of the most popular cases of user-defined CSS is a custom page background:&lt;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;body&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background: &amp;lt;%= profile.background_color %&amp;gt;;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A user with bad intentions can input a URL that an application will automatically load, and do damage to the current user viewing the content. To prevent such situations, provide a predefined set of values instead of allowing users to type any value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sanitize Rendered Output
&lt;/h3&gt;

&lt;p&gt;In the modern versions of Rails, output is sanitized by default. Even if a user inputs HTML or JS code and the application renders it, the HTML or JS code will escape.&lt;/p&gt;

&lt;p&gt;If you want to render HTML defined by users, always predefine which tags should render and which should escape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we allow users to render &lt;code&gt;strong&lt;/code&gt;, &lt;code&gt;em&lt;/code&gt;, and &lt;code&gt;a&lt;/code&gt; HTML elements in their comments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Include Sensitive Data in Comments
&lt;/h3&gt;

&lt;p&gt;This is primarily a reminder for junior developers unfamiliar with how web applications work on the front end. Don't ever put sensitive information in comments (especially in views, as it will be exposed to end-users):&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="c"&gt;&amp;lt;!--- password for the service is 1234 –&amp;gt;
&amp;lt;%= @some_service.result_of_search %&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Habits to Keep Your Rails Application Secure
&lt;/h2&gt;

&lt;p&gt;To make your Rails app secure and reduce technical debt, establish some valuable development habits. Taking small preventative actions frequently is much less painful than refactoring more significant portions of your codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upgrade Rails and Other Libraries Often
&lt;/h3&gt;

&lt;p&gt;An upgrade is often painful and time-consuming, unless you upgrade to minor versions of a library. Develop a habit of upgrading a library each time a new, stable version is published. Your application will then be in good shape (not only regarding security, but also performance).&lt;/p&gt;

&lt;p&gt;If you use GitHub for day-to-day development, an extension like &lt;a href="https://github.com/marketplace/depfu"&gt;Depfu&lt;/a&gt; is useful as it performs frequent updates for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Perform Security Audits
&lt;/h3&gt;

&lt;p&gt;I don't mean expensive audits by external companies. Often, it is enough to install a tool like &lt;a href="https://brakemanscanner.org/"&gt;Brakeman&lt;/a&gt; and scan code with every commit or pull request.&lt;/p&gt;

&lt;p&gt;Also, it is a good idea to scan your Gemfile and find gems that need updating due to security issues discovered by the community. You can use &lt;a href="https://github.com/rubysec/bundler-audit"&gt;bundler-audit&lt;/a&gt; to automate this process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Have A Proper Logging Strategy
&lt;/h3&gt;

&lt;p&gt;Logs, in many cases, are usually just thousands of lines of information you will never view. However, there might be a case where one of your users is attacked or experiences a suspicious action.&lt;/p&gt;

&lt;p&gt;In such a situation, if you have detailed and easily searchable logs, you can collect information that will help you prevent similar problems in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up: Keep Your Rails App Secure
&lt;/h2&gt;

&lt;p&gt;In this post, we ran through some security best practices for your Rails app that reduce the risk of a data breach.&lt;/p&gt;

&lt;p&gt;Making sure that your Rails application is secure might be challenging. Sticking to the framework's defaults is not enough; you have to know how to avoid creating security issues. For example, various injections and remote code executions have been well-known issues for the past few years, but still, you can't be 100% sure that your application won't be affected.&lt;/p&gt;

&lt;p&gt;Don't forget the importance of frequent upgrades, security audits, and a culture of good logging. When combined, all of these things will help to make your Rails application more secure.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

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

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Connect a Ruby on Rails App with React in a Monolith</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Wed, 10 Aug 2022 13:06:49 +0000</pubDate>
      <link>https://forem.com/appsignal/connect-a-ruby-on-rails-app-with-react-in-a-monolith-21l3</link>
      <guid>https://forem.com/appsignal/connect-a-ruby-on-rails-app-with-react-in-a-monolith-21l3</guid>
      <description>&lt;p&gt;More and more people are using Ruby on Rails to create a back-end API application for a front-end app.&lt;/p&gt;

&lt;p&gt;But what if you want to create a rich and functional interface with JavaScript and use Rails for the back-end, without having them in separate repositories? You can create a monolithic Rails application.&lt;/p&gt;

&lt;p&gt;This article will show you how to connect a Rails application with a front-end developed in React (without splitting the code into two separate applications). We'll also give some alternatives if React is not your framework of choice, but you like the idea of a monolithic app with a rich front-end.&lt;/p&gt;

&lt;p&gt;But first, let’s start with an architecture overview to see why linking Rails and React is an option worth considering.&lt;/p&gt;

&lt;h2&gt;
  
  
  App Architecture: An Overview
&lt;/h2&gt;

&lt;p&gt;A while back, when a developer started to build a web application, they didn’t have a list of possible architectures they could use. For the most part, web apps were monoliths without a very interactive user interface.&lt;/p&gt;

&lt;p&gt;In modern software development, we have much more to choose from. We can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the old monolith architecture&lt;/li&gt;
&lt;li&gt;Go for a separated back-end and front-end&lt;/li&gt;
&lt;li&gt;Use service-oriented architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take a closer look at the most common types of architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Headless Architecture
&lt;/h3&gt;

&lt;p&gt;In headless architecture, the head of an application is detached from its body. In other words, you create a back-end application using Ruby, Python, Node.js, or another programming language. This application manages a connection with a database and provides computing power.&lt;/p&gt;

&lt;p&gt;The body is a front-end application created with a framework like React, Vue, or Angular.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZF0oHejg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-08/headless.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZF0oHejg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-08/headless.png" alt="alt text" width="880" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CDN stands for content delivery network, a service designed to deliver assets faster for visitors worldwide. We can build the architecture in the cloud environment, or each setup piece can be a separate server.&lt;/p&gt;

&lt;p&gt;Choose headless architecture when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You aim for a large codebase.&lt;/strong&gt; If your application is going to be very large, the separate back-end and front-end layers will make it more maintainable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You expect different workloads for different system elements.&lt;/strong&gt; With a more modular approach, you can scale modules separately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You foresee that you'll change the technology in the future.&lt;/strong&gt; It is easier to adopt new technology with a headless architecture, as the communication, in most cases, will be the same between the back-end and front-end, regardless of the stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid a headless approach when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You want to develop an MVP version of an app very quickly.&lt;/strong&gt; You will likely start over again in the future and consider a different angle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You have a very small team.&lt;/strong&gt; Finding the right person to do the back-end, front-end, and DevOps stuff simultaneously might be hard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to build a straightforward application.&lt;/strong&gt; Unfortunately, the headless approach increases your app's complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monolith Architecture
&lt;/h3&gt;

&lt;p&gt;One application handles the presentation layer (front) and computation layer (back) in a monolith architecture. Such an approach speeds up the application creation process and simplifies the server setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jpIVLNMe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-08/monolith.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jpIVLNMe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-08/monolith.png" alt="alt text" width="880" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you'll notice, in a monolith, we eliminate a part of the communication that's in the headless approach. Such architecture improves the performance and security of an application.&lt;/p&gt;

&lt;p&gt;Choose this architecture when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You develop an MVP version of your app.&lt;/strong&gt; Frameworks like Ruby on Rails or Django make it easy and fast to build robust monolith applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You start with a small team.&lt;/strong&gt; Back-end developers can easily handle the front-end in such an architecture, and the deployment process is straightforward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Your application doesn’t have to depend on a very interactive front-end, and you don’t want to build a single-page application.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid the monolith approach if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You know that the front-end of the application will be huge.&lt;/strong&gt; It is harder to maintain this part as it's connected directly with the back-end codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You know the front-end will have a completely different workload than the back-end.&lt;/strong&gt; As a result, you won’t scale certain elements of the monolith easily.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You may change the front-end or back-end tech stack in the future.&lt;/strong&gt; Such a transition would be pretty complicated with this architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hybrid Architecture
&lt;/h3&gt;

&lt;p&gt;An alternative approach to the monolith and headless architectures is a hybrid. You create a monolith application, but instead of using the front-end produced by the back-end framework, use the technology of headless applications.&lt;/p&gt;

&lt;p&gt;Yet this comes with a few disadvantages which you should consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The codebase is shared.&lt;/strong&gt; So as the application grows, it is harder to navigate through all the files and build a structure that is easy to understand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling only the back-end or front-end is very difficult&lt;/strong&gt; as both parts connect in one application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Being agile is not easy.&lt;/strong&gt; With every change, you deploy the whole application, even if it’s just a change in the front-end style.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, you also get some benefits that you wouldn’t get with a standard monolith architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You can work separately on the back-end and front-end and update libraries independently.&lt;/strong&gt; Changing the stack of the front-end is easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can build a very interactive, single-page application&lt;/strong&gt; based on the monolith with a reasonably simple deployment process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can be flexible.&lt;/strong&gt; It's possible to use the front-end served by the JavaScript framework for some parts of your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these pros and cons of hybrid architecture in mind, we will now go through the design process of a monolith application (created with Rails and a front-end React framework).&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing a Monolith with React
&lt;/h2&gt;

&lt;p&gt;There are three main things to keep in mind before you build a monolith application with a React front-end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Front-end framework installation process&lt;/strong&gt; - there are a few ways of adding React to a Rails application. However, each has some limitations, so choosing the right one for your project is essential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication inside the application&lt;/strong&gt; - The back-end needs to expose data to the front-end, and the front needs to present this data. This element of the system needs to be designed carefully to make information exchange as smooth and fast as possible. As always, your choice should depend on the type of application you would like to build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alternatives&lt;/strong&gt; - You can still go for the hybrid approach but not use React. We will show you other solutions that you can quickly adapt to build a more interactive monolith.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install React in Your Ruby on Rails App
&lt;/h2&gt;

&lt;p&gt;There are three main ways to install React in your Rails application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install React Using Ruby Gems
&lt;/h3&gt;

&lt;p&gt;We can extend the functionality of our app by installing external libraries called Ruby gems. If you are unfamiliar with JavaScript, this is the fastest way to add React to Rails.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/reactjs/react-rails"&gt;react-rails library&lt;/a&gt; is one of the most popular Ruby gems to integrate React with Rails. It provides generators for components, testing helpers, and view helpers to render JavaScript code inside the views.&lt;/p&gt;

&lt;p&gt;If you accept another layer of logic for your front-end, using this Ruby gem is the best way to get started with React quickly. However, remember that besides the JS framework updates, you also have to depend on the gem updates. The more gems you use, the more problematic the Rails upgrade process can become.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install React Using Import Maps
&lt;/h3&gt;

&lt;p&gt;Import maps were first presented in Rails 7. The lib lets you build modern JS applications using libraries made for ES modules without transpiling or bundling.&lt;/p&gt;

&lt;p&gt;Installing React with import maps is as easy as calling this command from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./bin/importmap pin react react-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don’t even need to host source files on your server, as they are served from JavaScript’s CDN by default. By applying the &lt;code&gt;--download&lt;/code&gt; flag, you can download the files to the vendor directory inside your application.&lt;/p&gt;

&lt;p&gt;However, you cannot use JSX (the most popular rendering extension in the React community) with import maps. HTM library is an alternative solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install React Using Package Manager
&lt;/h3&gt;

&lt;p&gt;This option seems to be the most flexible and optimal way to add React to a Rails project. You don’t create another level of abstraction for the front-end as you fetch the library directly. Updating React is also simpler.&lt;/p&gt;

&lt;p&gt;The most popular package managers in the Rails community are NPM and Yarn. You can add React libraries by invoking this command in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add react react-dom &lt;span class="c"&gt;# or npm install react react-dom&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this installation method, you won’t get any Rails helpers to render JavaScript code. However, we'll show you how to install and run React components using this approach shortly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication Between Your Ruby on Rails App and React
&lt;/h2&gt;

&lt;p&gt;Choosing the right way to exchange information between your Rails application and React components is essential. Selecting the wrong method can harm your application's performance and make your code less maintainable.&lt;/p&gt;

&lt;p&gt;There are two common, standardized ways of exposing information from the back-end — REST and GraphQL. Let's take a look at both options.&lt;/p&gt;

&lt;h3&gt;
  
  
  REST Communication
&lt;/h3&gt;

&lt;p&gt;The REST API is a pretty simple way of exchanging information. We have different request types at our disposal to perform CRUD (create, retrieve, update, delete) operations. If you are familiar with the concept of resources in Rails, the REST API works the same way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VOQRvYF7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-08/rest.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VOQRvYF7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-08/rest.png" alt="REST" width="880" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You call the &lt;code&gt;/authors&lt;/code&gt; endpoint if you want to pull information about authors or a single author. You call the &lt;code&gt;/posts&lt;/code&gt; endpoint to do the same, but for posts. If a single page in your application requires a lot of different data, you will perform multiple HTTP requests.&lt;/p&gt;

&lt;p&gt;You should consider using REST in your application if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your front-end application doesn’t need to render data from different sources on a single page.&lt;/strong&gt; On the other hand, this doesn’t have to mean that the application is very simple.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You don’t want to implement the cache mechanism on the application level.&lt;/strong&gt; Using REST API, you can benefit from the HTTP cache provided by browsers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You care about error reporting.&lt;/strong&gt; Implementing an error report with REST is easy as you work on different HTTP responses. With GraphQL, it’s not, as your application always returns a 200 response status.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GraphQL Communication
&lt;/h3&gt;

&lt;p&gt;GraphQL represents an entirely different approach to data fetching. Using this technology, you perform one request and get only the data you need. You can hit multiple endpoints at once and pull only one attribute per endpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6qPqE0uo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-08/graphql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6qPqE0uo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2022-08/graphql.png" alt="GraphQL" width="880" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With GraphQL implemented on the back-end, you always call the same endpoint and modify the query parameter.&lt;/p&gt;

&lt;p&gt;You should consider using GraphQL in your application if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You are building a very interactive application&lt;/strong&gt; requiring the front-end to pull a lot of different data at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to build your application very quickly.&lt;/strong&gt; Building an API with REST is slower. With GraphQL, you get maximum flexibility, and all you need to care about in every step is the content of the query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You are building an application where the API is used by multiple different applications&lt;/strong&gt; like mobile, front-end, or services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Front-end Alternatives to React for Your Ruby on Rails App
&lt;/h2&gt;

&lt;p&gt;If you don’t want to use React but like the idea of having a front-end application inside the Rails monolith, there are some alternative solutions available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rails Turbo
&lt;/h3&gt;

&lt;p&gt;Turbo allows you to write single-page applications without the need for any JavaScript code. You design the page from independent frames. They behave like components and can also be lazy-loaded if needed.&lt;/p&gt;

&lt;p&gt;With Rails Turbo, you don’t spend time building the JSON responses from an API. You can simply reuse Rails views and render HTML — it’s automatically transferred and rendered in your components. This reduces the amount of code in your application.&lt;/p&gt;

&lt;p&gt;You should consider using Rails Turbo if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You don’t like writing JavaScript.&lt;/strong&gt; It is possible to design rich and interactive web applications without the need for a single line of JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You like the Rails framework.&lt;/strong&gt; The Turbo library provides helper methods to quickly build interactive pages or transform existing ones into more dynamic forms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to move fast.&lt;/strong&gt; As mentioned before, you don’t have to care about the JSON responses from an API. You can simply serve HTML views and the Turbo library will automatically pull them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stimulus JS
&lt;/h3&gt;

&lt;p&gt;Stimulus is a JavaScript framework created by the Rails team. It was designed to extend the HTML that you already have. The main concept of this framework is the controller — one controller per view.&lt;/p&gt;

&lt;p&gt;Your Rails application will automatically load the JavaScript controller for the given view and map the HTML elements. You can respond to events or modify the view when you need to.&lt;/p&gt;

&lt;p&gt;You should consider using Stimulus if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are already using Rails Turbo, but want more control over a page's granular elements.&lt;/li&gt;
&lt;li&gt;You don’t want to build a single-page application but still want to make some page elements interactive or dynamic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other JavaScript Frameworks
&lt;/h3&gt;

&lt;p&gt;Besides React, other interesting JavaScript frameworks on the market right now include Vue, Svelte, and Ember.js. So if you like writing JavaScript components, you can choose one of these.&lt;/p&gt;

&lt;p&gt;Rails Turbo and/or Stimulus JS are the perfect choice if you're a Rails developer who's familiar with JavaScript but doesn’t want to write much of it. Both libraries heavily follow the principles of the Rails framework, so if you already work in this ecosystem, it will be easier for you to use these extensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a Monolith with React
&lt;/h2&gt;

&lt;p&gt;It’s time to build a simple Rails application to manage a list of books we want to read. Let's start with generating the Rails skeleton.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ruby on Rails Project Bootstrap
&lt;/h3&gt;

&lt;p&gt;Ensure you have the latest version of Ruby on Rails installed locally on your operating system. Then generate the skeleton using the &lt;code&gt;rails new&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails new booklist &lt;span class="nt"&gt;-d&lt;/span&gt; postgresql &lt;span class="nt"&gt;-j&lt;/span&gt; esbuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will generate a new Rails project in the &lt;code&gt;booklist&lt;/code&gt; directory, with support for a PostgreSQL database. We can now enter the project’s directory, create the database, and run the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;booklist/
./bin/rails db:create
rails s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit the localhost:3000 address in your browser to see the default Rails welcome screen.&lt;/p&gt;

&lt;p&gt;Now generate a home controller for a single endpoint in the application so we can attach the React application to the layout. Run this command in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/rails g controller Home index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last step is to update &lt;code&gt;config/routes.rb&lt;/code&gt; file to set the root path:&lt;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;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;"home#index"&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 focus on the front-end part with React.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Installation
&lt;/h3&gt;

&lt;p&gt;We will install the &lt;code&gt;react&lt;/code&gt;, &lt;code&gt;react-dom&lt;/code&gt;, and &lt;code&gt;node-uuid&lt;/code&gt; libraries using yarn in our terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add react react-dom node-uuid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To properly handle JSX, we also have to update our build script. First, open the &lt;code&gt;package.json&lt;/code&gt; file, and in the &lt;code&gt;scripts&lt;/code&gt; section, ensure that the &lt;code&gt;build&lt;/code&gt; command is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;esbuild app/javascript/&lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;--bundle&lt;/span&gt; &lt;span class="nt"&gt;--sourcemap&lt;/span&gt; &lt;span class="nt"&gt;--outdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;app/assets/builds &lt;span class="nt"&gt;--public-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;assets &lt;span class="nt"&gt;--loader&lt;/span&gt;:.js&lt;span class="o"&gt;=&lt;/span&gt;jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now run &lt;code&gt;./bin/dev&lt;/code&gt; to kickstart both &lt;code&gt;yarn build&lt;/code&gt; and the rails server or run them separately. For example, if you want processes in separate terminal tabs, run &lt;code&gt;rails s&lt;/code&gt; in one and &lt;code&gt;yarn build –watch&lt;/code&gt; in the second.&lt;/p&gt;

&lt;h3&gt;
  
  
  Render a React Application in a Rails Layout
&lt;/h3&gt;

&lt;p&gt;We want to run the React application when a user enters the main root in our Rails application. To do this, update the &lt;code&gt;app/views/layout.html.erb&lt;/code&gt; file, so that the &lt;code&gt;body&lt;/code&gt; section looks like this:&lt;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;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;p&gt;The React application will render the component with JavaScript logic inside the &lt;code&gt;app&lt;/code&gt; div.&lt;/p&gt;

&lt;p&gt;Let’s now create the entry point for the React application. Open the &lt;code&gt;app/javascript/application.js&lt;/code&gt; file and write the following code:&lt;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;React&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoot&lt;/span&gt; &lt;span class="p"&gt;}&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;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have the container element, but we are missing the &lt;code&gt;App&lt;/code&gt; component so the application will throw an error. Let’s create the &lt;code&gt;App&lt;/code&gt; component then.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a React Component
&lt;/h2&gt;

&lt;p&gt;We won’t create a separate directory for components. Let's place our tiny application in one component for demonstration purposes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: When writing a production app, always design the React app using multiple components and follow the best design principles.&lt;/p&gt;

&lt;p&gt;Create a file &lt;code&gt;App.js&lt;/code&gt; in the &lt;code&gt;app/javascript&lt;/code&gt; directory and place the application skeleton there, with the header as a placeholder:&lt;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;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setBooks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Books&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;h3&gt;
  
  
  Rendering a Book List
&lt;/h3&gt;

&lt;p&gt;Now it’s time to render a simple list of books. To do this, we have to update the &lt;code&gt;render&lt;/code&gt; function:&lt;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;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Books&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;thead&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/th&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/th&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tr&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/thead&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tbody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tr&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;))}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tbody&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/table&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a New Book
&lt;/h3&gt;

&lt;p&gt;We don’t have anything we can render. Let’s add the form to create a new book so we can fill our list with some data that we can later edit and delete if needed.&lt;/p&gt;

&lt;p&gt;However, before we do this, we have to update our component to store some more information:&lt;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="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;UUID&lt;/span&gt; &lt;span class="p"&gt;}&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;node-uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAction&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFormData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m not going to create a separate component for the form, so I have to use a conditional in the render function. Thanks to the &lt;code&gt;action&lt;/code&gt;, I can tell when to render the list or form.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;render&lt;/code&gt; function is the following:&lt;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;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Books&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;thead&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/th&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/th&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/th&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tr&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/thead&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tbody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
              &lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tr&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="p"&gt;))}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tbody&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/table&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
            &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
              &lt;span class="nx"&gt;setFormData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
            &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
              &lt;span class="nx"&gt;setFormData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;saveBook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Submit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Back&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each time you type something into the title or author input, &lt;code&gt;formData&lt;/code&gt; will update with values from the form. The last missing piece is the &lt;code&gt;saveBook&lt;/code&gt; function:&lt;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;setBooks&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;books&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="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}]);&lt;/span&gt;
  &lt;span class="nx"&gt;setFormData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;setAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&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;The first step is to prevent a form submission — we don’t want to reload the page. Then we update the books collection and reset the form data. The last step is to render back the list view.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update an Existing Book
&lt;/h3&gt;

&lt;p&gt;Let's reuse the form we created in the previous step. We also have to store the id of the book we are currently editing:&lt;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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentBookId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCurrentBookId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;editBook&lt;/code&gt; function will set the id of the book we want to edit. Fill the form with values and render the form view:&lt;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;editBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;setCurrentBookId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;setFormData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentBook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentBook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;setAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;"&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;Since we are going to use the &lt;code&gt;saveBook&lt;/code&gt; function for a creation and update action, we have to modify it accordingly:&lt;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentBookId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bookIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;currentBookId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;updatedBooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;updatedBooks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bookIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;setBooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedBooks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;setCurrentBookId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setBooks&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;books&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="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v4&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="nx"&gt;setFormData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;setAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;"&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;h3&gt;
  
  
  Delete a Single Book
&lt;/h3&gt;

&lt;p&gt;Let’s start with building the simple code for destroying the book:&lt;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&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="nx"&gt;setBooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;id&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;We get the array of books that aren't deleted and update the &lt;code&gt;books&lt;/code&gt; collection. React will update our component automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Complete Component
&lt;/h3&gt;

&lt;p&gt;We implemented all the actions needed to create, update, list, and destroy a book. &lt;a href="https://gist.github.com/pdabrowski6/ac2c04a71f8c98e6ee386d2a3c3807cd"&gt;View the code for the complete component in this Gist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Congratulations, you created a React application inside a Rails monolith! Let’s summarize what we have learned and done during this article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Different Types of Architecture for Web Apps
&lt;/h3&gt;

&lt;p&gt;When you build a web application, it has a front and back part. So you need to decide how you want to connect these two sides. Here's a reminder of the most common types of architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Headless architecture&lt;/strong&gt; - The front-end application is separate from the back-end. Choose it if you expect to write a lot of code, want to change technologies in the future, or expect a different workload for the front-end and back-end. However, keep in mind that such architecture increases the complexity of an application, takes more time to build, and the development process might be complex for a small development team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monolith architecture&lt;/strong&gt; - One application that handles both front-end and back-end. You can build one with popular frameworks like Ruby on Rails or Django. With this architecture, you can build fast with a small team. However, you can run into trouble in the future if you want to change the technology or scale only part of the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid architecture&lt;/strong&gt; - A compromise between a monolith and headless architecture. You build one codebase, but the front-end is served by a different technology from the back-end. The codebase might be hard to navigate, and it will be challenging to scale only one piece of the system. However, it is more flexible than a monolith. If you don’t like monoliths, but don’t want to fully detach the front from the back-end, this solution is for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Design Process to Build Hybrid Architecture
&lt;/h3&gt;

&lt;p&gt;Don’t jump into coding straight away. Think about the specifications of your application. When building a Rails application that uses a React framework for the front-end, you have to consider the following things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Installation process&lt;/strong&gt; - You can install React using import maps if you don’t need to use JSX templates. Otherwise, you can select package managers like Yarn or NPM, or install React using a Ruby gem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication between Rails and React&lt;/strong&gt; - Both applications are inside the same codebase, but need a bridge to exchange information. Use REST API for simpler applications and GraphQL for more complex interfaces where a lot of different data is necessary for the view layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Alternatives to React for a Rails App
&lt;/h3&gt;

&lt;p&gt;If you decide that you don’t want to use React, but you like the idea of hybrid architecture, you can consider the following alternative solutions for the front-end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rails Turbo&lt;/strong&gt; - With this library, you can split the page into independent frames and update them when needed. Go for this option if you like the Rails framework, and don’t want to write a lot of JavaScript code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stimulus JS&lt;/strong&gt; - A small library for the HTML you already have in your views. Choose it when you are happy with your Rails monolith, but you want to make some views more interactive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other JavaScript frameworks&lt;/strong&gt; - If you want to write more JavaScript, you can use Vue.js, Svelte, or Ember.js, instead of React. You can easily incorporate these solutions into your Rails application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summing Up
&lt;/h2&gt;

&lt;p&gt;In this post, we explored the three main types of architecture — headless, monolith, and hybrid — and looked at their pros and cons. We then created a React application inside a Rails monolith.&lt;/p&gt;

&lt;p&gt;Now it should hopefully be easier for you to choose the right architecture and tools for your next project.&lt;/p&gt;

&lt;p&gt;Until next time, happy coding!&lt;/p&gt;

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

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>react</category>
    </item>
    <item>
      <title>5 Tips to Design Ruby on Rails Transactions the Right Way</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Thu, 14 Apr 2022 10:20:20 +0000</pubDate>
      <link>https://forem.com/appsignal/5-tips-to-design-ruby-on-rails-transactions-the-right-way-40n0</link>
      <guid>https://forem.com/appsignal/5-tips-to-design-ruby-on-rails-transactions-the-right-way-40n0</guid>
      <description>&lt;p&gt;Data integrity problems are among the most common database issues Rails developers face. Besides allowing for proper validation, correctly designed transaction blocks ensure that your data isn't partially created or updated.&lt;/p&gt;

&lt;p&gt;However, transactions can also harm your application — or even take down your whole database — when not properly designed.&lt;/p&gt;

&lt;p&gt;This article offers a set of good practices for working with transactions. The tips are pretty simple, but they will help make your transactions bulletproof, readable, and relatively safe.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Use Bang Methods in Rails When Possible
&lt;/h2&gt;

&lt;p&gt;In Rails, method versions with &lt;code&gt;!&lt;/code&gt; can give you confidence that an error will be raised when something goes wrong.&lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;#save&lt;/code&gt; method also exists in the &lt;code&gt;save!&lt;/code&gt; version. You might want to use this version in the controller if you don't want to raise any errors:&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="c1"&gt;# redirect&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;:new&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above approach won't work well in transactions. By using &lt;code&gt;save&lt;/code&gt;, we can't roll back the process when the error is raised. That's why it is so important to use the &lt;code&gt;!&lt;/code&gt; version of the methods:&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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memberships&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;membership_attributes&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;In the above, the transaction succeeds even if the membership record isn't created, and we end up with a messed-up data structure in the database.&lt;/p&gt;

&lt;p&gt;If you use the following version, the transaction reverts due to an &lt;code&gt;ActiveRecord::RecordNotSaved&lt;/code&gt; error:&lt;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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memberships&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;membership_attributes&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;h2&gt;
  
  
  2. Handle Errors in Rails Transactions Properly
&lt;/h2&gt;

&lt;p&gt;When it comes to errors in transactions, there are a few rules that you should respect. By following these rules, you'll have readable and well-working code that will not create confusion among other developers or weird behavior that is hard to debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do Not Rescue from &lt;code&gt;ActiveRecord::StatementInvalid&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ActiveRecord::StatementInvalid&lt;/code&gt; is a special error raised when something on the database level goes wrong. Never rescue from this error. You should always be explicitly notified when something goes wrong with the database query.&lt;/p&gt;

&lt;p&gt;Avoid the following code:&lt;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;perform_action&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="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# perform transaction&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StatementInvalid&lt;/span&gt;
  &lt;span class="c1"&gt;# do something&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use the Rescue on the Right Level
&lt;/h3&gt;

&lt;p&gt;If you use rescue on the following level, you'll catch the error:&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&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;perform_action!&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;perform_another_action!&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;SomeError&lt;/span&gt;
  &lt;span class="c1"&gt;# rescue&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 transaction does not roll back because you caught the error. Let the error be raised and catch it outside the transaction block:&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;some_method&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&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;perform_action!&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;perform_another_action!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;SomeError&lt;/span&gt;
  &lt;span class="c1"&gt;# rescue&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above approach, the transaction rolls back in case of an error, and you catch the error. This is the right approach to catch errors raised inside transactions without overwriting transaction behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do Not Catch Generic Errors
&lt;/h3&gt;

&lt;p&gt;You should avoid catching generic errors like &lt;code&gt;StandardError&lt;/code&gt; or &lt;code&gt;ArgumentError&lt;/code&gt;. This is more like a general rule for readable and easily testable code, but it's worth mentioning.&lt;/p&gt;

&lt;p&gt;Catching these errors can make debugging harder, as other places in the code may raise the errors. This could silence some serious issues in your app that are not necessarily related to the place you rescue them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use ActiveRecord's Default Rollback Error Wisely
&lt;/h3&gt;

&lt;p&gt;ActiveRecord provides a particular error class that you can use inside a transaction to make a silent rollback. You roll back the transaction by raising the &lt;code&gt;ActiveRecord::Rollback&lt;/code&gt; error, but the error isn't raised outside, as happens with other errors. Keep this behavior in mind and use it wisely.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Know When to Avoid Using Transactions in Rails
&lt;/h2&gt;

&lt;p&gt;As with anything, you should not overuse transactions in your code. For example, a common mistake is to wrap only one query into your transaction. This does not make sense because, if the query doesn't succeed, there is no need to roll back anything.&lt;/p&gt;

&lt;p&gt;Another common mistake is to wrap code unrelated to your database call into a transaction. You should avoid such an approach, as the transaction will hold the connection unless the code inside the block executes. Limit the code inside the block to call only your database, if possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Understand the Disadvantages of Transactions
&lt;/h2&gt;

&lt;p&gt;Transactions help maintain data integrity inside a database, but you should also be aware of their disadvantages. For example, queries wrapped in a transaction block take more DB resources than single queries.&lt;/p&gt;

&lt;p&gt;Another drawback of using transactions is that it leads to more complex code. Transactions can make your code less readable when used incorrectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Use the Transaction Block in the Right Context
&lt;/h2&gt;

&lt;p&gt;You can use the transaction method when a class inherits from the &lt;code&gt;ActiveRecord&lt;/code&gt; class. This does not mean that the version you use does not matter. Although it might not matter from a functional perspective, it matters in terms of ensuring your code is readable.&lt;/p&gt;

&lt;p&gt;Three common versions use the transaction method:&lt;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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;
&lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;
&lt;span class="no"&gt;Model&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="nf"&gt;transaction&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you use many models and mix instance method invocations with classes inside a block, you should use &lt;code&gt;ActiveRecord::Base.transaction&lt;/code&gt;:&lt;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;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;membership&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Membership&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;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;LogService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_creation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;membership&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;If you deal mostly with code that belongs to a given model, invoke the transaction method on a 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="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="n"&gt;creation&lt;/span&gt;&lt;span class="err"&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;When you operate on a model instance, it makes sense to invoke the transaction method on the instance level:&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&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;make_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="err"&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;Of course, these rules are not official. They are just suggestions to make code more readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps: Review Transactions in Your Ruby on Rails Project
&lt;/h2&gt;

&lt;p&gt;I hope you've found these tips for working with transactions in Ruby on Rails useful.&lt;/p&gt;

&lt;p&gt;We've covered the importance of designing Rails transactions properly to improve data integrity and ensure that your processes perform without surprising side effects.&lt;/p&gt;

&lt;p&gt;However, a proper error handling policy isn't only beneficial when using transactions — it will also improve your whole codebase. Keep that in mind the next time you expect your code to throw some errors.&lt;/p&gt;

&lt;p&gt;Now is an excellent time to review transactions in your Ruby on Rails project design to avoid errors. Design for efficient and reliable communication with your database to make your application more stable.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

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

</description>
      <category>rails</category>
    </item>
    <item>
      <title>Import Maps Under the Hood in Rails 7</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Wed, 16 Mar 2022 13:15:12 +0000</pubDate>
      <link>https://forem.com/appsignal/import-maps-under-the-hood-in-rails-7-4752</link>
      <guid>https://forem.com/appsignal/import-maps-under-the-hood-in-rails-7-4752</guid>
      <description>&lt;p&gt;Import maps is the new feature in Rails 7 that allows us to say goodbye to Node.js and tools like Webpack. There's no need for bundling anymore. With this new mechanism, you can still manage your JavaScript libraries with a specific version. Instead of one big file, though, your application serves many small JavaScript files.&lt;/p&gt;

&lt;p&gt;It’s essential you know how import maps work to benefit from the newest version of Rails (but don’t worry, you can still use tools like Webpack instead). However, it’s also worth discovering what is happening under the hood. This way, you can better understand the journey from installing a JavaScript library in your project to serving its content to your users.&lt;/p&gt;

&lt;p&gt;This article will show you how to install, serve, and uninstall JavaScript libraries with import maps and what happens under the hood during each phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core of Import Maps
&lt;/h2&gt;

&lt;p&gt;The feature itself is not complicated. However, before I show you what happens inside the library, I would like to introduce the JSPM tool. JSPM is a shortcut for JavaScript Package Management. Thanks to this tool, you can load any NPM package inside the browser without extra tooling, and it will be fully optimized.&lt;/p&gt;

&lt;p&gt;Rails uses JSPM to serve JavaScript libraries in your application. You can either download the source files to the vendor directory or serve the code directly from the URL.&lt;/p&gt;

&lt;p&gt;For example, to access the jQuery library, you can call &lt;a href="https://api.jspm.io/generate?install=jquery"&gt;https://api.jspm.io/generate?install=jquery&lt;/a&gt; URL in your browser, and you will receive the URL to the minified source code in the JSON response. Of course, the service provides some more options for requests, but this knowledge is enough to understand how Rails cooperates with JSPM in the import maps library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Import Maps in Your Rails Project
&lt;/h2&gt;

&lt;p&gt;The import maps feature is available in Rails as a Ruby gem. If you generate a new project with Rails 7, it’s included in the Gemfile by default. You can add it to existing projects by executing the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle add importmap-rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have the gem installed, you have to generate the required files by using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/rails importmap:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What does this command do, exactly? First, it calls the install rake task included in the importmap namespace. The gem delivers the rake task. Inside the task, the standard rails rake task is executed:&lt;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="nb"&gt;system&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;RbConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ruby&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ./bin/rails app:template LOCATION=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"../install/install.rb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;__dir__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;install.rb&lt;/code&gt; file does a few things in the following order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adds helper to the layout in the head section&lt;/strong&gt; - if the &lt;code&gt;application.html.erb&lt;/code&gt; layout exists in your project, it adds the &lt;code&gt;javascript_importmap_tags&lt;/code&gt; line before the closing &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt; tag. This helper includes JavaScript libraries pinned by import maps. If your project does not include the standard layout file, it will render the information about the helper so you can add it by yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creates app/javascript/application.js file&lt;/strong&gt; - it adds the comment about import maps to let you know that there is a new place where you should define dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creates vendor/javascript directory&lt;/strong&gt; - this is where JavaScript libraries will be stored if they are not served directly via a URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updates sprockets manifest, if it exists&lt;/strong&gt; - sprockets needs to know about the JavaScript files placed inside the &lt;code&gt;vendor/javascript&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creates config/importmap.rb file&lt;/strong&gt; - this will contain the list of libraries used by Rails (something like package.json file).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copies bin/importmap file and sets the correct permission to execute it&lt;/strong&gt; - this file is used to pin and unpin specific libraries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As soon as this process finishes, you are ready to install JavaScript libraries using the &lt;code&gt;./bin/importmap&lt;/code&gt; file, and Rails is prepared to serve those files to your visitors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Libraries to Your Rails Project
&lt;/h2&gt;

&lt;p&gt;When installing a library with import maps, you have two options: you can either use the code directly from the CDN URL or download the file and serve it directly from your server.&lt;/p&gt;

&lt;p&gt;Let’s explore the CDN option first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using NPM Packages via JavaScript CDN’s
&lt;/h3&gt;

&lt;p&gt;It all starts with the pin command and the library name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/importmap pin jquery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You pass two arguments to the program located inside the &lt;code&gt;./bin/importmap&lt;/code&gt; file. &lt;code&gt;importmap&lt;/code&gt; is an executable file that loads a &lt;code&gt;config/application.rb&lt;/code&gt; file from your project and the import maps commands file.&lt;/p&gt;

&lt;p&gt;Import maps use the Thor gem to handle command line programs gently. The first argument, pin, is the command name. When you invoke it, the following things happen under the hood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Request to JSPM API is executed&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I mentioned before, the gem uses JSPM to get the contents of the package into our application. Therefore, the very first step is the request URL formation. Without any extra parameters passed to the pin command, the request URL is &lt;a href="https://api.jspm.io/generate"&gt;https://api.jspm.io/generate&lt;/a&gt; with the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;install&lt;/code&gt; - ["jquery"] - the param is an array because you can install multiple packages simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flattenScope&lt;/code&gt; - true - with this param set to &lt;code&gt;true&lt;/code&gt;, the returned import map format will be more straightforward without scopes, if possible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env&lt;/code&gt; - ["browser", "module", "production"] - this is the list of environment condition strings.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;provider&lt;/code&gt; - "jspm" - besides JSPM, Skypack, JSdelivr, and Unpkg providers are available to use as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try accessing &lt;a href="https://api.jspm.io/generate?install=jquery&amp;amp;flattenScope=true"&gt;https://api.jspm.io/generate?install=jquery&amp;amp;flattenScope=true&lt;/a&gt; in your browser, and you will get a simple JSON response with some simple attributes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Response from JSPM is parsed&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Parsing is a very straightforward phase. As you saw, the response is simple, and all we need is the library name and CDN URL. The &lt;code&gt;Packager&lt;/code&gt; class is responsible for parsing the response in the import map library. Instead of returning the attributes, it returns a complete line that you can use directly in the config:&lt;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="sx"&gt;%(pin "#{package}", to: "#{url}")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are not yet familiar with &lt;code&gt;%()&lt;/code&gt;, don’t worry, as it works almost the same as the following:&lt;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="s2"&gt;"pin &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, to: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference is that the &lt;code&gt;%()&lt;/code&gt; notation escapes the double quotes automatically. The generated config line passes to another function that handles the config file update process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import map config is updated&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The last step is to update the config file with the pin definition. Because the previous pin definition can be present, the gem first verifies the config file and searches for the existing library definition. It’s simple to do with the regex:&lt;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="sr"&gt;/^pin "&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;".*$/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the pin definition for the given package is present, the gem replaces the line with the new definition. If the previous definition is not present, the gem adds a new line at the end of the &lt;code&gt;config/importmap.rb&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: When you use this method, keep in mind that this option is free, but one person supported by the community manages the JSPM servers. This situation could change in the future, so I advise you to store libraries locally if you plan to use import maps with production-ready applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Downloading NPM Packages
&lt;/h3&gt;

&lt;p&gt;If you don’t want to use source code from a CDN URL, you can download the library to the &lt;code&gt;vendor&lt;/code&gt; directory inside your application. In this case, the process is very similar to the case with CDN URLs. What's different is that the code is downloaded into a .js file before the config line is returned for an update.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The download process&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The download process consists of a few small steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The gem calls &lt;code&gt;FileUtils.mkdir_p&lt;/code&gt;, which creates the &lt;code&gt;vendor&lt;/code&gt; directory if it does not already exist.&lt;/li&gt;
&lt;li&gt;The gem calls &lt;code&gt;FileUtils.rm_rf&lt;/code&gt; to remove the previous package file if it exists.&lt;/li&gt;
&lt;li&gt;The gem saves JS code located under the JSPM-provided URL into the .js file and places it into the &lt;code&gt;vendor&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the gem cannot download the package’s contents, you will be notified by a proper error raised in your command line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Config line generation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the package is named the same as the file with the package source, the config line is straightforward:&lt;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="sx"&gt;%(pin "#{package}" # #{version})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise, the import map needs to map the package name to the file directly:&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="sx"&gt;%(pin "#{package}", to: "#{filename}" # #{version})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the config line is formatted and returned, the config is updated with the package definition (as described in the CDN URL version above).&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Pinned Packages in Your Rails App
&lt;/h2&gt;

&lt;p&gt;The gem extensively uses the Rails engine mechanism to deliver features. The &lt;code&gt;Importmap::Engine&lt;/code&gt; defines a bunch of initializers that perform the configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Import Pinned Packages from config/importmap.rb
&lt;/h3&gt;

&lt;p&gt;The configuration lines inside the config/importmap.rb file are replaced with references inside the application. First, the gem collects all definitions into a hash where the &lt;code&gt;Struct&lt;/code&gt; object represents each definition to make it easier to access specific attributes.&lt;/p&gt;

&lt;p&gt;You can call &lt;code&gt;Rails.application.importmap.packages&lt;/code&gt; to access the hash with all definitions inside the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Including References to Packages inside Views
&lt;/h3&gt;

&lt;p&gt;When pinned packages are imported, you can include them in your views to provide the code. The gem provides a &lt;code&gt;javascript_importmap_tags&lt;/code&gt; helper, which you can simply render in your layout. It uses &lt;code&gt;Rails.application.importmap&lt;/code&gt; to generate JSON output for all pinned libraries, and with the usage of &lt;code&gt;asset_helper&lt;/code&gt; in Rails, it provides the correct paths to the libraries.&lt;/p&gt;

&lt;p&gt;After adding the following line to your &lt;code&gt;head&lt;/code&gt; section of the layout:&lt;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="na"&gt;javascript_importmap_tags&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;You will get the following output (assuming that you are only using jQuery):&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;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"importmap"&lt;/span&gt; &lt;span class="na"&gt;data-turbo-track=&lt;/span&gt;&lt;span class="s"&gt;"reload"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;imports&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/assets/application-37f365cbecf1fa2810a8303f4b6571676fa1f9c56c248528bc14ddb857531b95.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jquery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://ga.jspm.io/npm:jquery@3.6.0/dist/jquery.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Import Maps by Browser
&lt;/h3&gt;

&lt;p&gt;From that moment on, the browser handles the rest. If you are not familiar with the &lt;code&gt;importmap&lt;/code&gt; script type, let me explain it quickly.&lt;/p&gt;

&lt;p&gt;Import map simply controls what is fetched when you use the following statement inside your JavaScript code:&lt;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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jquery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Removing Pinned Packages
&lt;/h2&gt;

&lt;p&gt;To remove a pinned package, you have to execute the unpin command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/importmap unpin react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don’t have to pass the &lt;code&gt;--download&lt;/code&gt; parameter because the gem will automatically delete any files related to the package. The removal process consists of three steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Request to the JSPM API&lt;/strong&gt; - the same request is executed when the package is added. The gem does this to get up-to-date package information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Removal of the existing package files&lt;/strong&gt; - the gem uses &lt;code&gt;FileUtils.rm_rf&lt;/code&gt; to remove all related files even if they are placed in directories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove the config lines related to the package&lt;/strong&gt; - with the usage of &lt;code&gt;File.readlines&lt;/code&gt;, the gem loads all lines from the config file, selects those that do not contain anything related to the removed package and saves them again in the config file. Simple as that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also unpin multiple packages at once; just add their names after the &lt;code&gt;unpin&lt;/code&gt; argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;We've reached the end of this short, yet valuable, journey. You now know that import maps is just a different way of loading JavaScript libraries in your web application. Instead of one big bundled file, you serve multiple smaller files that are easy to cache and control.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;importmap-rails&lt;/code&gt; gem, it’s easy to adapt import maps in your project. The gem ships with a simple configuration file and command-line interface to install and remove packages using the JSPM.&lt;/p&gt;

&lt;p&gt;Should you use import maps in your next Rails project? As always, it depends. The good thing is that you are not limited to import maps — you can always switch between it, Webpack, and similar, more complex tools.&lt;/p&gt;

&lt;p&gt;Thanks for reading and happy coding!&lt;/p&gt;

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

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Solution for those who like Action Cable but hate writing JavaScript</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Tue, 21 Jul 2020 13:02:02 +0000</pubDate>
      <link>https://forem.com/paweldabrowski/solution-for-those-who-like-action-cable-but-hate-writing-javascript-3b6o</link>
      <guid>https://forem.com/paweldabrowski/solution-for-those-who-like-action-cable-but-hate-writing-javascript-3b6o</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cable Ready&lt;/strong&gt; is a great addition for Action Cable, especially if you don't like to write JavaScript code to interact with your website's DOM in a real-time. It helps us to quickly write real-time applications that work out of the box.&lt;/p&gt;

&lt;p&gt;To demonstrate the features that the gem provides, we will build a simple chat where new messages will appear on the website without reloading. We will start with creating a brand-new Rails application, creating a little code, adding the &lt;strong&gt;Cable Ready&lt;/strong&gt; gem, and making things real-time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;At the end of the article, we will have fully working simple chat:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A_86o0MQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://s3-us-west-1.amazonaws.com/pdabrowski.com/articles/cable-ready-rails/cableready.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A_86o0MQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://s3-us-west-1.amazonaws.com/pdabrowski.com/articles/cable-ready-rails/cableready.gif" alt="Real-time chat example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating example application
&lt;/h2&gt;

&lt;p&gt;Our application will consist of only one model: &lt;code&gt;Message&lt;/code&gt;. The model will have the following columns: &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt;. The main purpose of the application is to let guests to simply write messages on the chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application sekeleton
&lt;/h3&gt;

&lt;p&gt;We will use Ruby &lt;code&gt;2.7.0&lt;/code&gt; and Rails in version &lt;code&gt;6.0.3.2&lt;/code&gt; . Since we don't need anything fancy on the database side, we will use &lt;code&gt;SQLite&lt;/code&gt; as a database engine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Model
&lt;/h3&gt;

&lt;p&gt;As mentioned before, we would need one model - &lt;code&gt;Message&lt;/code&gt; where we will save the guest username along with the message that will appear in the chat. Let's create it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="ss"&gt;:string&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="ss"&gt;:text&lt;/span&gt;
&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:setup&lt;/span&gt;
&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="ss"&gt;:migrate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Controller
&lt;/h3&gt;

&lt;p&gt;We will need a controller to display messages and save new messages. Let's create one and save the following code into &lt;code&gt;app/controllers/messages_controller.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;MessagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at DESC'&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;create&lt;/span&gt;
    &lt;span class="no"&gt;Message&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;message_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="ss"&gt;:messages&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;message_params&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:message&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&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;update routes in &lt;code&gt;config/routes.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;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;resources&lt;/span&gt; &lt;span class="ss"&gt;:messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="sx"&gt;%i[index create]&lt;/span&gt;
  &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'messages#index'&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 create &lt;code&gt;app/views/messages/index.html.erb&lt;/code&gt; view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Current&lt;/span&gt; &lt;span class="ss"&gt;messages:

&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% @messages.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;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
    &amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= message.username %&amp;gt;: &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
  &amp;lt;% end %&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&amp;gt;

&amp;lt;h2&amp;gt;Add new message:&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= form_for(@message) do |f| %&amp;gt;
  &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_field&lt;/span&gt; &lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;placeholder: &lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= f.text_area :body, placeholder: 'message' %&amp;gt;
  &amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt; &lt;span class="s1"&gt;'Send'&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% end &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now our "application" works but looks terrible and page is reloading each time we hit the &lt;code&gt;Send&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---4kMkn1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s3-us-west-1.amazonaws.com/pdabrowski.com/articles/cable-ready-rails/cableready1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---4kMkn1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s3-us-west-1.amazonaws.com/pdabrowski.com/articles/cable-ready-rails/cableready1.png" alt="Real-time chat first version"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding proper styles to the application
&lt;/h2&gt;

&lt;p&gt;Before we add Cable Ready to our application, let's refactor the look a little bit so it looks more like a web chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bootstrap installation
&lt;/h3&gt;

&lt;p&gt;We will use the Bootstrap framework to save tons of time and adding nicely look to our simple application. The installation process is simple and consists of two steps. The first one is to install the library using yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;yarn&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="n"&gt;bootstrap&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="mf"&gt;4.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and the second one is to load styles. Update &lt;code&gt;app/assets/stylesheets/application.css&lt;/code&gt; and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="n"&gt;bootstrap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Updating styles of messages list and new message form
&lt;/h3&gt;

&lt;p&gt;Make sure that your &lt;code&gt;app/views/layouts/application.html.erb&lt;/code&gt; looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="no"&gt;DOCTYPE&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Chat&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/title&amp;gt;
    &amp;lt;%= csrf_meta_tags %&amp;gt;
    &amp;lt;%= csp_meta_tag %&amp;gt;
    &amp;lt;%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %&amp;gt;
    &amp;lt;%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %&amp;gt;
  &amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"flex-md-row p-3 px-md-4 mb-3 bg-white border-bottom box-shadow"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h5&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text-center"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Simple&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h5&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"container"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= yield %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;app/views/messages/index.html.erb&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"row justify-content-center"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'col-6'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"list-group"&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"messages"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;% @messages.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;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="sx"&gt;%&amp;gt;
        &amp;lt;a href="#" class="list-group-item list-group-item-action"&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mb-1"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= message.body %&amp;gt;&amp;lt;/p&amp;gt;
          &amp;lt;small class=&lt;/span&gt;&lt;span class="s2"&gt;"text-muted"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= message.username %&amp;gt;&amp;lt;/small&amp;gt;
        &amp;lt;/a&amp;gt;
      &amp;lt;% end %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&lt;/span&gt;&lt;span class="s2"&gt;"row justify-content-center"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'col-6'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;hr&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mt-3 mb-3"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Add&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="ss"&gt;:&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= form_for(@message) do |f| %&amp;gt;
      &amp;lt;div class=&lt;/span&gt;&lt;span class="s2"&gt;"form-group"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&amp;gt;
        &amp;lt;%= f.text_field :username, placeholder: 'username', class: 'form-control' %&amp;gt;
      &amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"form-group"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&amp;gt;
        &amp;lt;%= f.text_area :body, placeholder: 'message', class: 'form-control' %&amp;gt;
      &amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= f.submit 'Send', class: 'btn btn-primary' %&amp;gt;
    &amp;lt;% end %&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now our application looks way better:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--itG9hBku--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s3-us-west-1.amazonaws.com/pdabrowski.com/articles/cable-ready-rails/cableready2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--itG9hBku--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s3-us-west-1.amazonaws.com/pdabrowski.com/articles/cable-ready-rails/cableready2.png" alt="Real-time chat updated version"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making our chat real-time
&lt;/h2&gt;

&lt;p&gt;It's time to update our code so new chat messages will appear without reloading the whole page. In the first step, we will update the form so the request will be sent in the background via AJAX, not as the normal POST request with redirection. In the second step, we will finally add ActionCable along with CableReady to make real-time updates.&lt;/p&gt;

&lt;p&gt;Continue reading on &lt;a href="https://pdabrowski.com/articles/cable-ready-with-action-cable"&gt;https://pdabrowski.com/articles/cable-ready-with-action-cable&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>javascript</category>
      <category>rails</category>
    </item>
    <item>
      <title>Value objects - a complete guide to Ruby code that is testable, readable and simple</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Tue, 14 Jul 2020 11:48:17 +0000</pubDate>
      <link>https://forem.com/paweldabrowski/value-objects-a-complete-guide-to-ruby-code-that-is-testable-readable-and-simple-352b</link>
      <guid>https://forem.com/paweldabrowski/value-objects-a-complete-guide-to-ruby-code-that-is-testable-readable-and-simple-352b</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Regardless of the type of architecture do you like the most in Rails, you will find value objects design pattern useful and, which is just as important, easy to maintain, implement and test. The pattern itself doesn't introduce any unneeded level of abstraction and aims to make your code more isolated, easier to understand and less complicated.&lt;/p&gt;

&lt;h3&gt;
  
  
  A quick look at the pattern's name
&lt;/h3&gt;

&lt;p&gt;Let's just quickly analyze its name before we move on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;value&lt;/strong&gt; - in your application there are many classes, some of them are complex which means that they have a lot of lines of code or performs many actions and some of them are simple which means the opposite. This design pattern focuses on providing &lt;strong&gt;values&lt;/strong&gt; that's why it is so simple - it don't care about connecting to the database or external APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;object&lt;/strong&gt; - you know what is an object in objected programming and similarly, &lt;strong&gt;value object&lt;/strong&gt; is an object that provides some attributes and accepts some initialization params.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The definition
&lt;/h3&gt;

&lt;p&gt;There is no need to reinvent the wheel so I will use the definition created by Martin Fowler:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A small simple object, like money or a date range, whose equality isn't based on identity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Won't it be beautiful if a part of our app would be composed of small and simple objects? Sounds like a heaven and we can easily get a piece of this haven and put it into our app. Let's see how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands on keyboard
&lt;/h2&gt;

&lt;p&gt;I would like to discuss the advantages of using value objects and rules for writing good implementation but before I will do it, let's take a quick look at some examples of value objects to give you a better understanding of the whole concept.&lt;/p&gt;

&lt;h3&gt;
  
  
  Colors - equality comparsion example
&lt;/h3&gt;

&lt;p&gt;If you are using colors inside your app, you would probably end up with the following representation of a color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;Color&lt;/span&gt;
  &lt;span class="no"&gt;CSS_REPRESENTATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'black'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'#000000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'white'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'#ffffff'&lt;/span&gt;
  &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&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="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&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;css_code&lt;/span&gt;
    &lt;span class="no"&gt;CSS_REPRESENTATION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:name&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 implementation is self-explainable so we won't focus on going through the lines. Now consider the following case: two users picked up the same color and you want to compare the colors and when they are matching, perform some action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user_a_color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Color&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="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user_b_color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Color&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="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_a_color&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_b_color&lt;/span&gt;
  &lt;span class="c1"&gt;# perform some action&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With the current implementation, the action would never be performed because now objects are compared using their identity and its different for every new object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user_a_color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 70324226484560&lt;/span&gt;
&lt;span class="n"&gt;user_b_color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 70324226449560&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Remember Martin's Fowler words? A value object is compared not by the identity but with its attributes. Taking this into account we can say that our &lt;code&gt;Color&lt;/code&gt; class is not a true value object. Let's change that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;Color&lt;/span&gt;
  &lt;span class="no"&gt;CSS_REPRESENTATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'black'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'#000000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'white'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'#ffffff'&lt;/span&gt;
  &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&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="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&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;css_code&lt;/span&gt;
    &lt;span class="no"&gt;CSS_REPRESENTATION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vi"&gt;@name&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;==&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:name&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 the compare action makes sense as we compare not object ids but color names so the same color names will be always equal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Color&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="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Color&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="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class="no"&gt;Color&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="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Color&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="s1"&gt;'white'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With the above example we have just learned about the first fundamental of value object - &lt;strong&gt;its equality is not based on identity.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Price - duck typing example
&lt;/h3&gt;

&lt;p&gt;Another very common but yet meaningful example of a value object is a price object. Let's assume that you have a shop application and separated object for a price:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;Price&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;value&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="vi"&gt;@currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:currency&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 want to display the price to the end user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;juice_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Price&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;value: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;currency: &lt;/span&gt;&lt;span class="s1"&gt;'USD'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Price of juice is: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;juice_price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;juice_price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currency&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;the goal is achieved but it doesn't look good. Another feature often seen in value object is &lt;strong&gt;duck typing&lt;/strong&gt; and this example is a perfect case where we can take advantage of it. In simple words &lt;strong&gt;duck typing&lt;/strong&gt; means that the object behaves like a different object if it implements a given method - in the above example, our price object should behave like a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;Price&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;value&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="vi"&gt;@currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currency&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;to_s&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:currency&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 we can update our snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;juice_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Price&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;value: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;currency: &lt;/span&gt;&lt;span class="s1"&gt;'USD'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Price of juice is: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;juice_price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We have just discovered another fundamental of value objects and as you see we still only return values and we keep the object very simple and testable.&lt;/p&gt;

&lt;p&gt;Read the rest of the article at &lt;a href="https://pdabrowski.com/articles/rails-design-patterns-value-object"&gt;https://pdabrowski.com/articles/rails-design-patterns-value-object&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Ruby on Rails - list validators for a given model</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Fri, 03 Jan 2020 14:28:34 +0000</pubDate>
      <link>https://forem.com/paweldabrowski/ruby-on-rails-list-validators-for-a-given-model-2dee</link>
      <guid>https://forem.com/paweldabrowski/ruby-on-rails-list-validators-for-a-given-model-2dee</guid>
      <description>&lt;p&gt;You can easily list given model validators by calling &lt;code&gt;validators&lt;/code&gt; method on the class name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validators&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; array of validators&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you define presence validator on your &lt;code&gt;User&lt;/code&gt; model:&lt;br&gt;
&lt;/p&gt;

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



&lt;p&gt;you can access validator attributes the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;email_presence_validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
&lt;span class="n"&gt;email_presence_validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; ActiveRecord::Validations::PresenceValidator&lt;/span&gt;
&lt;span class="n"&gt;email_presence_validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [:email]&lt;/span&gt;
&lt;span class="n"&gt;email_presence_validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; {}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;options&lt;/code&gt; method returns options like &lt;code&gt;:if&lt;/code&gt; or &lt;code&gt;:unless&lt;/code&gt; conditions that are passed to the validator definition as extra arguments. Experiment with other types of validations to see how it's working.&lt;/p&gt;

&lt;p&gt;Thank you for reading! Follow me on &lt;a href="https://twitter.com/pdabrowski6"&gt;Twitter&lt;/a&gt; to be up to date with the latest tips, articles, and videos about programming.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Call Rails helper method from another helper method</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Thu, 02 Jan 2020 11:36:39 +0000</pubDate>
      <link>https://forem.com/paweldabrowski/call-rails-helper-method-from-another-helper-method-4fn9</link>
      <guid>https://forem.com/paweldabrowski/call-rails-helper-method-from-another-helper-method-4fn9</guid>
      <description>&lt;p&gt;Since 4 version of Rails, helpers are loaded in the view so it is possible to call one helper method from another one without including helpers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ApplicationHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_method_from_application_helper&lt;/span&gt;
    &lt;span class="c1"&gt;# do something&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;AnotherHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_something&lt;/span&gt;
    &lt;span class="n"&gt;some_method_from_application_helper&lt;/span&gt; &lt;span class="c1"&gt;# will work&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;Thank you for reading! Follow me on &lt;a href="https://twitter.com/pdabrowski6"&gt;Twitter&lt;/a&gt; to be up to date with the latest tips, articles, and videos about programming.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Speed up development by adding bash aliases</title>
      <dc:creator>Paweł Dąbrowski</dc:creator>
      <pubDate>Wed, 01 Jan 2020 14:58:37 +0000</pubDate>
      <link>https://forem.com/paweldabrowski/speed-up-development-by-adding-bash-aliases-1407</link>
      <guid>https://forem.com/paweldabrowski/speed-up-development-by-adding-bash-aliases-1407</guid>
      <description>&lt;p&gt;Currently, I'm using two git accounts with separated keys, one for side projects and one for the company I'm working for. I have to switch to the second account each time I am using a new shell window and want to push changes using a different account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eval "$(ssh-agent -s)"
ssh-add ~/.ssh/company

git push origin something
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;it is quite annoying. However, it can be easily replaced with a simple bash alias with a meaningful name that you would always remember.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a bash alias
&lt;/h2&gt;

&lt;p&gt;Mainly, there are two user-level files which system run when a bash shell starts - &lt;code&gt;~/.bash_profile&lt;/code&gt; and &lt;code&gt;~/.bashrc&lt;/code&gt;. You have to edit one of them to add our alias. The alias format is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;alias_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'command1;command2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;in my case I have to run two commands so the alias looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;company_git&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'eval "$(ssh-agent -s)";ssh-add ~/.ssh/company'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now I can simply type &lt;code&gt;company_git&lt;/code&gt; in terminal each time I open a new shell window and I want to be ready to push some code to the company repositories. It's so simple but makes the development less frustrating.&lt;/p&gt;

&lt;p&gt;Thank you for reading! Follow me on &lt;a href="https://twitter.com/pdabrowski6"&gt;Twitter&lt;/a&gt; to be up to date with the latest tips, articles, and videos about programming.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>git</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
