<?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: Cristian Molina</title>
    <description>The latest articles on DEV Community by Cristian Molina (@megatux).</description>
    <link>https://dev.to/megatux</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%2F2170%2F850020.jpeg</url>
      <title>DEV Community: Cristian Molina</title>
      <link>https://dev.to/megatux</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/megatux"/>
    <language>en</language>
    <item>
      <title>Basic painting with Ruby: implementing a flood-fill algorithm with DragonRuby GTK</title>
      <dc:creator>Cristian Molina</dc:creator>
      <pubDate>Wed, 22 May 2024 00:10:14 +0000</pubDate>
      <link>https://dev.to/megatux/basic-painting-with-ruby-implementing-a-flood-fill-algorithm-with-dragonruby-gtk-334k</link>
      <guid>https://dev.to/megatux/basic-painting-with-ruby-implementing-a-flood-fill-algorithm-with-dragonruby-gtk-334k</guid>
      <description>&lt;p&gt;Continuing with my learning and experimentation with &lt;em&gt;DRGTK&lt;/em&gt;, I wanted to implement a flood-fill algorithm on a canvas with some drawings.&lt;/p&gt;

&lt;p&gt;This idea came from an old interview exercise I did some years ago.&lt;/p&gt;

&lt;p&gt;The Flood Fill algorithm is commonly used in graphics software to fill an area with a specific color. It’s what powers the paint bucket tool.&lt;/p&gt;

&lt;p&gt;So having a canvas and a pair of coordinates on that, the initial color is the one located in this location and the plan is to change it to another color, filling all adjacent pixels with the original color with the new one until a different one from the original one is reached.&lt;br&gt;
My implementation is a recursive one:&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;paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;base_color&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;new_color&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;sleep_time: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_color&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;base_color&lt;/span&gt;

   &lt;span class="n"&gt;coords_inside_matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;

   &lt;span class="n"&gt;pixel_should_be_painted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coords_inside_matrix&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;base_color&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;pixel_should_be_painted&lt;/span&gt;

   &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_color&lt;/span&gt;  &lt;span class="c1"&gt;# paint the cell&lt;/span&gt;

   &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;x: &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;base_color: &lt;/span&gt;&lt;span class="n"&gt;base_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;new_color: &lt;/span&gt;&lt;span class="n"&gt;new_color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;x: &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;base_color: &lt;/span&gt;&lt;span class="n"&gt;base_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;new_color: &lt;/span&gt;&lt;span class="n"&gt;new_color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;x: &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;base_color: &lt;/span&gt;&lt;span class="n"&gt;base_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;new_color: &lt;/span&gt;&lt;span class="n"&gt;new_color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;x: &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;base_color: &lt;/span&gt;&lt;span class="n"&gt;base_color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;new_color: &lt;/span&gt;&lt;span class="n"&gt;new_color&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;I worked on my solution using TDD, with Minitest spec tests, so it was easier to start with basic scenarios and also printed it while it moved the brush position recursively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Running:

paint(2-1)
  changing x:2 y:1
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 2, 3, 8]
[2, 2, 2, 0, 0]
[0, 0, 2, 0, 5]
paint(2-2)
  changing x:2 y:2
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 8, 3, 8]
[2, 2, 2, 0, 0]
[0, 0, 2, 0, 5]
paint(2-3)
  changing x:2 y:3
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 8, 3, 8]
[2, 2, 8, 0, 0]
[0, 0, 2, 0, 5]
paint(2-4)
  changing x:2 y:4
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 8, 3, 8]
[2, 2, 8, 0, 0]
[0, 0, 8, 0, 5]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the Dragon Ruby side, I drew the matrix as a canvas and queried the mouse functions to get the cell position to paint or to change the color of the brush by clicking on a color palette box.&lt;br&gt;
There is no pencil functionality so the canvas starts with a default drawing to play with it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w6KCaEil--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://i.ibb.co/XzHjV9k/Grabaci-n-de-pantalla-desde-2024-05-21-19-46-51.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w6KCaEil--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://i.ibb.co/XzHjV9k/Grabaci-n-de-pantalla-desde-2024-05-21-19-46-51.gif" alt="Application screeshot" width="652" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...and that's basically it. Not sure what will be next on this series. There is a game jam with Dragon Ruby &lt;a href="https://itch.io/jam/kifass-2"&gt;starting soon&lt;/a&gt;, maybe I could participate on it :) .&lt;/p&gt;

&lt;p&gt;Links to the app and code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://megatux.itch.io/flood-fill-example"&gt;Itch.io page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/megatux/drgtk_floodfill"&gt;github repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

