<?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: Roland Studer</title>
    <description>The latest articles on DEV Community by Roland Studer (@rolandstuder).</description>
    <link>https://dev.to/rolandstuder</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%2F338325%2Fa81e7234-aed2-4820-92d2-2d96b0e3864a.png</url>
      <title>DEV Community: Roland Studer</title>
      <link>https://dev.to/rolandstuder</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rolandstuder"/>
    <language>en</language>
    <item>
      <title>RailsWorld 2023: Hotwire Edition</title>
      <dc:creator>Roland Studer</dc:creator>
      <pubDate>Sun, 08 Oct 2023 12:23:24 +0000</pubDate>
      <link>https://dev.to/rolandstuder/railsworld-2023-hotwire-edition-3m1f</link>
      <guid>https://dev.to/rolandstuder/railsworld-2023-hotwire-edition-3m1f</guid>
      <description>&lt;p&gt;The very first &lt;a href="https://rubyonrails.org/world"&gt;RailsWorld&lt;/a&gt; conference, organized by the &lt;a href="https://rubyonrails.org/foundation"&gt;Rails Foundation&lt;/a&gt;, has concluded at the beautiful venue of Beurs van Berlage in Amsterdam, Netherlands. Approximately 700 attendees joined in to look at the future of Rails.&lt;/p&gt;

&lt;h2&gt;
  
  
  RailsWorld 2023 was in many ways a "Hotwire" edition
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--acbpi-6T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.masto.host/rubysocial/media_attachments/files/111/182/182/644/465/180/original/592ed6de7c1008e8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--acbpi-6T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.masto.host/rubysocial/media_attachments/files/111/182/182/644/465/180/original/592ed6de7c1008e8.png" alt="notes about turbo" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The opening keynote and six talks primarily revolved around Hotwire, or you could count it as seven if you include Jason Charnes' talk, "Don't call it a comeback." The prevailing message seemed to be: "Yes, while the Rails API/SPA approach exists, but the sane path forward is Hotwire." It was repeatedly underscored that Rails remains a one-person framework. Rails &lt;del&gt;was&lt;/del&gt; &lt;strong&gt;is&lt;/strong&gt; empowering and revolutionary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Morphing and broadcasted refreshes come to Turbo
&lt;/h2&gt;

&lt;p&gt;Turbo Drive, which currently replaces the HTML body by default, becomes more powerful. Soon Turbo will use morphing to make only minimal changes to the DOM, enhancing user experiences significantly. With this change, you can expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seamless preservation of scroll position&lt;/li&gt;
&lt;li&gt;Elimination of flickering&lt;/li&gt;
&lt;li&gt;Application of smooth CSS transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turbo also implements the concept of server-side-triggered refreshes, allowing you to broadcast instructions to refresh a view following a model change, or directly from a controller or job.&lt;/p&gt;

&lt;p&gt;You can find a demo video and more information here:&lt;br&gt;
&lt;a href="https://github.com/hotwired/turbo/pull/1019"&gt;https://github.com/hotwired/turbo/pull/1019&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Morphing and the concept to do refreshes after broadcast are hardly new. &lt;a href="https://docs.stimulusreflex.com"&gt;Stimulus Reflex&lt;/a&gt; has employed morphing to update the page for years, and &lt;a href="https://cableready.stimulusreflex.com/guide/updatable.html"&gt;CableReady::Updatable&lt;/a&gt;, which allows listening to model requests for refreshes, has also been around for a while. But I am excited to see these concepts being adopted in Turbo and becoming more mainstream.&lt;/p&gt;

&lt;p&gt;BTW: Be sure to check out and contribute to the Hotwire community hub at &lt;a href="https://hotwire.io"&gt;hotwire.io&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Most impactful talk wasn't technical
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qrrGcCyK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.masto.host/rubysocial/media_attachments/files/111/192/507/293/553/209/original/657e235a091dcb72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qrrGcCyK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.masto.host/rubysocial/media_attachments/files/111/192/507/293/553/209/original/657e235a091dcb72.png" alt="Notes about shape up" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ryan Singer's talk on "Shaping up in the real world" hit the mark. It addressed precisely the areas where I currently face challenges in our design and development process, leaving me with some homework to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Great community
&lt;/h2&gt;

&lt;p&gt;The community was a delight! I can't even count how many people I connected with, and it was just great to meet people whom I had previously only interacted with online. Also got to meet a lot of those people who significantly contribute to the Rails ecosystem. Everyone I said hi to was more than willing to spare time and nerd out and share experiences. I was also thrilled to connect with Philip, who shares my enthusiasm for Phlex. He provided me with some exciting ideas for creating an even more crazy yet cohesive form object with &lt;a href="https://www.phlex.fun"&gt;Phlex&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Would I go again?
&lt;/h2&gt;

&lt;p&gt;I attended because it was in Europe, offering proximity, and also providing an opportunity to meet my coworker living in Amsterdam. We have numerous great conferences here in Europe, so there's no need for long flights and the stress of adjusting to different time zones. Personally, a crowd of 700 people was a bit too much for me. I am going to try smaller gatherings in the future.&lt;/p&gt;

