<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adrian Marin ⛵️</title>
    <description>The latest articles on DEV Community by Adrian Marin ⛵️ (@adrianthedev).</description>
    <link>https://dev.to/adrianthedev</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%2F165585%2F55b6cff5-2963-4f09-b7df-939be3e7fff1.jpg</url>
      <title>DEV Community: Adrian Marin ⛵️</title>
      <link>https://dev.to/adrianthedev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adrianthedev"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Adrian Marin ⛵️</dc:creator>
      <pubDate>Wed, 13 Aug 2025 09:17:54 +0000</pubDate>
      <link>https://dev.to/adrianthedev/-1hb</link>
      <guid>https://dev.to/adrianthedev/-1hb</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/lucianghinda/more-than-code-the-friendships-youll-build-at-friendlyrb-589k" class="crayons-story__hidden-navigation-link"&gt;More Than Code: The Friendships You’ll Build at FriendlyRB&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/lucianghinda" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F907797%2Fcd901ef3-ad6b-4b2b-b33a-d533880aa3ef.jpg" alt="lucianghinda profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/lucianghinda" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Lucian Ghinda
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Lucian Ghinda
                
              
              &lt;div id="story-author-preview-content-2771575" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/lucianghinda" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F907797%2Fcd901ef3-ad6b-4b2b-b33a-d533880aa3ef.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Lucian Ghinda&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/lucianghinda/more-than-code-the-friendships-youll-build-at-friendlyrb-589k" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 13 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/lucianghinda/more-than-code-the-friendships-youll-build-at-friendlyrb-589k" id="article-link-2771575"&gt;
          More Than Code: The Friendships You’ll Build at FriendlyRB
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/lucianghinda/more-than-code-the-friendships-youll-build-at-friendlyrb-589k" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/lucianghinda/more-than-code-the-friendships-youll-build-at-friendlyrb-589k#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              1&lt;span class="hidden s:inline"&gt; comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            1 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Delegated Types and and building the ultimate Kanban Board in Rails</title>
      <dc:creator>Adrian Marin ⛵️</dc:creator>
      <pubDate>Tue, 09 Jul 2024 08:44:38 +0000</pubDate>
      <link>https://dev.to/adrianthedev/delegated-types-and-and-building-the-ultimate-kanban-board-in-rails-1jmn</link>
      <guid>https://dev.to/adrianthedev/delegated-types-and-and-building-the-ultimate-kanban-board-in-rails-1jmn</guid>
      <description>&lt;p&gt;We're getting ready to work on our Kanban Board feature. It's exciting as we haven't started a "big" feature in a while.&lt;/p&gt;

&lt;p&gt;As we embark on this, we began to think about how to model the database around it. We have a couple of constraints within Avo's context, which makes it a bit more challenging.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. We don't own the records/models that sit on the board
&lt;/h4&gt;

&lt;p&gt;This is the ethos of Avo; the user can have Users, Products, ToDos, and any other kind of models added to the board. We don't own those models and can't dictate the design decisions around them.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. We don't want to enforce database restrictions (or have as few as possible)
&lt;/h4&gt;

&lt;p&gt;Another big feature of Avo is that you configure it with code and not by database rows. This is powerful because all your configuration is very portable across all environments. This means we don't ship any migrations with Avo and, again, don't impose database restrictions on you.&lt;/p&gt;

&lt;p&gt;So how do we keep track of which item is on which board/column?&lt;/p&gt;

&lt;h4&gt;
  
  
  3. We want multi-item-type boards
&lt;/h4&gt;

&lt;p&gt;We'd love to support boards that handle multiple item types, meaning you could have a User, a Product, and a To-Do on the same board and work with all of them as if they were the same item.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Make it as configurable as possible
&lt;/h4&gt;

&lt;p&gt;We want this to be the ultimate Kanban board feature out there. Maybe not from the initial iteration, but still be able to easily add features as we progress.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Do We Do It?
&lt;/h3&gt;

&lt;p&gt;It's an interesting challenge, I think.&lt;/p&gt;

&lt;h4&gt;
  
  
  Initial Iteration
&lt;/h4&gt;

&lt;p&gt;We first thought about the simplest use case. We would require the user to add a column to each record type in the database to indicate which column that record is placed in.&lt;/p&gt;

&lt;p&gt;That quickly becomes problematic if you want that record in multiple columns.&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="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="o"&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;kanban_column&lt;/span&gt; &lt;span class="c1"&gt;# add this to hold the board it is on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This starts to look like &lt;a href="https://guides.rubyonrails.org/association_basics.html#single-table-inheritance-sti" rel="noopener noreferrer"&gt;Single Table Inheritance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You also need to hold the position on the board. Adding another database column? What if you need to add another property? It starts getting messy really quickly.&lt;/p&gt;