</description>
      <category>dragonruby</category>
      <category>ruby</category>
      <category>paint</category>
    </item>
    <item>
      <title>Playing with DragonRuby Game Toolkit (DRGTK)</title>
      <dc:creator>Cristian Molina</dc:creator>
      <pubDate>Mon, 06 May 2024 23:20:30 +0000</pubDate>
      <link>https://dev.to/megatux/playing-with-dragonruby-game-toolkit-drgtk-471h</link>
      <guid>https://dev.to/megatux/playing-with-dragonruby-game-toolkit-drgtk-471h</guid>
      <description>&lt;p&gt;Last week I was working on a very simple game to test &lt;a href="https://dragonruby.org/toolkit/game" rel="noopener noreferrer"&gt;DragonRuby&lt;/a&gt; Game Toolkit and learn its API.&lt;/p&gt;

&lt;p&gt;This Ruby implementation is based on &lt;a href="https://mruby.org/" rel="noopener noreferrer"&gt;mruby&lt;/a&gt; and &lt;a href="https://llvm.org/" rel="noopener noreferrer"&gt;LLVM&lt;/a&gt; and it’s commercial software but cheap.&lt;/p&gt;

&lt;p&gt;The game toolkit has a simple API to handle sprites with animations, sound/music, inputs, basic drawing primitives like lines or rectangles, allows for HTTP requests  and other niceties. It also has a fixed resolution and 60 times a second game loop.&lt;/p&gt;

&lt;p&gt;The engine has a game loop that calls to a &lt;strong&gt;#tick&lt;/strong&gt; method where you draw, check inputs and update game logic.&lt;/p&gt;

&lt;p&gt;The code is structured in a couple of classes, the main one is the MyGame, that contains other classes like the Player, the list of FloorFires and the Coin to collect.&lt;/p&gt;

&lt;p&gt;When it detects a collision it plays a sound. The game is paused when the window loses focus.&lt;/p&gt;

&lt;p&gt;For collision detection, it uses some builtin rectangle intersection primitives.&lt;/p&gt;

&lt;p&gt;For Input handling, the keyboard, arrows/WASD are handled the same.&lt;/p&gt;

&lt;p&gt;And that’s pretty much it. There are a lot to improve the game but I think I’ll start a new project with a better idea to start doing something funnier and more polished.&lt;/p&gt;

&lt;p&gt;Besides &lt;a href="https://docs.dragonruby.org/static/docs.html" rel="noopener noreferrer"&gt;the official docs&lt;/a&gt;, I learnt a lot from &lt;a href="https://dev.to/presidentbeef/series/16166"&gt;this post series&lt;/a&gt;, kudos to Justin!, and also the &lt;a href="https://discord.com/channels/608064116111966245/" rel="noopener noreferrer"&gt;official discord&lt;/a&gt; is super friendly and helpful when you get stuck on something.&lt;/p&gt;

&lt;h4&gt;
  
  
  The start screen:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2Fhs791w4%2Fclideo-editor-3c8662f134954b7885542cdacc289741.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2Fhs791w4%2Fclideo-editor-3c8662f134954b7885542cdacc289741.gif" alt="start screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  In-game:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FXjCPhQ5%2Fclideo-editor-4a7788b1097a4da2a8b419cc1fa6f7fa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FXjCPhQ5%2Fclideo-editor-4a7788b1097a4da2a8b419cc1fa6f7fa.gif" alt="in-game animation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source is about 360 lines and available &lt;a href="https://github.com/megatux/dragonruby_the_fire/" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;. The assets are free.&lt;/p&gt;

&lt;p&gt;You can download or play it online &lt;a href="https://megatux.itch.io/the-fire" rel="noopener noreferrer"&gt;here&lt;/a&gt; at Itch.io (DRGTK includes a great tool to deploy to Itch.io)&lt;/p&gt;

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

</description>
      <category>gamedev</category>
      <category>dragonruby</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Adding an inline delete action on the listing page</title>
      <dc:creator>Cristian Molina</dc:creator>
      <pubDate>Sat, 30 Mar 2024 18:33:01 +0000</pubDate>
      <link>https://dev.to/megatux/adding-an-inline-delete-action-on-the-listing-page-1f95</link>
      <guid>https://dev.to/megatux/adding-an-inline-delete-action-on-the-listing-page-1f95</guid>
      <description>&lt;p&gt;This is the part III of the Phlex, htmx combo for building UIs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dLmPizEa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://i.ibb.co/R79NnTF/Grabacindepantalladesde2024-03-3013-38-28-ezgif-com-video-to-gif-converter.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dLmPizEa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://i.ibb.co/R79NnTF/Grabacindepantalladesde2024-03-3013-38-28-ezgif-com-video-to-gif-converter.gif" alt="deleting an article" width="600" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty similar to the insert action, &lt;br&gt;