&lt;p&gt;I'd also like to give a shout-out to Pelle, my coworker, for being an excellent host (though he might want to step up his game in the restaurant recommendation department ;-) ).&lt;/p&gt;

&lt;p&gt;This article orginally was published on &lt;a href="https://rstuder.ch/2023-rails-world/"&gt;rstuder.ch &lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>hotwire</category>
    </item>
    <item>
      <title>Supercharged table component built with ViewComponent</title>
      <dc:creator>Roland Studer</dc:creator>
      <pubDate>Wed, 02 Aug 2023 07:05:54 +0000</pubDate>
      <link>https://dev.to/rolandstuder/supercharged-table-component-built-with-viewcomponent-3j6i</link>
      <guid>https://dev.to/rolandstuder/supercharged-table-component-built-with-viewcomponent-3j6i</guid>
      <description>&lt;p&gt;When searching for examples of table components built with the &lt;a href="https://github.com/viewComponent/view_component/"&gt;ViewComponent gem&lt;/a&gt;, I was surprised to find none. After some inquiries, I came across examples that worked like this:&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="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Table&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;table&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Name"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Description"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_row&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;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="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;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;This approach is pragmatic, but personally, I prefer to think of tables in terms of columns. So, let's supercharge our table component to enable something like this:&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="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;TableComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="vi"&gt;@products&lt;/span&gt;&lt;span class="p"&gt;)&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;table&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  table.column("Name") { |product| link_to product.name, product }
  table.column("Description") { |product| truncate(product.description) } 
&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;h3&gt;
  
  
  Introducing the table component
&lt;/h3&gt;

&lt;p&gt;Let's dive into the implementation:&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;TableComponent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ViewComponent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:columns&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="vi"&gt;@columns&lt;/span&gt; &lt;span class="o"&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;column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&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="err"&gt;§&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@columns&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Column&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;label&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="kp"&gt;private&lt;/span&gt;

  &lt;span class="c1"&gt;# By calling content, we ensure that the view component calls the block, and @columns get populated&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;before_render&lt;/span&gt;
    &lt;span class="n"&gt;content&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;Column&lt;/span&gt; &lt;span class="c1"&gt;# a value object to hold the column definition&lt;/span&gt;
    &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:td_block&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&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="vi"&gt;@label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;
      &lt;span class="vi"&gt;@td_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&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;The corresponding erb file keeps things simple, omitting thead, tbody, or any styling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/th&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="vi"&gt;@rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
          &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;view_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&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;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;td_block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
          &lt;span class="c"&gt;&amp;lt;%# the capture ensures, that we do not only return the return of the block, but all the html from the block, see below %&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice three peculiarities here that require explanation:&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not use slots?
&lt;/h3&gt;

&lt;p&gt;The main challenge is that the ViewComponent gem does not have an official way to render a table cell multiple times with different data each time. Meaning &lt;a href="https://github.com/ViewComponent/view_component/issues/1810"&gt;you can't pass that while rendering a slot&lt;/a&gt; like this:&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="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no way to pass arbitrary data from the component (in our example, the rows of the table) to a slot block while rendering it. Therefore, we handle column definitions ourselves instead of using slots.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;before_render&lt;/code&gt; to ensure the block is called
&lt;/h3&gt;

&lt;p&gt;When you use the &lt;code&gt;TableComponent&lt;/code&gt; like this, and don't call &lt;code&gt;content&lt;/code&gt; at some point.&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="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;TableComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="vi"&gt;@products&lt;/span&gt;&lt;span class="p"&gt;)&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;table&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  table.column("Name") { |product| link_to product.name, product }
&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;The ViewComponent gem won't actually call the block, and the column method calls won't happen. We use the &lt;code&gt;before_render&lt;/code&gt; lifecycle method to ensure the block is called.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is this &lt;code&gt;view_context.capture(row, &amp;amp;column.td_block)&lt;/code&gt; doing?
&lt;/h3&gt;

&lt;p&gt;If in erb you define a column like this:&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="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;TableComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="vi"&gt;@products&lt;/span&gt;&lt;span class="p"&gt;)&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;table&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="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;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;The block will actually return only the last string, which is &lt;code&gt;&amp;lt;/div&amp;gt;&lt;/code&gt;. To capture the entire block's HTML content, we use &lt;code&gt;view_context.capture(row, &amp;amp;column.td_block)&lt;/code&gt;. This way, we ensure that all the HTML from the block is included.&lt;/p&gt;

&lt;p&gt;So there you have it, an easy-to-use but highly flexible table component.&lt;/p&gt;

&lt;p&gt;But this is just the beginning. You will be able to create more methods to handle special requirements and create title columns, image columns, and columns with action links.&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="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;TableComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="vi"&gt;@products&lt;/span&gt;&lt;span class="p"&gt;)&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;table&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  table.image_column :image, :featured_image
  table.title_column "Name", &lt;span class="err"&gt;&amp;amp;&lt;/span&gt;:name
  table.column("Description", &lt;span class="err"&gt;&amp;amp;&lt;/span&gt;:description)
  table.actions_column(:edit, :destroy)