&lt;h4&gt;
  
  
  Second Iteration
&lt;/h4&gt;

&lt;p&gt;Then, we thought about holding the position and board information in a different table. This is great as it doesn't impact the user's database modeling. We can "do what we want there" and not bother the user.&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="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;

&lt;span class="no"&gt;KanbanItem&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;record_type&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"User"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;record_id&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"In progress"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# which column&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# position in the column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is already better. But what does it look like? That's right! It looks like &lt;a href="https://guides.rubyonrails.org/association_basics.html#delegated-types" rel="noopener noreferrer"&gt;Delegated Types&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Delegated Types are a great abstraction for holding common data for multiple types of models. They have great heuristics and are proven and tested.&lt;/p&gt;

&lt;p&gt;It checks all the boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We don't need to apply any updates to the user's model database structure.&lt;/li&gt;
&lt;li&gt;We can have the same record sit on multiple boards.&lt;/li&gt;
&lt;li&gt;This future-proofs this feature so we can customize it as we want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We still have a few things to look into and test out, but the "meat" of the feature is there!&lt;/p&gt;

&lt;p&gt;How would you build this feature?&lt;/p&gt;




&lt;p&gt;Originally posted on &lt;a href="https://avohq.io/blog/delegated-types-and-and-building-the-ultimate-kanban-board-in-rails" rel="noopener noreferrer"&gt;avohq.io&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>kanban</category>
      <category>devtools</category>
      <category>database</category>
    </item>
    <item>
      <title>How to bundle assets in a Rails engine</title>
      <dc:creator>Adrian Marin ⛵️</dc:creator>
      <pubDate>Tue, 10 Jan 2023 18:29:35 +0000</pubDate>
      <link>https://dev.to/adrianthedev/how-to-bundle-assets-in-a-rails-engine-ilp</link>
      <guid>https://dev.to/adrianthedev/how-to-bundle-assets-in-a-rails-engine-ilp</guid>
      <description>&lt;p&gt;During Rails' lifetime, we had a lot of ways to load, parse, and process assets. The "recommended" way is to hook into the asset pipeline (whichever that is) and, on deployment, let the &lt;code&gt;assets:precompile&lt;/code&gt; task to take over and... compile them so they can be used in your app.&lt;/p&gt;

&lt;p&gt;But, if you built an engine that you want to distribute to others, there might be a few things that might get in the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asset pipeline cons
&lt;/h3&gt;

&lt;p&gt;Let's say you're using &lt;code&gt;jsbundling-rails&lt;/code&gt; and/or &lt;code&gt;tailwindcss-rails&lt;/code&gt; with your distributable engine. To you, it's common to have all you need to compile those assets.&lt;br&gt;
You'll have Node.js and all other external dependencies installed and ready to compile everything together, but the parent app that's using your gem might not. They might use &lt;code&gt;importmaps,&lt;/code&gt; and they might not have all that Node.js infrastructure.&lt;/p&gt;