it consist of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Edit ArticleComponent to add a delete button code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a delete route, if needed (not here).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a controller method to handle the removal of the article using the &lt;em&gt;id&lt;/em&gt; received.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;template&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="no"&gt;ARTICLE_STYLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="s2"&gt;"article-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="no"&gt;HEADER_STYLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&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="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"bg-slate-200 p-4 basis-3/4"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;details&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"No description"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="no"&gt;DELETE_BUTTON_STYLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;hx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;delete: &lt;/span&gt;&lt;span class="n"&gt;article_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;confirm: &lt;/span&gt;&lt;span class="s2"&gt;"Delete Article?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;target: &lt;/span&gt;&lt;span class="s2"&gt;"#article-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;swap: &lt;/span&gt;&lt;span class="s2"&gt;"outerHTML swap:.5s"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"X"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;The new button inside the Phlex Article component&lt;/em&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
    &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&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="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;plain: &lt;/span&gt;&lt;span class="s2"&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;&lt;em&gt;The controller action&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.item-article.htmx-swapping&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;0.5s&lt;/span&gt; &lt;span class="n"&gt;ease-out&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;&lt;em&gt;Some CSS for htmx effect&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Note that it should return an empty string or it would not do the removal effect in the UI and JS would be needed.&lt;/p&gt;

&lt;p&gt;It felt pretty easy to add this functionality. It start to looks good.&lt;/p&gt;

&lt;p&gt;Next will be that inline edit action or some inline filtering of the items in the list.&lt;/p&gt;

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

</description>
      <category>htmx</category>
      <category>rails</category>
      <category>phlex</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Adding lazy loading of items using htmx</title>
      <dc:creator>Cristian Molina</dc:creator>
      <pubDate>Mon, 25 Mar 2024 23:11:08 +0000</pubDate>
      <link>https://dev.to/megatux/adding-lazy-loading-of-items-using-htmx-2d9</link>
      <guid>https://dev.to/megatux/adding-lazy-loading-of-items-using-htmx-2d9</guid>
      <description>&lt;p&gt;This is a continuation of some testing of these front-end libraries.&lt;br&gt;
I used the &lt;a href="https://htmx.org/examples/lazy-load/" rel="noopener noreferrer"&gt;lazy load example here&lt;/a&gt; to add lazy loading of the articles on this index page.&lt;/p&gt;

&lt;p&gt;This is how it looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30qtx2vfu0sgqa59texu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30qtx2vfu0sgqa59texu.gif" alt="a spinning animation before showing articles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Previous to this change, I created an Articles Active Record model, ran migrations, and changed the insert action to create new records.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I added a GET &lt;em&gt;htmx&lt;/em&gt; definition in the index view code to fetch the items' partial and added a route and a controller action for this new response.&lt;/p&gt;

&lt;p&gt;You can see that there is an extra logic in the &lt;code&gt;Articles::IndexView&lt;/code&gt; to discern between displaying the lazy load code or the fragment with the list of articles. This is based on an initialization parameter. I'd probably refactor this later, maybe extracting the list block into a new component.&lt;/p&gt;

&lt;p&gt;Here is the important code changes:&lt;/p&gt;

&lt;h3&gt;
  
  
  Route definition
&lt;/h3&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;:articles&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: :collection&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Controller changes
&lt;/h3&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;index&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Articles&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IndexView&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="kp"&gt;nil&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;items&lt;/span&gt;
    &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&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="ss"&gt;id: :desc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# this is to simulate a slow response and should be removed in a real scenario&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Articles&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IndexView&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;articles&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;layout: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Changes in the index view class
&lt;/h3&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;Articles::IndexView&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationView&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;template&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="n"&gt;title_with_add_button&lt;/span&gt;
      &lt;span class="n"&gt;load_indicator_placeholder&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;load_articles_section&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_indicator_placeholder&lt;/span&gt;
    &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"hx-get"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;items_articles_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"hx-trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"load"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"flex justify-center"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;alt: &lt;/span&gt;&lt;span class="s2"&gt;"Loading items..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"htmx-indicator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;width: &lt;/span&gt;&lt;span class="s2"&gt;"150"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;src: &lt;/span&gt;&lt;span class="s2"&gt;"/bars.svg"&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;def&lt;/span&gt; &lt;span class="nf"&gt;load_articles_section&lt;/span&gt;
    &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="s2"&gt;"article-list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"grid md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Also available in the &lt;a href="https://github.com/megatux/rails_sample_bun_tailwind_phlex_htmx" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;&lt;/p&gt;

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

</description>
      <category>htmx</category>
      <category>rails</category>
      <category>phlex</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Experimenting with Modern UI Alternatives in Rails</title>
      <dc:creator>Cristian Molina</dc:creator>
      <pubDate>Thu, 14 Mar 2024 23:31:33 +0000</pubDate>
      <link>https://dev.to/megatux/experimenting-with-modern-ui-alternatives-in-rails-3p9d</link>
      <guid>https://dev.to/megatux/experimenting-with-modern-ui-alternatives-in-rails-3p9d</guid>
      <description>&lt;p&gt;I embarked on an experiment with a Rails application using modern alternative tools and libraries. In this post, I'll walk you through the steps I took to set up a Rails template repository with the following technologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Configuration with &lt;strong&gt;&lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun JS&lt;/a&gt;&lt;/strong&gt; runtime for efficient JavaScript management.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration of &lt;strong&gt;&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt;&lt;/strong&gt; for sleek UI design.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Utilization of &lt;strong&gt;&lt;a href="https://www.phlex.fun/" rel="noopener noreferrer"&gt;Phlex&lt;/a&gt;&lt;/strong&gt; library to build views entirely in Ruby, eliminating the need for mixed template languages and replacing partials and view helpers with components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implementation of &lt;strong&gt;&lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt;&lt;/strong&gt; actions for dynamic content swapping without full page refresh.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setup Steps:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Installed bun js runtime (I used &lt;a href="https://mise.jdx.dev/" rel="noopener noreferrer"&gt;mise&lt;/a&gt;, btw)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Added necessary gems and generated some configurations:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


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