&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 have the beginning of an abstraction here, that can be further extended. Stay tuned for more!&lt;/p&gt;

&lt;p&gt;&lt;small&gt;Disclaimer: This post was written entirely by Roland Studer, but revised with the help of chat gpt.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;This post was &lt;a href="https://rstuder.ch/2023-supercharged-table-component/"&gt;originally posted on rstuder.ch&lt;/a&gt;&lt;/p&gt;

</description>
      <category>viewcomponent</category>
      <category>rails</category>
    </item>
    <item>
      <title>More expressive APIs for View Components</title>
      <dc:creator>Roland Studer</dc:creator>
      <pubDate>Tue, 25 Jul 2023 23:22:28 +0000</pubDate>
      <link>https://dev.to/rolandstuder/more-expressive-apis-for-view-components-lbj</link>
      <guid>https://dev.to/rolandstuder/more-expressive-apis-for-view-components-lbj</guid>
      <description>&lt;p&gt;&lt;a href="https://viewcomponent.org"&gt;View components&lt;/a&gt; offer two primary ways to interact with the component: passing arguments to the initializer and using slots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;SomeComponent&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;some_params&lt;/span&gt;&lt;span class="p"&gt;)&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;component&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_some_slot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_other_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"My slot content"&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;Having worked with &lt;a href="https://dev.to/phlex-is-fun"&gt;Phlex (the better way to build views in Ruby)&lt;/a&gt;, I came to appreciate calling methods directly on the component. While the slot API offers a lot and is versatile, it can sometimes feel like you have to work around it rather than with it.&lt;/p&gt;

&lt;p&gt;Let's consider a &lt;code&gt;BreadCrumbsComponent&lt;/code&gt; as an example. Traditionally, one might pass in all the data using an array of hashes, 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="no"&gt;BreadCrumbsComponent&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="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;label: &lt;/span&gt;&lt;span class="s2"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;root_path&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;label: &lt;/span&gt;&lt;span class="s2"&gt;"Settings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;settings_path&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;label: &lt;/span&gt;&lt;span class="s2"&gt;"Notifications"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;settings_notifications_path&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;However, I prefer a more expressive API 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="no"&gt;BreadCrumbsComponent&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;bread&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;bread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crumb&lt;/span&gt; &lt;span class="s2"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root_path&lt;/span&gt;
  &lt;span class="n"&gt;bread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crumb&lt;/span&gt; &lt;span class="s2"&gt;"Settings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings_path&lt;/span&gt;
  &lt;span class="n"&gt;bread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crumb&lt;/span&gt; &lt;span class="s2"&gt;"Notifications"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings_notifications_path&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution with lambda slots
&lt;/h2&gt;

&lt;p&gt;To achieve the desired API using the ViewComponent gem, we can utilize &lt;a href="https://viewcomponent.org/guide/slots.html#lambda-slots"&gt;lambda slots&lt;/a&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;class&lt;/span&gt; &lt;span class="nc"&gt;BreadCrumbsComponent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ViewComponent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;renders_many&lt;/span&gt; &lt;span class="ss"&gt;:crumbs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vi"&gt;@paths&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;label: &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;url&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;With a lambda slot, you can create a similar API, though it forces you to use specific method naming, as you will need to call &lt;code&gt;c.with_crumb("Home", root_path)&lt;/code&gt; in the template.&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;BreadCrumbsComponent&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;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_crumb&lt;/span&gt; &lt;span class="s2"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root_path&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, there is an issue here. When iterating through the &lt;code&gt;@paths&lt;/code&gt; supposedly set by the slot calls, you will notice that it doesn't work as expected. The &lt;code&gt;with_crumb&lt;/code&gt; method did not have any effect. &lt;br&gt;
Why? ViewComponent only calls the block if you make a call to the accessor method for those slots, in this case &lt;code&gt;crumbs&lt;/code&gt;, which is what you do when in your template you are calling &lt;code&gt;crumbs.each …&lt;/code&gt;. This means that even if you don't render the slots, you still need to call the accessor method for it, a hint that we are somewhat abusing slots in this scenario.&lt;/p&gt;
&lt;h2&gt;
  
  
  Better solution
&lt;/h2&gt;

&lt;p&gt;My preferred approach would be to directly call an instance method on the component, 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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BreadCrumbsComponent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ViewComponent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;crumb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@paths&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;label: &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;url&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 this should work seamlessly:&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;BreadCrumbsComponent&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;bread&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;bread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crumb&lt;/span&gt; &lt;span class="s2"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root_path&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However the same caveat applied. The block is not called.&lt;/p&gt;