&lt;p&gt;When the user pushes the Rails app with your gem installed, they might get the gem without any assets (because they haven't been built yet), or they might even get a crash that sprockets (or propshaft) can't find those assets.&lt;/p&gt;

&lt;p&gt;But there's a better way!&lt;/p&gt;
&lt;h2&gt;
  
  
  Compile assets on publish-time
&lt;/h2&gt;

&lt;p&gt;One approach we had with &lt;a href="https://github.com/avo-hq/avo" rel="noopener noreferrer"&gt;avo-hq/avo&lt;/a&gt; was to precompile the assets before we publish a new version and serve them as static assets to the parent app.&lt;/p&gt;
&lt;h3&gt;
  
  
  How do we do that?
&lt;/h3&gt;

&lt;p&gt;The overview is this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up build commands&lt;/li&gt;
&lt;li&gt;Provide a way for the parent app to reach those assets&lt;/li&gt;
&lt;li&gt;Run all the compilation commands&lt;/li&gt;
&lt;li&gt;Package the gem up with those static assets&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  1. Set up build commands
&lt;/h2&gt;

&lt;p&gt;You first install your asset handlers as you need them for your project. They can be anything from &lt;a href="https://github.com/rails/jsbundling-rails" rel="noopener noreferrer"&gt;rails/jsbundling-rails&lt;/a&gt; and &lt;a href="https://github.com/rails/tailwindcss-rails" rel="noopener noreferrer"&gt;rails/tailwindcss-rails&lt;/a&gt; to webpacker or something custom.&lt;/p&gt;

&lt;p&gt;Add some commands to build up those assets. &lt;br&gt;
We have the following scripts inside the &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prod:build:js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esbuild app/javascript/*.js --bundle --sourcemap --minify --outdir=public/avo-assets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prod:build:css"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tailwindcss -i ./app/assets/stylesheets/avo.base.css -o ./public/avo-assets/avo.base.css --postcss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They take the source JS and CSS files, compile them, and move them to a &lt;code&gt;public/assets&lt;/code&gt; directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Provide a way for the parent app to reach those assets
&lt;/h2&gt;

&lt;p&gt;When you ship your gem, that &lt;code&gt;public&lt;/code&gt; directory will not be public at all. It will be hosted somewhere hidden on that machine, so the browser will not have a way to reach them.&lt;/p&gt;

&lt;p&gt;Let's use a &lt;code&gt;Rack::Static&lt;/code&gt; middleware to the &lt;code&gt;engine.rb&lt;/code&gt; file.&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;Avo&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app_middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&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;Static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;urls: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/avo-assets"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;root: &lt;/span&gt;&lt;span class="no"&gt;Avo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the parent app will re-route all the traffic from &lt;code&gt;/avo-assets&lt;/code&gt; to that "hidden" directory where our compiled assets are.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Run all the compilation commands
&lt;/h2&gt;

&lt;p&gt;When we're ready to ship our gem to RubyGems (or any other gems server), we should run the compilation commands to ensure all the JS and CSS files are compiled, minified, and packaged up how we need them to be in production.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Package the gem up with those static assets
&lt;/h2&gt;

&lt;p&gt;We need to instruct the &lt;code&gt;gem&lt;/code&gt; utility to add the compiled files to the packaged gem.&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;Gem&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Specification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&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;spec&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"avo"&lt;/span&gt;
  &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Avo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VERSION&lt;/span&gt;
  &lt;span class="c1"&gt;# more spec properties&lt;/span&gt;

  &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"{bin,app,config,db,lib,public}/**/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MIT-LICENSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Rakefile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"README.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"avo.gemspec"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Gemfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Gemfile.lock"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_dependency&lt;/span&gt; &lt;span class="s2"&gt;"activerecord"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 6.0"&lt;/span&gt;
  &lt;span class="c1"&gt;# other dependencies here&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 &lt;code&gt;spec.files&lt;/code&gt; property knows which files should be bundled up. Finally, you'll see the &lt;code&gt;Dir["{public}/**/*"]&lt;/code&gt; will add the whole &lt;code&gt;public&lt;/code&gt; directory to the gem, including the &lt;code&gt;avo-assets&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;bundle exec rails build&lt;/code&gt; to package everything up and profit 🙌&lt;/p&gt;

&lt;h3&gt;
  
  
  All you need to know about Rails engines
&lt;/h3&gt;

&lt;p&gt;This post is part of a series I'm writing to share some of my learnings while building my own distributed engine, Avo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://avohq.io/rails-engines" rel="noopener noreferrer"&gt;All you need to know about Rails Engines&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Why Successful Startups Use MVPs</title>
      <dc:creator>Adrian Marin ⛵️</dc:creator>
      <pubDate>Sun, 31 Jul 2022 14:15:48 +0000</pubDate>
      <link>https://dev.to/avo-cool/why-successful-startups-use-mvps-392e</link>
      <guid>https://dev.to/avo-cool/why-successful-startups-use-mvps-392e</guid>
      <description>&lt;p&gt;Website building platforms like WordPress or Wix undeniably revolutionized the internet.&lt;/p&gt;

&lt;p&gt;Why? For the same reason, a band like Green Day draws a festival-sized crowd in comparison to a jazz ensemble formed of professional musicians who studied music theory their entire lives. It is more easily accessible to the masses.&lt;/p&gt;

&lt;p&gt;As long as the majority of people can understand the inner workings of a certain process - spanning from relating to the 3 chords an artist plays in front of them to build their own minimalistic website - they will be drawn to what they can achieve on their own. Relying on an expert in any field of work implies a bigger budget and a schedule you don't always call the shots for.&lt;/p&gt;

&lt;p&gt;In the world of companies, a startup is the equivalent of an individual that starts something from scratch. They will need a lot of resources to evolve and they should seek any support they can get. The biggest support always comes in the form of cost- and time-efficient solutions for a business. This is the category an MVP falls under.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are MVPs?
&lt;/h2&gt;

&lt;p&gt;Dating back not too long ago - 2001 - the acronym has been coined, which stands for Minimum Viable Product. Before a product starts generating profit, its developers need to understand its market extremely well. They need to detect the most significant problems clients face and solve them. Determining what makes the problem significant is also what defines the product.&lt;/p&gt;

&lt;p&gt;By using an MVP, developers can concentrate directly on the said problem, without pouring too many resources into a more advanced form of the product. It is more important to see if the product solves the customer's problem first. Then comes the consolidation.&lt;/p&gt;

&lt;p&gt;For a business, efficiency is key. It is much more efficient for product development to take its course in tandem with customer research. This way, both issues, and solutions can be handled in real-time.&lt;/p&gt;

&lt;p&gt;More precisely, an MVP is the skeleton of a human body, the outline of an essay, and the stage setup of a concert. It is the bare frame that bears the least number of features, focusing on only solving the most important one it was designed to solve. Since solving that issue is the focal point of an MVP, it needs to function perfectly in order to not create additional problems along the way while the infrastructure is still in its fetal stage.&lt;/p&gt;

&lt;p&gt;Eric Ries - author of the book "&lt;a href="http://theleanstartup.com/" rel="noopener noreferrer"&gt;The Lean Startup&lt;/a&gt;" - defines an MVP as the stage of a product in which as much client data collection is permitted, by undergoing the least amount of effort. It is the stage that helps a team understand how their product can start making money from that point on.&lt;br&gt;
Some common MVP practices&lt;br&gt;
To thoroughly understand what minimum and viable means when it comes to a product, it is helpful to look at some common practices that led MVPs to become successful. Six such practices come to mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Landing page&lt;/li&gt;
&lt;li&gt;Media (video or animation)&lt;/li&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;Piecemeal&lt;/li&gt;
&lt;li&gt;Illusion&lt;/li&gt;
&lt;li&gt;Concierge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any product, regardless of its level of functionality, requires a brief description of what it does. Ideally, this information should fit on a &lt;strong&gt;landing page&lt;/strong&gt;. Its main focus is to shine some visibility on the product. A customer's curiosity brings value to the product, which is what a founder should be initially seeking and feeding off of, to know where to go next. In this case, value is measured by emotional responses, meaning customers found some solace, thus making the problem more easily identifiable.&lt;/p&gt;

&lt;p&gt;People have different learning methods. They are stimulated either visually, sonically, or creatively. If reading about a product still does not help them make the connections they need to understand a product, a &lt;strong&gt;video&lt;/strong&gt; or an &lt;strong&gt;animation&lt;/strong&gt; would be more useful. Brief as it might be, the essence of a product's function can be extracted and transformed - or edited - in moving images. After the emotional responses have been triggered, slap a call to action at the end of the video, and boom - you got yourself what is known as an opportunity in the corporate world.&lt;/p&gt;

&lt;p&gt;It may seem old-fashioned but &lt;strong&gt;email campaigns&lt;/strong&gt; are still effective. Granted, you need to build an initial contact list, but it only takes sending one email to test the waters and analyze the recipients' responses.&lt;br&gt;
It would be nice to have some sort of demo version for everything we buy but are initially unsure of. Like testing a melon at a market, or taking a new car for a ride from a dealership lot. In terms of using this piecemeal approach for an MVP, a founder can extract what they believe to be the most important and useful features of a product that still make it viable on its own, limited as it is.&lt;/p&gt;

&lt;p&gt;With a little pizzazz to collect user responses and create intent to buy, the development team doesn't have to go out of their way to make a functioning product that seems as easy to use as a demo version, but is really all there is to get the job done. In the beginning, this method requires a more "hand-made" tactic before the team can fully automate its functionality. Just to get the pulse and explore the potential buying power. Just until it gets too difficult to operate "manually".&lt;/p&gt;

&lt;p&gt;If you have an e-commerce platform on your hands, the &lt;strong&gt;illusion&lt;/strong&gt; (sometimes called &lt;strong&gt;Wizard of Oz&lt;/strong&gt;) approach is ideal. While your company does not physically carry the products on the page, customers still need to be drawn to purchase them from vendors. Your platform is the one that draws them in, and that ultimately generates traffic to your page.&lt;/p&gt;

&lt;p&gt;Your illusion game needs to be top notch…meaning you have to make the customers' buying experience one that they cannot find anywhere else. This can only be achieved by trial and error, constantly changing the way the platform looks and taking notes on when there's an interest growth spike.&lt;/p&gt;

&lt;p&gt;Similar to the piecemeal approach, the &lt;strong&gt;concierge&lt;/strong&gt; approach relies on manually operating a process before turning the operation into a product. In this case, manual operation refers to personal interaction with customers. Building trust the old fashioned way! After customers develop "feelings" for the way your company treated them, they will most likely stay on board after you automate the services as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Can Startups Benefit from MVPs?
&lt;/h2&gt;

&lt;p&gt;Does a customer really need your product? An MVP can provide the best answer.&lt;/p&gt;

&lt;p&gt;There is no need to waste resources, effort, and budget before finding the answer out. A low-code version of the product's main functionality is all the customer needs to decide if they need it, and for you to decide whether you found your niche. This version can actually demonstrate your product's ability to the customer which generates the most important criteria for a business owner: &lt;strong&gt;validation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The customer will explore the product thoroughly in order to test its validity. Although an MVP is a very basic version of the final result, it needs to work in all of the &lt;strong&gt;iterations&lt;/strong&gt; that the customer is able to explore. This puts more pressure on the development team, but will take an insurmountable weight off their shoulders in the long run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer feedback&lt;/strong&gt; is crucial but not immeasurably important. Development team and management should not cling on to every single critique or feedback, but instead draw a conclusion in regards to what direction to take. After all, the big aim is to head towards product-market fit.&lt;/p&gt;

&lt;p&gt;If your product is so innovative that it is unprecedented on the market, you may have struck gold! But as any pirate or miner will tell you, gold must be kept well-protected. Having first entry to market, your product may be so revolutionary that the market is not yet educated or prepared for the benefits it can generate. By using an MVP you can also gradually infiltrate the concept on the market before competitors find a better way to display the value of the product to potential customers.&lt;/p&gt;

&lt;p&gt;An MVP allows a startup to reduce the risk of failure by spending less resources such as time and money. After all, this is the very definition of &lt;strong&gt;effective investment management&lt;/strong&gt;. A smaller budget helps a team to focus on where the money is necessary and where it actually ends up helping. This implies simplifying demands, cutting out unnecessary tasks and expenses, and most importantly, being vigilant in getting an accurate pulse of the market.&lt;/p&gt;

&lt;p&gt;As difficult as it may be to fathom, there are solutions on the market that tick all the boxes above. Languages and frameworks that support an MVP for as long as needed before the application takes off. One such useful programming language is Ruby on Rails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby on Rails: Doing the Heavy-Lifting For You
&lt;/h2&gt;

&lt;p&gt;Two words that should titillate your interest: programmer productivity.&lt;/p&gt;

&lt;p&gt;When it comes to syntax, language, and afferent suite of tools, programmers sigh with relief knowing that they can build faster and worry less about basic code and database structures. Numerous Silicon Valley startups such as Airbnb, Shopify, Github, Twitter, Hulu, Dribble, Kickstarter, and even Tesla have used RoR to expand within the market.&lt;/p&gt;

&lt;p&gt;Instead of wasting your budget and your programming team's time for basic - but necessary - coding, by using Ruby on Rails they can get right to building the essence of your product, while you and your clients can observe the results much quicker and solve any issues on the fly.&lt;/p&gt;

&lt;p&gt;Similar to frameworks like Java, Python, and PHP, RoR uses a model-view-controller (&lt;strong&gt;MVC&lt;/strong&gt;) architectural pattern. The model refers to the logic of the application, the view represents the template files that result in the data's visual representation, and the controller is the connecting piece between the latter two. With the help of an MVC arch, the application logic is kept clean and applications become flexible.&lt;/p&gt;

&lt;p&gt;Aside from MVC, RoR also relies on convention-over-configuration (&lt;strong&gt;CoC&lt;/strong&gt;) and the Don't Repeat Yourself (&lt;strong&gt;DRY&lt;/strong&gt;) principle.&lt;/p&gt;

&lt;p&gt;CoC refers to frameworks setting "sensible defaults" so that programmers don't have to make every little decision, instead they get to focus more on building the actual application.&lt;/p&gt;

&lt;p&gt;DRY prevents programmers from using repetition in their codebase, which makes files and functions easier to maintain, debug, and most important of all…understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros
&lt;/h2&gt;

&lt;p&gt;It is important to note that an MVP is not a Beta version of a product. While Beta versions aid in finding bugs, user experience, stability, and reliability issues, they are meant for a group of tech savvy people known as Beta testers.&lt;/p&gt;

&lt;p&gt;An MVP relies on the viable aspect by being able to function well for normal customers. The aim is for the customers to appreciate the value and understand how the product can solve their problems. It is necessary for the product to work well and impress potential customers by solving their outstanding problem.&lt;/p&gt;

&lt;p&gt;So let's go over the pros of using Ruby on Rails one more time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes little time to develop&lt;/li&gt;
&lt;li&gt;Easy to read and understand&lt;/li&gt;
&lt;li&gt;Free to use&lt;/li&gt;
&lt;li&gt;Highly scalable&lt;/li&gt;
&lt;li&gt;Secure framework&lt;/li&gt;
&lt;li&gt;Helpful community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds perfect, but there has to be a dealbreaker, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Cons
&lt;/h2&gt;

&lt;p&gt;For startup CEOs, the &lt;strong&gt;cost&lt;/strong&gt; might be the main con. While RoR is free to use, the few programmers who can develop a solid base will charge 100K + per year.&lt;/p&gt;

&lt;p&gt;Even if you dispose of that kind of money, it is &lt;strong&gt;difficult to find&lt;/strong&gt; RoR programmers on every corner. Since it is not the most commonly used language and framework, there is quite a niche of programmers that can even take on RoR-based projects.&lt;/p&gt;

&lt;p&gt;Since these two aspects might be discouraging enough for a startup team to go through with using RoR, they may eventually &lt;strong&gt;slow down progress&lt;/strong&gt;. Paying copious amounts of money and wasting precious time on fixing bugs and not understanding the needs of the market entirely are not efficient uses of resources. During this time the competition might prosper, rendering your product inferior or even outdated by the time it is finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Overseeing and successfully managing all the activities a startup implies is harrowing enough. And although they are paid for their efforts, even programmers would much rather concentrate on programming the main function of a product, rather than waste time in the "pre-production" phase.&lt;/p&gt;

&lt;p&gt;Ideally, there should be a platform that is as easy to use as Wordpress or Wix. A platform that makes the process easy to understand and use for anyone. A platform from which you can choose RoR templates, features, resources, common actions (such as deleting a page or duplicating a page), an area where you can edit results, all the while the activity is transparent, easy to access for programmers, and works quickly and efficiently.&lt;/p&gt;

&lt;p&gt;Such a platform is &lt;a href="https://avohq.io" rel="noopener noreferrer"&gt;Avo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Avo essentially takes care of building everything else that is not the absolute core functionality of the product. By using Avo, you don't have to allocate big budgets to developers to build the boilerplate.&lt;/p&gt;

&lt;p&gt;You are better off using the fortune you save on basic infrastructure developing on other marketing practices to get your product in the hands of those that actually need it.&lt;/p&gt;

&lt;p&gt;While your programming team concentrates on the core functionality, here are the ways you will be ahead of schedule on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scouting the market&lt;/li&gt;
&lt;li&gt;generating demand&lt;/li&gt;
&lt;li&gt;creating supply&lt;/li&gt;
&lt;li&gt;securing your place on the market (even making it indispensable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the while your competitors will barely be at step one.&lt;/p&gt;

&lt;p&gt;In comparing the subscription-based Avo to a platform like Wordpress but for Ruby on Rails, it is also implied that it makes your production time at least 10 times more efficient.&lt;/p&gt;

&lt;p&gt;No additional costs, no scouting for the ideal RoR programmer, just an app developed by programmers who have faced the same problems and developed a solution. A solution that will save you money, time, stress, and a lot of extra hassle to just turn your product into reality as quickly and effortlessly as possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Requirements for implementing an MVP
&lt;/h2&gt;

&lt;p&gt;While some products are less likely to have an obvious and compact explanation for their value and functionality, others can be summed up in a basic elevator pitch. Regardless, there are some factors to take into consideration before deciding on implementing an MVP approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aligned Ideology Within the Team
&lt;/h3&gt;

&lt;p&gt;A team's first real test is coming up with the Proof of Concept (PoC). This usually resembles the final product, except it is just an early model. This is where hypotheses and outcomes are discussed and explored. Demonstrating the functionality of the product will ascertain the theory that lies behind it.&lt;/p&gt;

&lt;p&gt;This means the team has to be on the same wavelength. It should be strong enough to not crumble during the brainstorming, documentation, and &lt;a href="https://leanstack.com/lean-canvas" rel="noopener noreferrer"&gt;Lean Canvas&lt;/a&gt; phases. After weak assumptions are expelled and timing and presentation types are decided upon, the team will already have gone through enough consolidation phases to have a clearer purpose and work ethic.&lt;/p&gt;

&lt;p&gt;After all, a healthy and unified team is the strongest type, as the results always show. An MVP is guaranteed to benefit from this type of collective attitude.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Staffing
&lt;/h3&gt;

&lt;p&gt;After the PoC phase, the team needs to start taking each following step of the product very seriously in terms of execution. A strong and effective MVP requires sophistication.&lt;/p&gt;

&lt;p&gt;A team should look into adding to their staff as a necessary investment to their operation. Specifically, developers with knowledge of back-end technology, as well as front-end technologies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prototype Evolution
&lt;/h3&gt;

&lt;p&gt;If a product works, it needs to be scaled up to satisfy mass demand. This means the architecture of information is crucial in setting the product's stability and scalability. The natural progression of a product should be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Proof of Concept&lt;/li&gt;
&lt;li&gt;Prototype&lt;/li&gt;
&lt;li&gt;MVP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without having to give up on quality, scalability should be kept in mind with each step forward. Every factor that plays a positive role in reliability should be kept from one stage to another, and improved upon.&lt;/p&gt;

&lt;h3&gt;
  
  
  MVP Construction
&lt;/h3&gt;

&lt;p&gt;Finally, the actual building of the MVP. Based on initial interest and first experiences reported by users, the experimentation stage should be very advanced by now and the business model is on the verge of confirming or infirming initial theories. Namely, as a startup owner, you should have by now been able to tick the following boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Market and customer segment targeting&lt;/li&gt;
&lt;li&gt;Finding problems within customer segments and trying to find viable solutions&lt;/li&gt;
&lt;li&gt;Offering your solution as having unique value on the market&lt;/li&gt;
&lt;li&gt;Setting up communication and distribution channels&lt;/li&gt;
&lt;li&gt;Setting up revenue streams and possible future collaborations&lt;/li&gt;
&lt;li&gt;Assessing the costs - how much goes in and out to sustain the operation until it takes off&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your experimentation stage has undergone all of these (and afferent) bullet points, the team's path should be clear and ready to start integrating the MVP process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is an MVP a viable solution for your startup?
&lt;/h3&gt;

&lt;p&gt;Since knowledge is the driving force behind the startup, it is also its kerosene until it takes off. An MVP is a gap. It helps a company turn knowledge into business.&lt;/p&gt;

&lt;p&gt;By collecting customer data, a Minimum Viable Product aims to validate initial assumptions and hypotheses a team has when embarking on this startup trip, and turn them into a viable business model.&lt;/p&gt;

&lt;p&gt;Before looking for external funding, startup companies have to be unified in the faith they have in their product. This can only happen by receiving customer validation: if one customer finds value in the product, they won't be alone. Thus the value will increase, and the faith will drive the team to gradually go "all in". A functioning product built with a minimum budget is exactly what fills that gap and what fuels the next exciting stages.&lt;/p&gt;

&lt;p&gt;As a bonus - which can turn out to work in their favor - a team can also explore if there is any competition for what their product has to offer, and take notes on why customers are looking elsewhere after not being satisfied with those solutions. You could even be the first that presents this solution!&lt;/p&gt;

&lt;p&gt;But in order not to jump ahead too much or even pose a threat to the existing market, an MVP also helps in maintaining balance and somewhat of a low-profile in its initial stages. Does your product relate to the potential an MVP serves? It depends on how far you are willing to take it, how quickly, and how much you believe in its success. The ball is in your court now.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dynamically re-use &amp; lazy-load page using Hotwire</title>
      <dc:creator>Adrian Marin ⛵️</dc:creator>
      <pubDate>Tue, 01 Mar 2022 15:31:11 +0000</pubDate>
      <link>https://dev.to/avo-cool/dynamically-re-use-lazy-load-page-using-hotwire-4401</link>
      <guid>https://dev.to/avo-cool/dynamically-re-use-lazy-load-page-using-hotwire-4401</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://avohq.io/blog/dynamically-re-use-pages-with-hotwire" rel="noopener noreferrer"&gt;avo.cool&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hotwire is a fantastic technology that helps you build dynamic websites without thinking about JavaScript.&lt;/p&gt;

&lt;p&gt;When we re-wrote Avo from VueJS to Hotwire, when it first came out, we had to think about how we could leverage it to our advantage. &lt;/p&gt;

&lt;p&gt;One of the first things we did was to add dynamic turbo-frames around common pages.&lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;ResourceIndex&lt;/code&gt; page is the page that usually displays the table with the requested resources (it shows the users on &lt;code&gt;/users&lt;/code&gt;).&lt;br&gt;
We knew that we could use that exact partial when we wanted to display the &lt;code&gt;has_many&lt;/code&gt; association on the &lt;code&gt;ResourceShow&lt;/code&gt; page but didn't know how at first. We could have extracted it to a partial and rendered it in the &lt;code&gt;ResourceShow&lt;/code&gt; page underneath the record details, but the &lt;code&gt;ResourceShow&lt;/code&gt; page would take a long time to load when you have many associations to one record.&lt;/p&gt;

&lt;p&gt;We then came up with the idea to use a turbo-frame for that but still didn't want to use a partial.&lt;/p&gt;

&lt;p&gt;Let's say we have &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Team&lt;/code&gt; models like so:&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;Team&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:team&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 routes and controllers look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;

&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/:resource_name/:id/:related_name/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s2"&gt;"associations#index"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseController&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="c1"&gt;# if there's a query set up, use it, if not, set one up&lt;/span&gt;
    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="k"&gt;defined?&lt;/span&gt; &lt;span class="vi"&gt;@query&lt;/span&gt;
      &lt;span class="vi"&gt;@query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_scope&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AssociationsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="c1"&gt;# find the parent record and set the query&lt;/span&gt;
    &lt;span class="vi"&gt;@query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@parent_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;public_send&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;:related_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;p&gt;So what happens there? When a user goes to &lt;code&gt;/users&lt;/code&gt;, they will see the list of users, and if they go to &lt;code&gt;/teams/TEAM_ID/users&lt;/code&gt;, they should see the same list of users but scoped to the respective team.&lt;/p&gt;

&lt;p&gt;In order to achieve this using &lt;code&gt;turbo-frame&lt;/code&gt;s we'll add a lazy-loaded turbo frame on the &lt;code&gt;RecordShow&lt;/code&gt; page with the &lt;code&gt;src&lt;/code&gt; attribute set to the association path with the &lt;code&gt;?turbo_frame="has_many_users"&lt;/code&gt; param added to the path &lt;code&gt;/teams/TEAM_ID/users?turbo_frame="has_many_users"&lt;/code&gt; url.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/base/show.html.erb --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- record details here --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- association details below --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;turbo-frame&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"has_many_users"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/teams/#{@team.id}/users?turbo_frame=has_many_users"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_top"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Loading state --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/turbo-frame&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we should add a dynamic wrap around the &lt;code&gt;index.html.erb&lt;/code&gt; partial like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/base/index.html.erb --&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&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;:turbo_frame&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;turbo-frame&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&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;:turbo_frame&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- The list of records --&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&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;:turbo_frame&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/turbo-frame&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens is that when the user loads a teams &lt;code&gt;Show&lt;/code&gt; page &lt;code&gt;/teams/1&lt;/code&gt;, that page will display with the lazy-loaded frame (so no impact on the performance of that page on load), which in turn, loads the association &lt;code&gt;Index&lt;/code&gt; page with the &lt;code&gt;turbo_frame&lt;/code&gt; param. That will add the &lt;code&gt;&amp;lt;turbo-frame id="has_many_users"&amp;gt;&lt;/code&gt; tag around the template allowing Turbo to replace the content dynamically on the page.&lt;/p&gt;

&lt;p&gt;We're re-using the actual &lt;code&gt;index.html.erb&lt;/code&gt; template and the &lt;code&gt;BaseController#index&lt;/code&gt; action.&lt;/p&gt;

&lt;p&gt;Of course, this can be improved using some helpers and a partial.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;turbo_frame_wrap&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="s2"&gt;"partials/turbo_frame_wrap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/partials/turbo_frame_wrap.html.erb --&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;turbo-frame&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/turbo-frame&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/base/show.html.erb --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- record details here --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- association details below --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;turbo-frame&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;frame_url&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_top"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Loading state --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/turbo-frame&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- views/base/index.html.erb --&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_wrap&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;:turbo_frame&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- The list of records --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can do the same thing for &lt;code&gt;Show&lt;/code&gt; pages too. We did with Avo.&lt;/p&gt;

&lt;p&gt;You can find a more detailed example on Avo's GitHub repo.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/avo-hq/avo/blob/main/app/views/avo/base/index.html.erb" rel="noopener noreferrer"&gt;&lt;code&gt;index.html.erb&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/avo-hq/avo/blob/main/app/helpers/avo/application_helper.rb#L23" rel="noopener noreferrer"&gt;&lt;code&gt;turbo_frame_wrap&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/avo-hq/avo/blob/main/app/components/avo/turbo_frame_wrapper_component.html.erb" rel="noopener noreferrer"&gt;&lt;code&gt;turbo_frame_wrapper_component.html.erb&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay cool and improve performance 💪&lt;/p&gt;

&lt;p&gt;&lt;a href="https://avo.cool/" rel="noopener noreferrer"&gt;avo.cool/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://avo.cool/repo" rel="noopener noreferrer"&gt;avo.cool/repo&lt;/a&gt;&lt;br&gt;
&lt;a href="https://avo.cool/feedback" rel="noopener noreferrer"&gt;avo.cool/feedback&lt;/a&gt;&lt;br&gt;
&lt;a href="https://avo.cool/try" rel="noopener noreferrer"&gt;avo.cool/try&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hotwire</category>
      <category>rails</category>
      <category>ruby</category>
      <category>admin</category>
    </item>
  </channel>
</rss>