&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Edited &lt;em&gt;Procfile&lt;/em&gt; to remove bun css watch line and use &lt;em&gt;tailwindcss:watch&lt;/em&gt; task.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Edited &lt;em&gt;application_layout.rb&lt;/em&gt; to remove &lt;em&gt;javascript_importmap_tags&lt;/em&gt; line.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  This is how it looks:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FQH6xXCS%2FGrabaci-n-de-pantalla-desde-2024-03-14-23-20-58.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FQH6xXCS%2FGrabaci-n-de-pantalla-desde-2024-03-14-23-20-58.gif" alt="application screen-shot showing some articles and an Add button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FG9N8J04%2FCaptura-desde-2024-03-12-19-49-16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FG9N8J04%2FCaptura-desde-2024-03-12-19-49-16.png" alt="Server outputs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Rails &lt;strong&gt;&lt;em&gt;bin/dev&lt;/em&gt;&lt;/strong&gt; command starts the three processes defined in the &lt;strong&gt;&lt;em&gt;Procfile&lt;/em&gt;&lt;/strong&gt; file. The Rails server, the Bun js runtime, and the TailwindCSS watcher.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some code highlights:
&lt;/h3&gt;


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


&lt;p&gt;The main application layout, in Ruby, has some header and footer definitions and a call to a Ruby &lt;em&gt;block&lt;/em&gt; in the  tag. &lt;/p&gt;


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


&lt;p&gt;The controller has an action for listing the Articles and the method to handle the POST to add a new Article.&lt;/p&gt;


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


&lt;p&gt;The Index view has a title, an Add button, and the list of Articles. In a real app these Articles would probably be passed on initialization from a DB query outside this class.&lt;/p&gt;


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

&lt;p&gt;The Article component just generates some random data. In a real app it would probably be populated from an Active Record object on initialization.&lt;/p&gt;

&lt;p&gt;The full source repository &lt;a href="https://github.com/megatux/rails_sample_bun_tailwind_phlex_htmx" rel="noopener noreferrer"&gt;is available here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By following these steps, I was able to leverage a modern UI frontend for a Rails application in a hopefully more maintainable way.&lt;/p&gt;

&lt;p&gt;I plan to continue experimenting with these tools on more advanced topics like forms, websockets &amp;amp; SSE updates, filters, pagination, using JS interactivity, and more complex components.&lt;/p&gt;

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

</description>
      <category>rails</category>
      <category>htmx</category>
      <category>tailwindcss</category>
      <category>phlex</category>
    </item>
    <item>
      <title>Heroku, I stream you!</title>
      <dc:creator>Cristian Molina</dc:creator>
      <pubDate>Mon, 09 Aug 2021 23:44:15 +0000</pubDate>
      <link>https://dev.to/megatux/heroku-i-stream-you-4mdb</link>
      <guid>https://dev.to/megatux/heroku-i-stream-you-4mdb</guid>
      <description>&lt;p&gt;The problem: timeouts on generated content exported as CSV or other formats.&lt;/p&gt;

&lt;p&gt;It's a very common feature to have export functionality on the page to formats like CSV, XLSX, PDF, etc.&lt;/p&gt;

&lt;p&gt;This is a simple thing on Rails inside a #respond_to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def index
  @records = ...
  respond_to do |format|
    format.html # index.html
    format.csv { some_csv_generation_code }
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, some day, errors start to appear from users when they try to download a lot of information.&lt;/p&gt;

&lt;p&gt;The server responds with timeout errors after a certain amount of seconds and that stops the request that was generating the file. &lt;/p&gt;

&lt;p&gt;In a PaaS like &lt;strong&gt;Heroku&lt;/strong&gt; this time limit is at 30 seconds of no content generated. Also there is another timeout of 55’’ between each new fragment of output. Citing the official information:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An application has an initial 30 second window to respond with a single byte back to the client. However, each byte transmitted thereafter (either received from the client or sent by your application) resets a rolling 55 second window. If no data is sent during the 55 second window, the connection will be terminated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rails seems to have streaming functionality to handle this kind of output generation in the &lt;em&gt;ActionController::Live&lt;/em&gt;. Also in Rails edge (future v7) there is a #send_stream method that seems to achieve this functionality so I copied it to my Rails 5.2 app into a controller concern and changed the CSV generation logic to implement the &lt;em&gt;Enumerable #each method&lt;/em&gt;.&lt;br&gt;
It was not working until I also override the &lt;em&gt;Last-Modified&lt;/em&gt; header. See &lt;a href="https://github.com/rack/rack/issues/1619"&gt;this issue discussion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the full code of the controller concern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'content_disposition'