&lt;p&gt;To address this issue, I explored how I could call the block myself. &lt;br&gt;
The &lt;a href="https://github.com/ViewComponent/view_component/blob/3487283035bbcb868e46f93fcfb6abadfeed650a/lib/view_component/base.rb#L105"&gt;block is stored&lt;/a&gt; &lt;br&gt;
in &lt;code&gt;@__vc_render_in_block&lt;/code&gt;, a &lt;em&gt;very private looking&lt;/em&gt; instance variable.&lt;/p&gt;

&lt;p&gt;Then I realized that a call to &lt;code&gt;content&lt;/code&gt; would invoke the block. &lt;br&gt;
The solution is to add a  &lt;code&gt;before_render&lt;/code&gt; &lt;a href="https://viewcomponent.org/guide/lifecycle.html"&gt;lifecycle method&lt;/a&gt;.&lt;br&gt;
This calls the block and like this the &lt;code&gt;crumb&lt;/code&gt; method calls on the component will be performed as expected. So our component ruby file can 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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BreadcrumbsComponent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ViewComponent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:paths&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;before_render&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="c1"&gt;# ensures that block is called&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;initialize&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="vi"&gt;@paths&lt;/span&gt; &lt;span class="o"&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;crumb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@paths&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;label: &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;url&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;For further use, it would be a good idea to extract this into a module like &lt;code&gt;ViewComponent::CallBlock&lt;/code&gt; (or a better named module), and to then simply include it.&lt;/p&gt;

&lt;p&gt;While some may argue that passing this data directly might be a cleaner approach since it is essentially data, I wanted to explore if I could achieve this particular API with a view component.&lt;/p&gt;

&lt;p&gt;In conclusion: With a small tweak you can, directly calling instance methods on the component to shape your component. This way you can offer a very expressive and straightforward API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rstuder.ch/2023-calling-view-component-instance-methods/"&gt;Originally posted here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>viewcomponent</category>
      <category>phlex</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Phlex is the ruby way to build your views</title>
      <dc:creator>Roland Studer</dc:creator>
      <pubDate>Wed, 07 Jun 2023 21:02:08 +0000</pubDate>
      <link>https://dev.to/rolandstuder/phlex-is-the-ruby-way-to-build-your-views-1ejg</link>
      <guid>https://dev.to/rolandstuder/phlex-is-the-ruby-way-to-build-your-views-1ejg</guid>
      <description>&lt;p&gt;&lt;a href="https://www.phlex.fun"&gt;Phlex&lt;/a&gt; is an incredibly refreshing gem created by &lt;a href="https://github.com/joeldrapper"&gt;Joel Drapper&lt;/a&gt;. It introduces a remarkable way to build views in pure Ruby, as exemplified below:&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;Nav&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Phlex&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;template&lt;/span&gt;
    &lt;span class="n"&gt;nav&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;"main-nav"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;href: &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="s2"&gt;"Home"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;href: &lt;/span&gt;&lt;span class="s2"&gt;"/about"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"About"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;href: &lt;/span&gt;&lt;span class="s2"&gt;"/contact"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Contact"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially, you may wonder: Why? Why would you do that when HTML is already perfectly readable? Using Ruby here seems odd, detached from the underlying HTML I want to write.&lt;/p&gt;

&lt;p&gt;However, let's examine a typical partial, such as the one from the .&lt;br&gt;
&lt;a href="https://github.com/rubygems/rubygems.org/blob/5092832ce44d07b24a834f74afa9408e339d1ebf/app/views/searches/show.html.erb"&gt;rubygems.org search show page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The specific details aren't crucial; I'm only presenting an excerpt. Take note of the nested conditionals within.&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="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"search"&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="vi"&gt;@error_msg&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"errorExplanation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@error_msg&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&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="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"advanced_search"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;advanced_search_path&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;"t-link--gray t-link--has-arrow"&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="vi"&gt;@yanked_filter&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="vi"&gt;@subtitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.subtitle'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:query&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;content_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:em&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&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;:query&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@yanked_gem&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we find a significant amount of view logic nested within the HTML structure. To comprehend this template fully, one must examine both the HTML and the embedded Ruby code.&lt;/p&gt;

&lt;p&gt;With Phlex, however, we can fundamentally transform how we perceive and write these views. We can easily isolate the logic from the HTML nesting, resulting in something 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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Searches::Show&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Phlex&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;template&lt;/span&gt;
    &lt;span class="n"&gt;error_messages&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@yanked_filter&lt;/span&gt;
      &lt;span class="n"&gt;yanked_search&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;header&lt;/span&gt;
      &lt;span class="n"&gt;aggregations&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@gems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
        &lt;span class="n"&gt;search_results&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;suggestion_results&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;The &lt;code&gt;template&lt;/code&gt; method clearly displays the actual view logic of the full template. The appropriate instance methods provide the HTML. For me, Phlex isn't really about the HTML; in a way it is about everything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can write ruby and Phlex provides us the html tag methods we need.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have the freedom to choose the most suitable level of abstraction and refactor as we see fit. We have the elegance of Ruby at our disposal.&lt;/p&gt;