module Streamable
  extend ActiveSupport::Concern
  include ActionController::Live

  # Almost verbatim copy of new Rails 7 method.
  # See https://edgeapi.rubyonrails.org/classes/ActionController/Live.html#method-i-send_stream
  def send_stream(filename:, disposition: 'attachment', type: nil)
    response.headers['Content-Type'] =
      (type.is_a?(Symbol) ? Mime[type].to_s : type) ||
      Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete('.')) ||
      'application/octet-stream'

    response.headers['Content-Disposition'] = ContentDisposition.format(disposition: disposition, filename: filename)
      # with rails 6 remove content_disposition gem and use:
      #  ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)

    # Extra: needed for streaming
    response.headers['Last-Modified'] = Time.now.httpdate

    yield response.stream
  ensure
    response.stream.close
  end
end

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

&lt;/div&gt;



&lt;p&gt;For Rails 5.x I needed an extra gem to set the &lt;em&gt;Content-Disposition&lt;/em&gt; header:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem 'content_disposition', '~&amp;gt; 1.0' # Not needed on Rails 6&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A testing example of a controller that generates slow dummy data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ExporterController &amp;lt; ApplicationController
  include Streameable

  def index
    respond_to do |format|
      format.html # index.html
      format.js   # index.js
      format.csv do
        send_stream(attachment_opts) do |stream|
          stream.write "email_address,updated_at\n"

          50.times.each do |i|
            line = "pepe_#{i}@acme.com,#{Time.zone.now}\n"
            stream.write line
            puts line
            sleep 1  # force slow response
          end
        end
      end
    end
  end

  private

  def attachment_opts
    {
      filename: "data_#{Time.zone.now.to_i}.csv",
      disposition: 'attachment',
      type: 'text/csv'
    }
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, if you use something like curl you will see the output generated second by second.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;curl -i  &lt;a href="http://localhost:3000/exporter"&gt;http://localhost:3000/exporter&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2j3egtzo9frtqjqbbdub.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2j3egtzo9frtqjqbbdub.gif" alt="Alt Text" width="1343" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now your app can stream big amounts of data like a champ!. &lt;/p&gt;

&lt;p&gt;I have deployed this app in a &lt;a href="https://megatux-stream-test.herokuapp.com/exporter"&gt;Heroku app&lt;/a&gt; and the code is on &lt;a href="https://github.com/megatux/heroku_streaming_csv"&gt;this github repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An extra performance tip. When using ActiveRecord to fetch tons of records from the DB consider using &lt;em&gt;#find_each&lt;/em&gt; instead of &lt;em&gt;#each&lt;/em&gt; so it will fetch the records in batches. &lt;/p&gt;

&lt;p&gt;Let me know if this was helpful for you.&lt;br&gt;
Happy Rails hacking!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>heroku</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A very short intro to the Roda web toolkit</title>
      <dc:creator>Cristian Molina</dc:creator>
      <pubDate>Fri, 04 Oct 2019 16:25:35 +0000</pubDate>
      <link>https://dev.to/megatux/a-very-short-intro-to-the-roda-web-toolkit-2gag</link>
      <guid>https://dev.to/megatux/a-very-short-intro-to-the-roda-web-toolkit-2gag</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;There are several "&lt;em&gt;lightweight&lt;/em&gt;" Ruby web &amp;amp; API libraries out there.&lt;br&gt;
&lt;em&gt;Sinatra&lt;/em&gt; is probably the most known but there are others that are newer, faster, cleaner, more flexible and/or better maintained.&lt;/p&gt;

&lt;p&gt;One of those is &lt;a href="https://roda.jeremyevans.net" rel="noopener noreferrer"&gt;&lt;em&gt;Roda&lt;/em&gt;&lt;/a&gt;, created by Jeremy Evans, the creator of the Sequel gem. It started as a Cuba fork but then evolved a lot. It is specially notably by its speed and plugin system, shared characteristics with its sibling Sequel gem, where everything can be overridden.&lt;/p&gt;

&lt;p&gt;It is auto-defined as a "&lt;em&gt;Routing tree web toolkit&lt;/em&gt;". Why is that? Because of the way the routes are defined &amp;amp; run. Let me show you an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"roda"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Roda&lt;/span&gt;
  &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="ss"&gt;:render&lt;/span&gt;
  &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="ss"&gt;:all_verbs&lt;/span&gt;

  &lt;span class="n"&gt;route&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;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is&lt;/span&gt; &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Integer&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;artist_id&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="vi"&gt;@artist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Artist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;artist_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;check_access&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@artist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="ss"&gt;:artist&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="vi"&gt;@artist&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;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="vi"&gt;@artist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;  &lt;span class="s2"&gt;"/"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you compare with other libraries like Sinatra, instead of setting the &lt;a class="mentioned-user" href="https://dev.to/artist"&gt;@artist&lt;/a&gt; variable and checking the access is allowed in all three of the actions, the variables are set as soon as that branch of the tree is taken, and can be used in all routes under that branch. This is why Roda is called a routing tree web toolkit.&lt;/p&gt;

&lt;p&gt;More examples of the routing flexibility:&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;route&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;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="c1"&gt;# array matchers&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt; &lt;span class="sx"&gt;%w[hello hi]&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;greeting&lt;/span&gt;&lt;span class="o"&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;greeting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, world!"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c1"&gt;# method matchers&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is&lt;/span&gt;  &lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;method: &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="ss"&gt;:put&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:patch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="s2"&gt;"All your base are belong to us"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c1"&gt;# Regexp matchers&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;  &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="sr"&gt;/(\d+)/&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;user_id&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="s2"&gt;"Your id is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user_id&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="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;Roda's plugin system design is shared with the Sequel gem but also copied in other projects, like in the &lt;a href="https://shrinerb.com" rel="noopener noreferrer"&gt;Shrine&lt;/a&gt; file attachment toolkit, &lt;a href="http://cyx.is/cuba-3-released.html" rel="noopener noreferrer"&gt;Cuba(v3)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can start with a small, compact core of an application and grow complexity over time and learning as you go, having fine grained control on features and performance.&lt;/p&gt;

&lt;p&gt;For instance, wants &lt;a href="https://www.rubydoc.info/gems/roda/2.13.0/Roda/RodaPlugins/Websockets" rel="noopener noreferrer"&gt;websockets&lt;/a&gt;, checked:&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;plugin&lt;/span&gt; &lt;span class="ss"&gt;:websockets&lt;/span&gt;

&lt;span class="n"&gt;route&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;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"room"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Matches if it's a websocket request&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;websocket&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;ws&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&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="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c1"&gt;# If the request is not a websocket request, we render a template&lt;/span&gt;
    &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="s2"&gt;"room"&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;Do you want a JSON API? The &lt;em&gt;json&lt;/em&gt; &lt;a href="https://www.rubydoc.info/gems/roda/Roda/RodaPlugins/Json" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; responds with JSON automatically for arrays &amp;amp; hashes:&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;plugin&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;

&lt;span class="n"&gt;route&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;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is&lt;/span&gt; &lt;span class="s2"&gt;"foo"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"b"&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;But you can customize it easily, for example, adding &lt;em&gt;ActiveRecord&lt;/em&gt; relations:&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;plugin&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;classes: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Relation&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;serializer: &lt;/span&gt;&lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;
      &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="no"&gt;Serializer&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;object&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;as_json&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;route&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;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"albums/recent"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recent&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"albums/:id"&lt;/span&gt; &lt;span class="k"&gt;do&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="no"&gt;Album&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="nb"&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;The complete list of &lt;a href="http://roda.jeremyevans.net/documentation.html#included-plugins" rel="noopener noreferrer"&gt;bundled plugins&lt;/a&gt; is huge, more than &lt;a href="https://www.rubydoc.info/gems/roda/Roda/RodaPlugins" rel="noopener noreferrer"&gt;90&lt;/a&gt;, and there are several community extensions, so you are pretty much covered here. In case you want to do one, this is the structure:&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;Roda&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;RodaPlugins&lt;/span&gt;
    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MyPlugin&lt;/span&gt;
      &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&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;InstanceMethods&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;RequestClassMethods&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;RequestMethods&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;ResponseClassMethods&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;ResponseMethods&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;register_plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MyPlugin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;An image is worth a thousand words. Compare the request per second and the memory usage:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FbvWRMqG%2Fimage1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FbvWRMqG%2Fimage1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty close to raw Rack values, but with a nicer DSL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintenance
&lt;/h2&gt;