&lt;p&gt;I am genuinely enthusiastic about Phlex. It has completely transformed how I approach views, paving the way for elegant abstractions &lt;a href="https://fly.io/ruby-dispatch/component-driven-development-on-rails-with-phlex/#a-form-component-that-automatically-permits-strong-parameters"&gt;this one for forms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Working with your views as enjoyable as writing other ruby code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rstuder.ch/phlex-is-fun/"&gt;this article first appeared on rstuder.ch&lt;/a&gt;&lt;/p&gt;

</description>
      <category>phlex</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Delegated Types are an alternative to Single Table Inheritance</title>
      <dc:creator>Roland Studer</dc:creator>
      <pubDate>Thu, 07 Jul 2022 18:06:03 +0000</pubDate>
      <link>https://dev.to/rolandstuder/delegated-types-are-an-alternative-to-single-table-inheritance-25k7</link>
      <guid>https://dev.to/rolandstuder/delegated-types-are-an-alternative-to-single-table-inheritance-25k7</guid>
      <description>&lt;p&gt;&lt;a href="https://api.rubyonrails.org/classes/ActiveRecord/DelegatedType.html"&gt;Delegated types&lt;/a&gt; got quite a lot of attention when DHH was &lt;a href="https://twitter.com/dhh/status/1323936556424564736"&gt;tweeting about it&lt;/a&gt;&lt;br&gt;
But from what I heard from conversations with many people, &lt;br&gt;
I believe they have been poorly understood. &lt;br&gt;
By me in particular. &lt;br&gt;
Too often they are mentioned as flavour around polymorphic associations. &lt;/p&gt;

&lt;p&gt;While one could look at delegated types as convenience methods around polymorphic associations,&lt;br&gt;
DHHs intention was a quite different one.&lt;/p&gt;
&lt;h2&gt;
  
  
  The API does not make sense for convenience around polymorphic associations
&lt;/h2&gt;

&lt;p&gt;So let's have a look at what delegated types provide:&lt;/p&gt;

&lt;p&gt;Given a model that has a polymophic association with some other models, &lt;br&gt;
we can declare delegated types as such. &lt;br&gt;
I will itentionally start with "misguided" example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Recommendation&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;delegated_type&lt;/span&gt; &lt;span class="ss"&gt;:recommendable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;types: &lt;/span&gt;&lt;span class="sx"&gt;%w[ Wine Winery ]&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 a recommendation that can be for a wine or a winery. &lt;br&gt;
Now we have access to methods like these:&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;Recommendation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wines&lt;/span&gt;        &lt;span class="c1"&gt;# =&amp;gt; Recommendation.where(recommendable_type: "Wine")&lt;/span&gt;
&lt;span class="vi"&gt;@recommendation&lt;/span&gt;&lt;span class="c1"&gt;#wine?       # =&amp;gt; true when recommendable == "Message"&lt;/span&gt;
&lt;span class="vi"&gt;@recommendation&lt;/span&gt;&lt;span class="c1"&gt;#wine        # =&amp;gt; returns the wine record, when recommendable_type == "Wine", otherwise nil&lt;/span&gt;
&lt;span class="no"&gt;Recommendation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wineries&lt;/span&gt;     &lt;span class="c1"&gt;# =&amp;gt; Recommendation.where(entryable_type: "Comment")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice stuff. &lt;br&gt;
However this kinda confusing, let's look at one example:&lt;/p&gt;

&lt;p&gt;What does &lt;code&gt;Recommendation.wines&lt;/code&gt;  return? Since the method is called &lt;code&gt;wines&lt;/code&gt;, I woud expect to get the wine records back. But that is not what happens. I get the&lt;code&gt;recommendation&lt;/code&gt; records that are &lt;strong&gt;for wines&lt;/strong&gt;, instead.&lt;/p&gt;

&lt;p&gt;I found this API very confusing. So much so, that I proposed in a &lt;a href="https://github.com/rails/rails/pull/41506"&gt;pull request&lt;/a&gt; to add aliases for some of the methods. Allowing calls like &lt;code&gt;Recommendation.for_wines&lt;/code&gt;, which makes it more obvious that this is returning recommendation records,  rather than wine records.&lt;/p&gt;
&lt;h2&gt;
  
  
  So what is going on here? Why is this so confusing?
&lt;/h2&gt;

&lt;p&gt;I often wondered about the name, and talking with other people about the PR I realized something. I missunderstood the intention behind delegated types.&lt;/p&gt;

&lt;p&gt;An extract from the &lt;a href="https://api.rubyonrails.org/classes/ActiveRecord/DelegatedType.html"&gt;documentation&lt;/a&gt; on delegated types:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can get around the pagination problem by using single table inheritance, but now you're forced into a single mega table with all the attributes from all subclasses. No matter how divergent. If a Message has a subject, but the comment does not, well, now the comment does anyway! So STI works best when there's little divergence between the subclasses and their attributes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Actually if you read the entire docs, it fully introduces delegated types not as convenience around polymorphic associations. They are presented as as an alternative to single table inheritance or multiple tables.&lt;/p&gt;

&lt;p&gt;So if you have objects that you want to list / query together, delegated types are a good option.&lt;/p&gt;

&lt;p&gt;So if you look at it that way the API and the name &lt;code&gt;DelegatedType&lt;/code&gt; start to makea lot more sense.  While &lt;code&gt;Recommendation.wines&lt;/code&gt; will return records of the &lt;code&gt;reccomendations&lt;/code&gt; table, it is because we declared wrongly that a recommendation for a wine &lt;strong&gt;is of type Wine&lt;/strong&gt;, its delegated type &lt;strong&gt;is&lt;/strong&gt; wine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This will make way more sense, once I show the more reasonable example:&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A fitting example
&lt;/h2&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;Product&lt;/span&gt;
    &lt;span class="n"&gt;delegated_type&lt;/span&gt; &lt;span class="ss"&gt;:purchasable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;types: &lt;/span&gt;&lt;span class="sx"&gt;%w[Wine Bundle Voucher]&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 let's say we build some kind of shop where we sell wine, bundles of wines, but also vouchers. We really want to be able to query them from one table, and paginate them, but they vary widely in terms of attributes, so STI (single table inheritance) is not a great solution.&lt;/p&gt;

&lt;p&gt;This is where delegated types come in. We can have the shared attributes of the products table, like product_number, price, stock, published… But we can also have any attributes we need on the specific table: Maybe a Voucher has an expiration, a bundle will have multiple wines as an assocation…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So with delegated types we consider the &lt;code&gt;wine&lt;/code&gt; record to be the "joined row" of the &lt;code&gt;products&lt;/code&gt; table and the &lt;code&gt;wines&lt;/code&gt; table.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whatever we need to know from the product &lt;code&gt;wine&lt;/code&gt; we either get from the product record or its delegated type, hence the name… The idea is to not actually instantiate a record from the wines table alone.&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;Product&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;delegated_type&lt;/span&gt; &lt;span class="ss"&gt;:purchasable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;types: &lt;/span&gt;&lt;span class="sx"&gt;%w[Wine Bundle Voucher]&lt;/span&gt;
  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:display_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :purchasable&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;Wine&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_name&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;vintage&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;Bundle&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;:wines&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_name&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;wines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; wines)"&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;Voucher&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_name&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;$ Voucher"&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 you will have some aspects on the Product, and more specific aspects on the delegated types.  You can leverage OOP polymorphism and be way more flexible than you are with STI.&lt;/p&gt;

&lt;p&gt;So delegated types can be a powerful tool, when you want something to live on one table, so it is easily queryable and sometimes more importantly you can make use of pagination with it.&lt;/p&gt;

&lt;p&gt;first published on &lt;a href="https://rstuder.ch/delegated-types/"&gt;rstuder.ch&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
    </item>
    <item>
      <title>Custom Turbo Stream Actions</title>
      <dc:creator>Roland Studer</dc:creator>
      <pubDate>Sun, 15 May 2022 22:29:27 +0000</pubDate>
      <link>https://dev.to/rolandstuder/custom-turbo-stream-actions-2gh1</link>
      <guid>https://dev.to/rolandstuder/custom-turbo-stream-actions-2gh1</guid>
      <description>&lt;p&gt;Turbo Hotwire is neat, I really like the simplicity of Turbo frames. But after having used &lt;a href="http://cableready.stimulusreflex.com"&gt;CableReady&lt;/a&gt; the turbo streams feel a bit limiting. I was surprised, when I could not find any gem/package that enhances turbo streams to create custom turbo actions.&lt;/p&gt;