&lt;p&gt;Jeremy responses in the &lt;a href="https://groups.google.com/forum/#!forum/ruby-roda" rel="noopener noreferrer"&gt;mailing list&lt;/a&gt; are quick &amp;amp; he resolves any issues, too:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FgSnnpzz%2Fimage2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.ibb.co%2FgSnnpzz%2Fimage2.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Look Ma, no issues)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Some disadvantages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No routes introspection (although there is a &lt;a href="https://github.com/jeremyevans/roda-route_list" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; that helps with this).&lt;/li&gt;
&lt;li&gt;Less gems, as many are tightly coupled with default framework.&lt;/li&gt;
&lt;li&gt;Less community support vs Rails or Sinatra.&lt;/li&gt;
&lt;li&gt;Some reinventing the wheel may be unavoidable.&lt;/li&gt;
&lt;li&gt;The routing structure may not be your style (but there is a &lt;a href="http://roda.jeremyevans.net/rdoc/classes/Roda/RodaPlugins/ClassLevelRouting.html" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; to use a Sinatra like class route definitions)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Extra links:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This is a nice free &lt;a href="https://fiachetti.gitlab.io/mastering-roda/" rel="noopener noreferrer"&gt;Roda book&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The official &lt;a href="https://groups.google.com/forum/#!forum/ruby-roda" rel="noopener noreferrer"&gt;mailing list&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you want to know more about this &lt;em&gt;plugin system pattern&lt;/em&gt;, check &lt;a href="https://twin.github.io/the-plugin-system-of-sequel-and-roda/" rel="noopener noreferrer"&gt;this post&lt;/a&gt; from the &lt;a href="https://github.com/shrinerb/shrine" rel="noopener noreferrer"&gt;Shrine&lt;/a&gt;'s creator.&lt;/li&gt;
&lt;li&gt;Other benchmarks: &lt;a href="https://github.com/luislavena/bench-micro" rel="noopener noreferrer"&gt;ruby web frameworks micro benchmarks&lt;/a&gt; project, the famous &lt;a href="https://www.techempower.com/benchmarks/#section=data-r18&amp;amp;hw=ph&amp;amp;test=json&amp;amp;l=zijxtr-f" rel="noopener noreferrer"&gt;TechEmpower web benchmarks&lt;/a&gt; (filtered for Ruby frameworks) and also &lt;a href="https://github.com/jeremyevans/r10k/" rel="noopener noreferrer"&gt;this project&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>webdev</category>
    </item>
    <item>
      <title>asdf version manager</title>
      <dc:creator>Cristian Molina</dc:creator>
      <pubDate>Wed, 02 Oct 2019 18:43:00 +0000</pubDate>
      <link>https://dev.to/megatux/asdf-version-manager-153a</link>
      <guid>https://dev.to/megatux/asdf-version-manager-153a</guid>
      <description>&lt;h1&gt;
  
  
  Asdf-vm
&lt;/h1&gt;

&lt;p&gt;An extendable version manager&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When you work as a developer, it is the norm to have projects made with different languages &amp;amp; storages. Even if you only use a specific language or tool, every project could use a different version of it.&lt;/p&gt;

&lt;p&gt;A version manager allows you to easily install, use and switch between several versions of the same language or program.&lt;/p&gt;

&lt;p&gt;This is necessary mainly because most OS don't provide an effective way to have several versions of the same program or library (at a system level or per user account). For example, having several versions of the Ruby language at the same time in Linux with deb or rpm packaging systems.&lt;/p&gt;

&lt;p&gt;To overcome this limitations some scripts or "language version managers" like chruby, &lt;em&gt;RVM&lt;/em&gt; or &lt;em&gt;rbenv&lt;/em&gt; for Ruby, &lt;em&gt;nvm&lt;/em&gt; &amp;amp; &lt;em&gt;n&lt;/em&gt; for Node.js and dozens more started to appear. Each one with its own commands, setup instructions and peculiarities. Most of this version managers work installing all the things inside the user's home directory. As you can imagine, this may present some scaling &amp;amp; usability issues.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;asdf-vm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;asdf&lt;/em&gt; is  "generic" version manager, that with same interface of commands &amp;amp; one setup you can handle a lot of languages &amp;amp; tools versions altogether and it is extendable via plugins.&lt;/p&gt;

&lt;p&gt;Advantages, Why use asdf-vm?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;single CLI for multiple languages (&amp;amp; others)&lt;/li&gt;
&lt;li&gt;consistent commands to manage all your languages&lt;/li&gt;
&lt;li&gt;single global config keeping defaults in one place&lt;/li&gt;
&lt;li&gt;single .tool-versions config file per project&lt;/li&gt;
&lt;li&gt;support for existing config files .node-version, .nvmrc, .ruby-version for easy migration&lt;/li&gt;
&lt;li&gt;automatically switches runtime versions as you traverse your directories&lt;/li&gt;
&lt;li&gt;simple plugin system to add support for your language of choice&lt;/li&gt;
&lt;li&gt;completion scripts managed by the plugin, not you!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The extensive list of plugins can be found &lt;a href="https://asdf-vm.com/#/plugins-all?id=plugin-list"&gt;here&lt;/a&gt;. As you can see, there are a lot of languages like Python, Ruby, Go, Node.js, PHP, Java, .NET Core, Rust, Lua; stores like SQLite, Postgres, Redis, MongoDB, MySQL; and tools like ElasticSearch, ImageMagick, RabbitMQ, Vault, Terraform, k9s. Also, it is pretty easy to &lt;a href="https://asdf-vm.com/#/plugins-create"&gt;create a plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The install instructions for OSX &amp;amp; Linux &lt;a href="https://asdf-vm.com/#/core-manage-asdf-vm?id=install-asdf-vm"&gt;are here&lt;/a&gt;. It supports Bash, ZSH, Fish. They are quite simple and involves downloading a git repository or using &lt;em&gt;brew&lt;/em&gt; and adding a couple of lines in your shell configuration file.&lt;/p&gt;