&lt;p&gt;Disclaimer: This post won't make much sense to you, if you are not familiar with &lt;a href="https://turbo.hotwired.dev/handbook/streams"&gt;Turbo streams&lt;/a&gt; and &lt;a href="https://stimulus.hotwired.dev"&gt;StimulusJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's say if you want to redirect to a new page after some job is done, or you want to reset a form, there is no way to do that with a turbo stream action. With CableReady you could do &lt;br&gt;
&lt;code&gt;cable_ready.redirect_to(url: some_url)&lt;/code&gt; (&lt;a href="http://cableready.stimulusreflex.com"&gt;docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So I was wondering how one would go about creating custom turbo stream actions. From how DHH (the original creator of Rails) has discussed turbo streams, it is unlikely that turbo will support more actions, there is a&lt;br&gt;
&lt;a href="https://github.com/hotwired/turbo/pull/479"&gt;pending PR&lt;/a&gt; but not much has moved.&lt;/p&gt;

&lt;p&gt;So what can we do today?&lt;/p&gt;
&lt;h2&gt;
  
  
  Stimulus controllers to the rescue
&lt;/h2&gt;

&lt;p&gt;We can mimic the pattern turbo streams use with StimulusJS. StimulusJS has a lifecycle method called connect, that is executed when a new element with a stimulus controller is added to the DOM.&lt;/p&gt;

&lt;p&gt;For the redirect example, we can write something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// take a value as an argument where to redirect&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// perform wanted behavior&lt;/span&gt;
        &lt;span class="nx"&gt;Turbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// clean up after action is executed&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the following html added anywhere in the body would perform a redirect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"redirect"&lt;/span&gt; &lt;span class="na"&gt;data-redirect-url-value=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;%= your_redirect_url %&amp;gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So with a turbo stream we can attach this to the body:&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="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append_all&lt;/span&gt; &lt;span class="s2"&gt;"body"&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="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"redirect"&lt;/span&gt; &lt;span class="na"&gt;data-redirect-url-value=&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;your_redirect_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="nt"&gt;&amp;gt;&amp;lt;/template&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;And the browser will redirect, given this turbo stream response. BTW: We use &lt;code&gt;append_all&lt;/code&gt; so we can append to the body, withouth having to rely on the presence of an element with a certain id.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better developer experience
&lt;/h2&gt;

&lt;p&gt;It's not the most elegant solution, but it works and has only little overhead. We can improve the developer experience by writing a helper to allow something like:&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="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_stream_action&lt;/span&gt; &lt;span class="ss"&gt;:redirect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="s2"&gt;"http://www.rstuder.ch"&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;So we can write a helper that creates a turbo stream tag that will append to the body and convert a hash of values to values for a stimulus controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;TurboStreamHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;turbo_stream_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;controller_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dasherize&lt;/span&gt;

    &lt;span class="n"&gt;data_attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"data-controller"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;controller_name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;data_attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"data-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;controller_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dasherize&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-value"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;turbo_stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append_all&lt;/span&gt; &lt;span class="s2"&gt;"body"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;content_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:template&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="n"&gt;data_attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="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;So we got a simple API around some conventions, that gives us the power to create custom actions and invoke them with arguments from a turbo stream template.&lt;/p&gt;

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

&lt;p&gt;Turbo streams with Stimulus Controllers give us enough power to achieve custom actions. Without further dependencies. If you use CableReady anyway, then have a look at &lt;br&gt;
&lt;a href="https://github.com/marcoroth/cable-streams"&gt;this package from the wonderful Marco Roth&lt;/a&gt; that enables you to use the cable ready operations with turbo streams. &lt;/p&gt;

&lt;p&gt;ps: This post was originally published on &lt;a href="https://rstuder.ch/custom-turbo-stream-actions/"&gt;rstuder.ch&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>hotwire</category>
      <category>stimulus</category>
      <category>turbostreams</category>
    </item>
    <item>
      <title>Ruby refinements have a second good use case.</title>
      <dc:creator>Roland Studer</dc:creator>
      <pubDate>Sun, 15 Nov 2020 23:04:13 +0000</pubDate>
      <link>https://dev.to/rolandstuder/ruby-refinements-have-a-second-good-use-case-42jk</link>
      <guid>https://dev.to/rolandstuder/ruby-refinements-have-a-second-good-use-case-42jk</guid>
      <description>&lt;p&gt;According to Bradley &lt;a href="http://www.soulcutter.com/articles/ruby-refinements-have-one-good-use-case.html"&gt;Refinements have one good use case&lt;/a&gt;: Conversion Wrappers. I humbly present the second one.&lt;/p&gt;




&lt;p&gt;Monkey patching in Ruby is a simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;red&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[#31m&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[0m"&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 what if you don't to pollute the &lt;code&gt;String&lt;/code&gt;-class for users of your Gem? In the worst case you might actually overwrite a monkey patch provided by the developer. We faced this when we wanted to provide coloring of strings for our logger in &lt;a href="https://docs.stimulusreflex.com/"&gt;StimulusReflex&lt;/a&gt;. We wanted our users to be able to write a configuration proc 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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;proc&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="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cyan&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;reflex_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;red&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enter &lt;code&gt;refine&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;With refinements we were able to monkey patch only where explicitly called for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Colorize&lt;/span&gt;
  &lt;span class="n"&gt;refine&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;red&lt;/span&gt;
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[#31m&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s2"&gt;[0m"&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;Now you can activate this monkey patch in whatever class you need it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;
  &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="no"&gt;Colorize&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;error&lt;/span&gt;
    &lt;span class="s2"&gt;"Ooops"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;red&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;h1&gt;
  
  
  A complication
&lt;/h1&gt;

&lt;p&gt;Procs are a convenient solution, however they get the binding/context of where they are defined, not of where they are called. Just calling the proc in our &lt;code&gt;Logger&lt;/code&gt;class will not make use of the &lt;code&gt;colorize&lt;/code&gt;-refinement, actually no method of the &lt;code&gt;Logger&lt;/code&gt;class is available to the proc, because it was defined in a completely different context (in an initializer of the gem user).&lt;/p&gt;

&lt;p&gt;So we made use of &lt;a href="https://apidock.com/ruby/Object/instance_eval"&gt;instance_eval&lt;/a&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;class&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;
  &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="no"&gt;Colorize&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;instance_eval&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logging&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;See the commit for &lt;a href="https://github.com/hopsoft/stimulus_reflex/pull/337/commits/ef5e8d0047962740dd5e48919e1c5d3571172d9a"&gt;adding colorize&lt;/a&gt; and &lt;a href="https://github.com/hopsoft/stimulus_reflex/pull/337/commits/0e301d63951eb1910983ddb7c9a934d7d1225e58"&gt;using instance_eval&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Lightning fast reactive action with Stimulus Reflex partial morphs</title>
      <dc:creator>Roland Studer</dc:creator>
      <pubDate>Tue, 22 Sep 2020 07:24:12 +0000</pubDate>
      <link>https://dev.to/rolandstuder/lightning-fast-reactive-action-with-stimulus-reflex-partial-morphs-2fg5</link>
      <guid>https://dev.to/rolandstuder/lightning-fast-reactive-action-with-stimulus-reflex-partial-morphs-2fg5</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.stimulusreflex.com/" rel="noopener noreferrer"&gt;Stimulus reflex&lt;/a&gt; is an awesome way to build reactive single-page-like applications with Ruby on Rails, with minimal Javascript.&lt;/p&gt;

&lt;p&gt;The newest update v 3.3.0 introduces &lt;a href="https://docs.stimulusreflex.com/morph-modes#selector-morphs" rel="noopener noreferrer"&gt;partial morphs&lt;/a&gt;, that don't go through the controller action anymore, and it allows us to make reflexes lightning fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does a reflex action work? (simplified)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You &lt;strong&gt;trigger a reflex&lt;/strong&gt; on the client (by data attribute, or in a stimulus method).&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;server side &lt;code&gt;Reflex&lt;/code&gt; executes&lt;/strong&gt;, where you can trigger jobs, set instance variables, do db-stuff.&lt;/li&gt;
&lt;li&gt;Then the &lt;strong&gt;original controller action is performed&lt;/strong&gt;, but with the newly set instance variables and any updated state of the database.&lt;/li&gt;
&lt;li&gt;The page on the &lt;strong&gt;client updates&lt;/strong&gt; with morphdom over a websocket connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this gives us a lot of convenience as we reuse all the logic of the original controller action that rendered a page initially.&lt;br&gt;
However this convenience also means that every reflex can only be about as fast as the controller action it performs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introducing &lt;code&gt;morph :selector&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This release will introduce &lt;a href="https://docs.stimulusreflex.com/morph-modes#introducing-morph-modes" rel="noopener noreferrer"&gt;different morph modes&lt;/a&gt;&lt;br&gt;
that allow you to skip the controller action and instead render a partial, a view_component or a string, that you then morph into the page.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let us look at an example:
&lt;/h2&gt;

&lt;p&gt;In my application I have a timeline with 21 days where I can add orders to those days. Executing the controller action and rendering takes around 180ms,&lt;br&gt;
which is not horrible, but definitely not great.&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%2Fi%2Fsv9alzxhk77va2lf32jm.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsv9alzxhk77va2lf32jm.png" alt="Timeline with orders"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To delete an item in the timeline we can have a simple reflex:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;timeline_reflex.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;
  &lt;span class="no"&gt;ScheduledOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:scheduled&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is triggered from  a simple button with the right data attributes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-reflex=&lt;/span&gt;&lt;span class="s"&gt;"click-&amp;gt;My::TimelineReflex#delete"&lt;/span&gt; &lt;span class="na"&gt;data-scheduled-order-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;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So simple, so slim, so convenient.&lt;/p&gt;

&lt;p&gt;But every delete reflex action, will include those 180ms to execute the controller action, meh.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speeding up with with partial morph.
&lt;/h2&gt;

&lt;p&gt;Now we can leverage only doing a partial update.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;timeline_reflex.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;
  &lt;span class="c1"&gt;# we delete&lt;/span&gt;
  &lt;span class="no"&gt;ScheduledOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:order_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="c1"&gt;# we get all the data we need to render the component&lt;/span&gt;
  &lt;span class="vi"&gt;@date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:date&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="vi"&gt;@pauses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pauses&lt;/span&gt;
  &lt;span class="vi"&gt;@scheduled_orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheduled_orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;date: &lt;/span&gt;&lt;span class="vi"&gt;@date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;# we call morph with a selector and pass the rendered component into it.&lt;/span&gt;
  &lt;span class="n"&gt;morph&lt;/span&gt; &lt;span class="s2"&gt;"#&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;day_dom_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="no"&gt;ApplicationController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;My&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TimelineDayComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;day: &lt;/span&gt;&lt;span class="vi"&gt;@date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;scheduled_orders: &lt;/span&gt;&lt;span class="vi"&gt;@scheduled_orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;pauses: &lt;/span&gt;&lt;span class="vi"&gt;@pauses&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;Now instead of doing all the controller action stuff, and rendering 21 days components. We just render one day component.&lt;br&gt;
So our time to execute the reflex action goes down from 180ms to about 30ms. &lt;strong&gt;30 milliseconds!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With partial morphs we get SPA-speed interactions, without ever having to handle client side state. I love it!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>stimulusreflex</category>
    </item>
  </channel>
</rss>