&lt;p&gt;Example usage:&lt;/p&gt;

&lt;p&gt;Let's say your new assigned project "&lt;em&gt;Alpha&lt;/em&gt;" requires Ruby v2.4.2, Postgresql &amp;amp;gt; v9.x, Redis 4.x, ElasticSearch 6.7 and ImageMagick tooling and you have the plugins for all except &lt;em&gt;imagemagick&lt;/em&gt; don't have anything of that installed. You could setup it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asdf install redis 4.0.14
$ asdf install postgres 9.6.15
$ asdf install ruby 2.4.2
$ asdf install elasticsearch 6.7.1
$ asdf plugin-add imagemagick
$ asdf install imagemagick 7.0.8-66
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you setup the &lt;em&gt;local&lt;/em&gt; configuration for your project with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asdf local redis 4.0.14
$ asdf local postgres 9.6.15
$ asdf local ruby 2.4.2
$ asdf local elasticsearch 6.7.1
$ asdf local imagemagick 7.0.8-66
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a ".tool-versions" file, that you can add to the version control system, with the rest of your project files.&lt;/p&gt;

&lt;p&gt;If you switch to another project, when entering the directory, the local versions will be setup automatically.&lt;/p&gt;

&lt;p&gt;Other command examples:&lt;/p&gt;

&lt;p&gt;Just type "asdf" if you want to see the complete list of commands.&lt;/p&gt;

&lt;p&gt;List all available plugins for installation:&lt;/p&gt;

&lt;p&gt;$ asdf plugin-list-all&lt;/p&gt;

&lt;p&gt;List your currently installed plugins:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asdf plugin-list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Installing a new plugin:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asdf plugin-add elixir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What I am currently using (local or global):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asdf current
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Set a &lt;em&gt;global&lt;/em&gt; version for Ruby language:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asdf global ruby 2.4.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;List your current installed versions for the Ruby plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asdf list ruby
  2.4.7
  2.5.6
  2.6.4
  jruby-9.2.8.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List the available to install Postgresql versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asdf list-all postgres
  1.08
  …CUTTED…..
  11.4
  11.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;A nice feature is "Tab" completion for commands &amp;amp; version numbers, for instance, try typing&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
$ asdf &amp;lt;TAB&amp;gt;&amp;lt;TAB&amp;gt;&lt;br&gt;
current          install          local            plugin-list-all  reshim           update&lt;br&gt;
global           list             plugin-add       plugin-remove    shell            where&lt;br&gt;
help             list-all         plugin-list      plugin-update    uninstall        which&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
$ asdf install imagemagick &amp;lt;TAB&amp;gt;&amp;lt;TAB&amp;gt;&lt;br&gt;
7.0.8-38    7.0.8-41    7.0.8-44    7.0.8-47    7.0.8-50    7.0.8-53    7.0.8-56    7.0.8-59    7.0.8-62    7.0.8-65    7.0.8-39    7.0.8-42    7.0.8-45    7.0.8-48    7.0.8-51    7.0.8-54    7.0.8-57    7.0.8-60    7.0.8-63    7.0.8-66    7.0.8-40    7.0.8-43    7.0.8-46    7.0.8-49    7.0.8-52    7.0.8-55    7.0.8-58    7.0.8-61    7.0.8-64    continuous&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And that's basically it. In contrast to &lt;em&gt;Docker&lt;/em&gt;, where you have an isolated filesystem, libraries, ports &amp;amp; users and runs a specific process inside it utilizing the &lt;em&gt;docker&lt;/em&gt; command or &lt;em&gt;composing&lt;/em&gt; the application parts with some other tool like &lt;em&gt;docker-compose&lt;/em&gt;, which is great for creating reproducible &amp;amp; isolated environments for deploying, &lt;em&gt;asdf&lt;/em&gt; allows you a more natural &amp;amp; lightweight development workflow.&lt;/p&gt;

&lt;p&gt;Summarizing, &lt;em&gt;asdf&lt;/em&gt; tool lets me work without worry about conflicts between several moving parts, with speed and a minimum extra overhead. As an extra, it allows me to experiment with a new language (or a specific implementation like &lt;em&gt;jruby&lt;/em&gt; or &lt;em&gt;stackless&lt;/em&gt; Python) every now and then.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;p&gt;Project site &amp;amp; docs: &lt;a href="https://asdf-vm.github.io/asdf/"&gt;asdf-vm.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Github &lt;a href="https://github.com/asdf-vm/"&gt;projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lastly, I want to mention an alternative to &lt;em&gt;asdf&lt;/em&gt; I used previously called &lt;a href="https://github.com/anyenv/anyenv"&gt;anyenv&lt;/a&gt;.&lt;/p&gt;

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

</description>
      <category>productivity</category>
    </item>
  </channel>
</rss>
