<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: hungle00</title>
    <description>The latest articles on Forem by hungle00 (@hungle00).</description>
    <link>https://forem.com/hungle00</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%2F115379%2F0190108a-11f3-4019-9261-bbe3394c8b62.jpg</url>
      <title>Forem: hungle00</title>
      <link>https://forem.com/hungle00</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hungle00"/>
    <language>en</language>
    <item>
      <title>HTMX for building a SQLite query dashboard</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Mon, 09 Mar 2026 06:57:41 +0000</pubDate>
      <link>https://forem.com/hungle00/htmx-for-building-a-sqlite-query-dashboard-32ck</link>
      <guid>https://forem.com/hungle00/htmx-for-building-a-sqlite-query-dashboard-32ck</guid>
      <description>&lt;p&gt;I recently built &lt;a href="https://pypi.org/project/sqlite-opus/" rel="noopener noreferrer"&gt;Sqlite Opus&lt;/a&gt;, a small web-based SQLite browser that plugs into Flask. The UI is a single-page dashboard where you can click around to explore the current database. At first, I used &lt;code&gt;fetch&lt;/code&gt; calls with Flask partials, but later switched to HTMX to reduce JavaScript while keeping the same interactive feel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is HTMX?&lt;/strong&gt; A tiny JS lib: you put attributes like &lt;code&gt;hx-get&lt;/code&gt;, &lt;code&gt;hx-post&lt;/code&gt;, &lt;code&gt;hx-target&lt;/code&gt; on your HTML, and it does the AJAX + DOM swap for you. Server returns HTML chunks; no &lt;code&gt;fetch()&lt;/code&gt; + JSON + manual DOM updates. Nice for stuff like dashboards and admin UIs where the server already renders the page—you just want “click this, replace that” without a fat SPA.&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk through how I use HTMX in this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where HTMX fits in this project
&lt;/h2&gt;

&lt;p&gt;I use HTMX for three main flows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load table info — Click a table name → load schema, columns, and indexes without a full page reload.&lt;/li&gt;
&lt;li&gt;Execute query — Submit the query form → replace the results area with the new table (or error message).&lt;/li&gt;
&lt;li&gt;Pagination — Click a page number → request the same query for another page and swap the results area.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  How HTMX is used
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Load table (schema, columns, indexes)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Where:&lt;/strong&gt; Sidebar “Tables” list in &lt;code&gt;index.html&lt;/code&gt;. Each table name is a clickable element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Behavior:&lt;/strong&gt; On click, HTMX sends a &lt;strong&gt;GET&lt;/strong&gt; request to load that table’s metadata. The server returns one HTML response that contains &lt;strong&gt;three fragments&lt;/strong&gt;. HTMX swaps each fragment into the matching element by &lt;code&gt;id&lt;/code&gt;, so all three panels update from a single request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Markup (concept):&lt;/strong&gt;&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"table-item"&lt;/span&gt;
     &lt;span class="na"&gt;data-table-name=&lt;/span&gt;&lt;span class="s"&gt;"{{ table }}"&lt;/span&gt;
     &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Click to view schema"&lt;/span&gt;
     &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ url_for(blueprint_name ~ '.get_table_info_partial', table_name=table) }}"&lt;/span&gt;
     &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"click"&lt;/span&gt;
     &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"body"&lt;/span&gt;
     &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-table"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt; {{ table }}
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hx-get&lt;/code&gt;&lt;/strong&gt; — URL: &lt;code&gt;GET /api/table/&amp;lt;table_name&amp;gt;/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hx-trigger="click"&lt;/code&gt;&lt;/strong&gt; — Request is sent when the user clicks the table name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hx-target="body"&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;hx-swap="none"&lt;/code&gt;&lt;/strong&gt; — The main response body is not swapped anywhere; only 3 fragments are applied.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Server:&lt;/strong&gt; The route &lt;code&gt;get_table_info_partial&lt;/code&gt; renders &lt;code&gt;partials/table_info.html&lt;/code&gt;, which outputs three divs with &lt;code&gt;hx-swap-oob="true"&lt;/code&gt; and these ids:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;table-columns-container&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;table-indexes-container&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;table-schema-container&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So one response updates all three panels. JavaScript listens for &lt;code&gt;htmx:beforeRequest&lt;/code&gt; and &lt;code&gt;htmx:afterSettle&lt;/code&gt; to show a loading state, switch to the “Table Schema” tab, and prefill the query editor with &lt;code&gt;SELECT * FROM &amp;lt;table&amp;gt;;&lt;/code&gt; for convenience.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Execute query
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Where:&lt;/strong&gt; Query editor form in &lt;code&gt;index.html&lt;/code&gt; (textarea + Execute button).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Behavior:&lt;/strong&gt; Submitting the form sends a &lt;strong&gt;POST&lt;/strong&gt; with the SQL and pagination parameters. The server runs the query and returns the &lt;strong&gt;query results partial&lt;/strong&gt; (the results table plus the pagination bar). HTMX replaces the contents of the results area with that HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Markup (concept):&lt;/strong&gt;&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;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"query-form"&lt;/span&gt;
      &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"{{ url_for(blueprint_name ~ '.execute_query') }}"&lt;/span&gt;
      &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#query-results-area"&lt;/span&gt;
      &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"query-editor"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"per_page"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"execute-btn"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Execute Query&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"clear-btn"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Clear&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hx-post&lt;/code&gt;&lt;/strong&gt; — URL: &lt;code&gt;POST /api/query/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hx-target="#query-results-area"&lt;/code&gt;&lt;/strong&gt; — Response replaces the inner HTML of the results container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hx-swap="innerHTML"&lt;/code&gt;&lt;/strong&gt; — The new results table and pagination bar are inserted inside that container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Server:&lt;/strong&gt; The &lt;code&gt;execute_query&lt;/code&gt; route reads &lt;code&gt;query&lt;/code&gt;, &lt;code&gt;page&lt;/code&gt;, and &lt;code&gt;per_page&lt;/code&gt; from the form data. It runs the query (with pagination for SELECTs), then renders &lt;code&gt;partials/query_results.html&lt;/code&gt; and returns that HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error handling:&lt;/strong&gt; If the user submits an empty query, we don’t send the request: a &lt;code&gt;htmx:beforeRequest&lt;/code&gt; handler checks the form and the textarea, calls &lt;code&gt;preventDefault()&lt;/code&gt;, and shows a “Please enter a query” message in the top status banner. For other errors, an &lt;code&gt;htmx:responseError&lt;/code&gt; handler shows the error in the same banner.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Pagination
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Where:&lt;/strong&gt; Pagination bar inside &lt;code&gt;partials/query_results.html&lt;/code&gt;. It is rendered only when there is more than one page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Behavior:&lt;/strong&gt; Each page number, plus Prev and Next, is a &lt;strong&gt;submit&lt;/strong&gt; button inside a form. Submitting sends the &lt;strong&gt;same query&lt;/strong&gt; and &lt;strong&gt;per_page&lt;/strong&gt; with a new &lt;strong&gt;page&lt;/strong&gt; value. The server runs the query for that page and returns the same &lt;code&gt;query_results.html&lt;/code&gt; partial; HTMX swaps it into the same &lt;code&gt;#query-results-area&lt;/code&gt; container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Markup (concept):&lt;/strong&gt;&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;form&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"{{ url_for(blueprint_name ~ '.execute_query') }}"&lt;/span&gt;
      &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#query-results-area"&lt;/span&gt;
      &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ current_query }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"per_page"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ pagination.per_page }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Prev --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ pagination.page - 1 }}"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Prev&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Page numbers --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ i }}"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ i }}&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Next --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ pagination.page + 1 }}"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Next&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;current_query&lt;/code&gt;&lt;/strong&gt; — Passed from the route into the template so the pagination form always re-sends the last executed query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;name="page"&lt;/code&gt;&lt;/strong&gt; — Each button submits a different &lt;code&gt;page&lt;/code&gt; value; the server uses it for &lt;code&gt;LIMIT&lt;/code&gt;/&lt;code&gt;OFFSET&lt;/code&gt; (or equivalent).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Server:&lt;/strong&gt; The same &lt;code&gt;execute_query&lt;/code&gt; route handles both the initial run and pagination. When the request comes from this form, &lt;code&gt;query&lt;/code&gt; and &lt;code&gt;per_page&lt;/code&gt; are in the form data, and &lt;code&gt;page&lt;/code&gt; is the value of the clicked button. The route runs the query for that page and returns the updated &lt;code&gt;query_results.html&lt;/code&gt; partial.&lt;/p&gt;




&lt;p&gt;You can see detail implement in the repo: &lt;a href="https://github.com/hungle00/sqlite-opus" rel="noopener noreferrer"&gt;https://github.com/hungle00/sqlite-opus&lt;/a&gt;&lt;/p&gt;

</description>
      <category>htmx</category>
      <category>flask</category>
      <category>python</category>
      <category>sqlite3</category>
    </item>
    <item>
      <title>From Ruby OOP to Elixir Functional by Example</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Tue, 02 Dec 2025 04:00:02 +0000</pubDate>
      <link>https://forem.com/hungle00/from-ruby-oop-to-elixir-functional-11mp</link>
      <guid>https://forem.com/hungle00/from-ruby-oop-to-elixir-functional-11mp</guid>
      <description>&lt;p&gt;One of the most effective ways to learn functional programming is to convert the same features you've written in an OOP language to functional style. In this post, I won't go into theoretical details about functional programming. Instead, I'll implement and compare a concrete example to highlight the differences between thinking in OOP vs functional paradigms.&lt;/p&gt;

&lt;p&gt;For example, I have a &lt;code&gt;ToyRobot&lt;/code&gt; object in Ruby. In OOP terms, this object has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 attributes: &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;direction&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Some actions: &lt;code&gt;move&lt;/code&gt;, &lt;code&gt;turn left&lt;/code&gt;, &lt;code&gt;turn right&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is the code implemented in Ruby:&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;ToyRobot&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&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="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:direction&lt;/span&gt;
  &lt;span class="no"&gt;DIRECTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:north&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:east&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:south&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:west&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="no"&gt;BOUNDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;x: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="vi"&gt;@y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="vi"&gt;@direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:north&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;place&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;direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;DIRECTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
      &lt;span class="vi"&gt;@y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
      &lt;span class="vi"&gt;@direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Invalid direction"&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;report&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;x: &lt;/span&gt;&lt;span class="vi"&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="vi"&gt;@y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;direction: &lt;/span&gt;&lt;span class="vi"&gt;@direction&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;move&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="vi"&gt;@direction&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:north&lt;/span&gt;
      &lt;span class="vi"&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="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:east&lt;/span&gt;
      &lt;span class="vi"&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="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:south&lt;/span&gt;
      &lt;span class="vi"&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="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:west&lt;/span&gt;
      &lt;span class="vi"&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="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BOUNDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:x&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="vi"&gt;@x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;BOUNDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:x&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="vi"&gt;@y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BOUNDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:y&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="vi"&gt;@y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;BOUNDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:y&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Out of bounds"&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;right&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="vi"&gt;@direction&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:north&lt;/span&gt;
      &lt;span class="vi"&gt;@direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:east&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:east&lt;/span&gt;
      &lt;span class="vi"&gt;@direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:south&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:south&lt;/span&gt;
      &lt;span class="vi"&gt;@direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:west&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:west&lt;/span&gt;
      &lt;span class="vi"&gt;@direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:north&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;left&lt;/span&gt;
    &lt;span class="c1"&gt;# implement left&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;I'll build the same thing in Elixir. If you're new to Elixir, I encourage you to try implementing it yourself before checking my code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@directions&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:north&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:east&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:south&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:west&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nv"&gt;@bounds_x&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="nv"&gt;@bounds_y&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;

  &lt;span class="k"&gt;defstruct&lt;/span&gt; &lt;span class="ss"&gt;x:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;direction:&lt;/span&gt; &lt;span class="ss"&gt;:east&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;place&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:east&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;robot&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;place&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;direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;@directions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:invalid_direction&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="n"&gt;place&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;_direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;@bounds_x&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;@bounds_y&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:out_of_bounds&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="n"&gt;place&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;direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;ToyRobot&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="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;direction:&lt;/span&gt; &lt;span class="n"&gt;direction&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="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;ToyRobot&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="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;direction:&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&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;direction&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="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;direction:&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;new_direction&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;direction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="ss"&gt;:north&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:east&lt;/span&gt;
        &lt;span class="ss"&gt;:east&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:south&lt;/span&gt;
        &lt;span class="ss"&gt;:south&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:west&lt;/span&gt;
        &lt;span class="ss"&gt;:west&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:north&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;direction:&lt;/span&gt; &lt;span class="n"&gt;new_direction&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="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;direction:&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# implement right&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;ToyRobot&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="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;direction:&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;new_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_y&lt;/span&gt;&lt;span class="p"&gt;}&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;direction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="ss"&gt;:north&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;x&lt;/span&gt;&lt;span class="p"&gt;,&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;:east&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;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="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="ss"&gt;:south&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;x&lt;/span&gt;&lt;span class="p"&gt;,&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;:west&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;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="n"&gt;y&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;if&lt;/span&gt; &lt;span class="n"&gt;new_x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;@bounds_x&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;new_y&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;@bounds_y&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;x:&lt;/span&gt; &lt;span class="n"&gt;new_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;new_y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;robot&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;h2&gt;
  
  
  Key Differences
&lt;/h2&gt;

&lt;p&gt;So what are the differences between these two implementations? I think there are two important differences that highlight the different ways of thinking between OOP and functional languages:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Method vs Function
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;In Ruby&lt;/strong&gt;, we have objects. Methods are functions that belong to a class. When you call an instance method, you can think of the &lt;code&gt;ToyRobot&lt;/code&gt; taking an action like &lt;code&gt;move&lt;/code&gt; or &lt;code&gt;left&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="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;
&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;left&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In Elixir&lt;/strong&gt;, we don't have objects. Modules are just used to group functions. When we call &lt;code&gt;ToyRobot.move(robot)&lt;/code&gt;, we're applying the &lt;code&gt;move&lt;/code&gt; function to the robot's data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Elixir function definitions, we need to pass all required data to the function as arguments, but the Ruby version doesn't need to. This is because, in OOP languages, objects know their state (the position of the robot in our case). In Elixir, functions just receive and transform data—taking the robot's position and returning a new one.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Mutable vs Immutable
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;In Ruby&lt;/strong&gt;, every time we call a method, we change the state of the &lt;code&gt;ToyRobot&lt;/code&gt; object:&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;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;  &lt;span class="c1"&gt;# @x, @y change in the object&lt;/span&gt;
&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; {x: 0, y: 1, direction: :north}&lt;/span&gt;

&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;  &lt;span class="c1"&gt;# Continues to change the same object&lt;/span&gt;
&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; {x: 0, y: 2, direction: :north}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In Elixir&lt;/strong&gt;, data is immutable. Every time we call a function, it returns a new struct and does not change the input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; %ToyRobot{x: 0, y: 0, direction: :east}&lt;/span&gt;

&lt;span class="n"&gt;new_robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; %ToyRobot{x: 1, y: 0, direction: :east}&lt;/span&gt;

&lt;span class="n"&gt;robot&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; %ToyRobot{x: 0, y: 0, direction: :east}  # Original unchanged!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Elixir provides the pipe operator (&lt;code&gt;|&amp;gt;&lt;/code&gt;) so we can call functions sequentially:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ToyRobot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In conclusion&lt;/strong&gt;, the fundamental difference is in how we think about state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ruby (OOP)&lt;/strong&gt;: "The object changes its own state"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elixir (Functional)&lt;/strong&gt;: "The function receives data and returns new data"&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>elixir</category>
      <category>functional</category>
    </item>
    <item>
      <title>Ruby - use Redis-lock to avoid duplicate jobs</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Fri, 25 Apr 2025 07:57:17 +0000</pubDate>
      <link>https://forem.com/hungle00/ruby-use-redis-lock-to-avoid-duplicate-jobs-51e</link>
      <guid>https://forem.com/hungle00/ruby-use-redis-lock-to-avoid-duplicate-jobs-51e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In a Rails app, sometimes you need to execute a long-running job and have to run it in the background. You might run into some special requirements to avoid duplicate jobs&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 1
&lt;/h3&gt;

&lt;p&gt;You must delete the user and all related data in &lt;code&gt;CleanupDataJob&lt;/code&gt; job. It will take a long time, and to avoid some problems, such as database-locking, you need to keep only one user deleted at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 2
&lt;/h3&gt;

&lt;p&gt;You have the &lt;code&gt;ProductSalesJob&lt;/code&gt; to fetch the sales data of the product and calculate some numbers. What if you had 100 of these jobs? Your queue will have 100 jobs doing the exact same thing, and quickly pile up. So you need to avoid duplicate jobs that do the same thing for a single product.&lt;/p&gt;

&lt;p&gt;To avoid more than one job executing at a time, you'll need the locking mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Redis-based locking
&lt;/h2&gt;

&lt;p&gt;This solution is creating a Redis lock for each scheduled job. Depending on the uniqueness constraint, it will reject any jobs that match the same signature.&lt;/p&gt;

&lt;p&gt;The main idea of using Redis-lock can be described as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Try to Acquire Lock&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Success → Proceed to Critical Section → Release Lock.&lt;/li&gt;
&lt;li&gt;Failure → Log Message → Re-enqueue Job.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Critical Section&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perform job logic.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Release Lock&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always release the lock, even on failure or exception, using ensure.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full implementation for &lt;strong&gt;problem 1&lt;/strong&gt; is here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 1: Solution&lt;/strong&gt;&lt;br&gt;
In this case, we'll periodically call the job in 2 minutes if it has another job acquiring the lock.&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="s1"&gt;'redis'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CleanupDataJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="n"&gt;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;

  &lt;span class="c1"&gt;# Redis lock configurations&lt;/span&gt;
  &lt;span class="no"&gt;WAIT_FOR_NEXT_CALLING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt;
  &lt;span class="no"&gt;LOCK_KEY_EXPIRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt; &lt;span class="c1"&gt;# Prevent stale locks&lt;/span&gt;
  &lt;span class="no"&gt;REDIS_LOCK_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cleanup_data_job_lock"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

    &lt;span class="c1"&gt;# Try to acquire the lock&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;acquire_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="c1"&gt;# Perform the job logic&lt;/span&gt;
        &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"Executing &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&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; with user: &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="n"&gt;cleanup_data&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
      &lt;span class="k"&gt;ensure&lt;/span&gt;
        &lt;span class="c1"&gt;# Release the lock&lt;/span&gt;
        &lt;span class="n"&gt;release_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&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;else&lt;/span&gt;
      &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"Job already running: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&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; with user: &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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;wait: &lt;/span&gt;&lt;span class="no"&gt;WAIT_FOR_NEXT_CALLING&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_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;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cleanup_data&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate a long-running task&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"Data cleanup completed."&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;acquire_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Set the lock key with NX (set if not exists) and expiration&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;REDIS_LOCK_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"locked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;nx: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ex: &lt;/span&gt;&lt;span class="no"&gt;LOCK_KEY_EXPIRATION&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;release_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;REDIS_LOCK_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem 2: Solution&lt;/strong&gt;&lt;br&gt;
This problem is quite similar to problem 1, the difference is that we must keep a unique job for each job with passing argument, not for all instances of the job. To do that, we pass the argument as a part of lock_key, so instead of &lt;code&gt;product_sales_job_lock&lt;/code&gt;, we use &lt;code&gt;product_sales_job_lock#{product_id}&lt;/code&gt;. Besides, we will not retry the job if another job with the same &lt;code&gt;product_id&lt;/code&gt; is processing.&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="s1"&gt;'redis'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductSalesJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="n"&gt;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;

  &lt;span class="no"&gt;LOCK_KEY_EXPIRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt; &lt;span class="c1"&gt;# Prevent stale locks&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;lock_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"product_sales_job_lock&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Try to acquire the lock&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;acquire_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lock_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="c1"&gt;# Perform the job logic&lt;/span&gt;
        &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"Executing &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&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; with product: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="n"&gt;fetch_product_sales&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;
      &lt;span class="k"&gt;ensure&lt;/span&gt;
        &lt;span class="c1"&gt;# Release the lock&lt;/span&gt;
        &lt;span class="n"&gt;release_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lock_key&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;else&lt;/span&gt;
      &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"Job already running: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&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; with product: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;product_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="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_product_sales&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate processing logic&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



</description>
      <category>ruby</category>
      <category>redis</category>
    </item>
    <item>
      <title>Deploy a web app on VPS with Docker, Kamal</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Mon, 17 Feb 2025 03:47:20 +0000</pubDate>
      <link>https://forem.com/hungle00/docker-deployment-on-vps-3dbe</link>
      <guid>https://forem.com/hungle00/docker-deployment-on-vps-3dbe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When the Docker container came, it made building web applications easier for developers in both development and deployment. Before, I used Docker for development, but now I have decided to learn how to use it for deployment.&lt;br&gt;
When you search the Internet, you may encounter many complex terms for newbies, such as CI/CD, K8s, and docker swarm. In this post, I'll focus on a basic understanding of the deployment workflow and then, deploy a simple Go web app on my Linode VPS.&lt;/p&gt;

&lt;p&gt;In my opinion, the basic workflow behind Docker deployment can be divided into 4 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build an image from your application code.&lt;/li&gt;
&lt;li&gt;Push the image you built in the previous step to the Docker service ( docker hub, Amazon ECR, ...).&lt;/li&gt;
&lt;li&gt;Write a file (regularly .yml) that defines each needed service.&lt;/li&gt;
&lt;li&gt;Run a file in step 3 to pull images, setup and run each service on your VPS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3ebj7begnfljnhkig22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3ebj7begnfljnhkig22.png" alt="Docker deployment diagram" width="732" height="394"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Sample Golang web app
&lt;/h2&gt;

&lt;p&gt;For saving time, I'll use the existing repo, at the beginning I decided to use my old Rails app. But after going around on the Internet, and seeing &lt;a href="https://github.com/huytd/hacker-live" rel="noopener noreferrer"&gt;the exciting repo&lt;/a&gt; that was written in Go, I'll use it for demo deployment.&lt;br&gt;
Here's my Git repo for this demo: &lt;a href="https://github.com/hungle00/hacker-live" rel="noopener noreferrer"&gt;https://github.com/hungle00/hacker-live&lt;/a&gt;&lt;br&gt;
You can see a Dockerfile to build an image for this app in this repo.&lt;br&gt;
&lt;a href="https://github.com/hungle00/hacker-live/blob/main/Dockerfile" rel="noopener noreferrer"&gt;Dockerfile for hacker-live&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Use docker-compose
&lt;/h3&gt;

&lt;p&gt;I will use Treafik for the proxy server, which will act as the middle-man between the outside web and your VPS. The advantage of Treafik compared to other proxy servers is that it can automatically detect our running containers.&lt;br&gt;
Here's the &lt;code&gt;docker-compose.yml&lt;/code&gt; file that defines 2 services: Treafik proxy and our Go app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;reverse-proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The official v3 Traefik docker image&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik:v3.3&lt;/span&gt;
    &lt;span class="c1"&gt;# Enables the web UI and tells Traefik to listen to docker&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--api.insecure=true --providers.docker&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# The HTTP port&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="c1"&gt;# The Web UI (enabled by --api.insecure=true)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# So that Traefik can listen to the Docker events&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
  &lt;span class="na"&gt;hacker-live&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jamesjoyce/hacker-live&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hacker-live&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.hacker-live.rule=Host(`Your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;IP/domain`)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You just ssh to your server, copy &lt;code&gt;docker-compose.yml&lt;/code&gt; file and then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is not practical because every time you change code, you must rebuild this image, then ssh to the server, and edit the docker-compose file to redefine the services. &lt;br&gt;
In reality, we need the tool for the automatic setup of these steps. In the next part, I'll introduce Kamal for deployment.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Kamal
&lt;/h3&gt;

&lt;p&gt;Kamal is the tool developed by the Rails team for deploying and managing your web app in production with Docker. &lt;br&gt;
Simply speaking, Kamal is mostly a bunch of scripts to automate Docker deploys. When you run &lt;code&gt;kamal init&lt;/code&gt;, it will generate some files that need for deployment, the most important file is &lt;code&gt;config/deploy.yml&lt;/code&gt;.&lt;br&gt;
For example, below is the &lt;code&gt;config/deploy.yml&lt;/code&gt; file I use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Name of your application. Used to uniquely configure containers.&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hacker-live&lt;/span&gt;

&lt;span class="c1"&gt;# Name of the container image.&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jamesjoyce/hacker-live&lt;/span&gt;

&lt;span class="c1"&gt;# Deploy to these servers.&lt;/span&gt;
&lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;192.168.0.1&lt;/span&gt;  &lt;span class="c1"&gt;# change this with your IP&lt;/span&gt;
  &lt;span class="c1"&gt;# job:&lt;/span&gt;
  &lt;span class="c1"&gt;#   hosts:&lt;/span&gt;
  &lt;span class="c1"&gt;#     - 192.168.0.1&lt;/span&gt;
  &lt;span class="c1"&gt;#   cmd: bin/jobs&lt;/span&gt;

&lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app.example.com&lt;/span&gt;
  &lt;span class="c1"&gt;# Proxy connects to your container on port 80 by default.&lt;/span&gt;
  &lt;span class="c1"&gt;# app_port: 3000&lt;/span&gt;

&lt;span class="c1"&gt;# Credentials for your image host.&lt;/span&gt;
&lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Specify the registry server, if you're not using Docker Hub&lt;/span&gt;
  &lt;span class="c1"&gt;# server: registry.digitalocean.com / ghcr.io / ...&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jamesjoyce&lt;/span&gt;

  &lt;span class="c1"&gt;# Always use an access token rather than real password (pulled from .kamal/secrets).&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KAMAL_REGISTRY_PASSWORD&lt;/span&gt;

&lt;span class="c1"&gt;# Configure builder setup.&lt;/span&gt;
&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;arch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amd64&lt;/span&gt;
  &lt;span class="c1"&gt;# Pass in additional build args needed for your Dockerfile.&lt;/span&gt;
  &lt;span class="c1"&gt;# args:&lt;/span&gt;
  &lt;span class="c1"&gt;#   RUBY_VERSION: &amp;lt;%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I didn't define service for Treakfik. This is because Kamal automatically uses their &lt;a href="https://github.com/basecamp/kamal-proxy" rel="noopener noreferrer"&gt;Kamal proxy&lt;/a&gt; for the proxy server.&lt;br&gt;
After config, then you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kamal deploy
# or kamal setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will SSH into each server, prepare it with any necessary dependencies, and build and push the latest images to the docker registry.&lt;/p&gt;

&lt;p&gt;You can read more about Kamal at the &lt;a href="https://kamal-deploy.org/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;. There are many concepts that I didn't introduce in this post, for example: manage accessories (db/redis/search), manage logging, ...&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;In this post, I just introduce how to deploy a very basic web app on VPS using Docker. In reality, you may need more to deploy and manage your web app in production, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some services like database, job workers, Redis, ...&lt;/li&gt;
&lt;li&gt;Monitoring your server (memory usage, CPU, disk, etc.) ...&lt;/li&gt;
&lt;li&gt;Setup CI/CD&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloud</category>
      <category>docker</category>
      <category>kamal</category>
      <category>vps</category>
    </item>
    <item>
      <title>Concurrency in Ruby: Thread and Fiber</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Mon, 18 Nov 2024 02:17:27 +0000</pubDate>
      <link>https://forem.com/hungle00/concurrency-in-ruby-thread-and-fiber-jlb</link>
      <guid>https://forem.com/hungle00/concurrency-in-ruby-thread-and-fiber-jlb</guid>
      <description>&lt;p&gt;In a previous &lt;a href="https://dev.to/hungle00/ruby-fiber-scheduler-and-async-og6"&gt;post&lt;/a&gt;, I introduce briefly about Fiber and then, build a simple asynchronous HTTP server using Async gem. In this post, I'll focus on comparing Fiber and Thread and explain why Fiber is better than Thread in some cases.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Fibers and Threads &lt;/li&gt;
&lt;li&gt;Example: Http request&lt;/li&gt;
&lt;li&gt;Example: Http server&lt;/li&gt;
&lt;li&gt;Concluding&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fibers and Threads
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Thread&lt;/strong&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="n"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&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="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fiber&lt;/strong&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="n"&gt;fiber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&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="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt; &lt;span class="c1"&gt;# transfer / Fiber.schedule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, they have quite similar syntax, so what are the differences between them?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The level:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Threads are created 1:1 with threads on OS.&lt;/li&gt;
&lt;li&gt;Fibers are implemented at the programming language level, multiple fibers can run inside a thread.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Scheduling mechanism:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Threads are run pre-emptive by almost modern OS.&lt;/li&gt;
&lt;li&gt;Fibers are referred to as a mechanism for cooperative concurrency.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Threads will run automatically, they are scheduled by OS.&lt;/strong&gt;&lt;br&gt;
With Thread, programmers are just allowed to create new Threads, make them do some tasks, and use the &lt;code&gt;join&lt;/code&gt; method to get the return from execution. The OS will run threads and decide when to run and pause to achieve concurrency.&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="p"&gt;[&lt;/span&gt;
  &lt;span class="no"&gt;Thread&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="c1"&gt;# code },&lt;/span&gt;
  &lt;span class="no"&gt;Thread&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="c1"&gt;# code }&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;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:join&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Meanwhile, Fiber gives us more control&lt;/strong&gt;&lt;br&gt;
 With Fiber, programmers are free to start, pause, and resume them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Fiber.new { }&lt;/code&gt; : create new fiber, started with &lt;code&gt;resume&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Fiber.yield&lt;/code&gt;: pause current Fiber, moves control to where fiber was resumed&lt;/li&gt;
&lt;li&gt;After suspension, Fiber can be resumed later at the same point with the same execution state.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;fib2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;

&lt;span class="n"&gt;fib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"1 - fib started"&lt;/span&gt;
  &lt;span class="n"&gt;fib2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;
  &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"3 - fib resumed"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;fib2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"2 - control moved to fib2"&lt;/span&gt;
  &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 - fib started
2 - control moved to fib2

3 - fib resumed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Fiber over Thread
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A fiber is lighter-weight than a thread, so we can spawn more fibers than threads&lt;/li&gt;
&lt;li&gt;Less context-switching time ( the advantages of cooperative scheduling compare to preemptive scheduling&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Fiber scheduler
&lt;/h3&gt;

&lt;p&gt;In a previous article, I noted that before Fibers lacked the scheduler implementation to be useful, now it is officially supported from Ruby 3. If you want to enable the fiber scheduling, you need to set a Fiber Scheduler object.&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;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_scheduler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check the list of Fiber Scheduler implementations here &lt;a href="https://github.com/bruno-/fiber_scheduler_list" rel="noopener noreferrer"&gt;Fiber Scheduler List project&lt;/a&gt;.&lt;br&gt;
I suggest using this Fiber Schedulers implementation - &lt;a href="https://socketry.github.io/async/guides/getting-started/index" rel="noopener noreferrer"&gt;Async gem&lt;/a&gt;. It was created by Samuel Williams and has the robust API to write concurrency code. &lt;/p&gt;

&lt;p&gt;The next part will help you understand more about how to use Thread, Fiber, and Async gem to write concurrent HTTP requests.&lt;/p&gt;
&lt;h2&gt;
  
  
  HTTP requests example
&lt;/h2&gt;

&lt;p&gt;For example, we will get a list of &lt;code&gt;uuid&lt;/code&gt; from this site&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;"net/http"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_uuid&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://httpbin.org/uuid"&lt;/span&gt;
  &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;URI&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="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s2"&gt;"uuid"&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;This request will take about 1s to finish.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sequentially version&lt;/strong&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;get_http_sequently&lt;/span&gt;
  &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;get_uuid&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;results&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;get_http_sequently&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Fiber runtime: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# about 11-12s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One request took about 1s so if we call sequentially, this code will take about 10s.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgist.github.com%2Fuser-attachments%2Fassets%2Ffcaf8bf1-dbd7-44ac-979e-87b53c6a3ac0" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgist.github.com%2Fuser-attachments%2Fassets%2Ffcaf8bf1-dbd7-44ac-979e-87b53c6a3ac0" alt="Ruby 3 concurrency tools" width="800" height="415"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency version with thread&lt;/strong&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;get_http_via_threads&lt;/span&gt;
  &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Thread&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="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;get_uuid&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;results&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; 1.3s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Concurrency version with fiber&lt;/strong&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"async"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_http_via_fibers&lt;/span&gt;
  &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_scheduler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Async&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Scheduler&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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;get_uuid&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;results&lt;/span&gt;
&lt;span class="k"&gt;ensure&lt;/span&gt;
  &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_scheduler&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="c1"&gt;# =&amp;gt; 1.2s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because all requests are called concurrently, the total time is about the time of the slowest request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgist.github.com%2Fuser-attachments%2Fassets%2F2c503f61-f354-4cb5-af6a-bc2d16dfa4f9" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgist.github.com%2Fuser-attachments%2Fassets%2F2c503f61-f354-4cb5-af6a-bc2d16dfa4f9" alt="Ruby 3 concurrency tools" width="800" height="415"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  More about Async
&lt;/h3&gt;

&lt;p&gt;Another implementation uses Async gem like that, we use &lt;code&gt;Kernel#Async&lt;/code&gt; method instead of &lt;code&gt;Async::Scheduler&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;get_http_via_async&lt;/span&gt;
  &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="no"&gt;Async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;Async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;get_uuid&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="n"&gt;results&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 general structure of Async Ruby programs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You always start with an &lt;code&gt;Async&lt;/code&gt; block which is passed a task.&lt;/li&gt;
&lt;li&gt;That main task is usually used to spawn more Async tasks with &lt;code&gt;task.async&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;These tasks run concurrently with each other and the main task.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgist.github.com%2Fuser-attachments%2Fassets%2F20e9879e-85ba-4d6a-93f8-3394319647d6" class="article-body-image-wrapper"&gt;&lt;img alt="Screenshot 2024-11-07 at 14 15 23" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgist.github.com%2Fuser-attachments%2Fassets%2F20e9879e-85ba-4d6a-93f8-3394319647d6" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The task is built on top of each Fiber.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP server example
&lt;/h2&gt;

&lt;p&gt;The minimal HTTP server in Ruby can be implemented by using the built-in class &lt;code&gt;TCPServer&lt;/code&gt;, it'll look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SOCKET_READ_BACKLOG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt; &lt;span class="c1"&gt;# wait for a client to connect&lt;/span&gt;
  &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RequestParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;#... status, headers, body&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll make the server handle more than 1 request per time. &lt;/p&gt;

&lt;h3&gt;
  
  
  Thread pool version
&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;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ThreadPool&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;size: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt; &lt;span class="c1"&gt;# wait for a client to connect&lt;/span&gt;
  &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# handle each request&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RequestParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&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 idea is to use a thread pool to limit the number of threads running concurrently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Async version
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt; &lt;span class="c1"&gt;# wait for a client to connect&lt;/span&gt;
    &lt;span class="no"&gt;Async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="c1"&gt;# handle each request&lt;/span&gt;
      &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RequestParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&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;The Falcon is the most-known app server that uses async for connection pool &lt;a href="https://socketry.github.io/falcon/guides/getting-started/index" rel="noopener noreferrer"&gt;Falcon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;More detail about implementation and benchmark testing on &lt;a href="https://github.com/hungle00/async-http-server" rel="noopener noreferrer"&gt;this repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Threads and fibers allow programmers to write concurrent code, it's very useful for handling blocking-IO operations.
&lt;/li&gt;
&lt;li&gt;As a Ruby developer, we don't use Thread directly most of the time. But in reality, for web development, a lot of tools use threads.

&lt;ul&gt;
&lt;li&gt;A web server like Puma or Webrick&lt;/li&gt;
&lt;li&gt;A background job like Sidekiq, GoodJob, and SolidQueue&lt;/li&gt;
&lt;li&gt;An ORM like ActiveRecord or Sequel&lt;/li&gt;
&lt;li&gt;A Http client HTTParty or RestClient&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fiber (+ FiberScheduler) has just been released from Ruby 3 maybe may have a bright future due to its advantages compared to Thread. Here's a couple of the most useful tools on top of fiber:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;async-http&lt;/code&gt; a featureful HTTP client&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;falcon&lt;/code&gt; HTTP server built around Async core&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ruby</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Ruby Fiber Scheduler and Async</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Wed, 23 Oct 2024 14:53:14 +0000</pubDate>
      <link>https://forem.com/hungle00/ruby-fiber-scheduler-and-async-og6</link>
      <guid>https://forem.com/hungle00/ruby-fiber-scheduler-and-async-og6</guid>
      <description>&lt;p&gt;Ruby 3 comes with a lot of support tools for concurrency programming, one of them is Fiber Scheduler. In this post, I'll introduce briefly about Fiber Scheduler, the Async gem, and then, implement the simple server using Fiber Scheduler.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Fiber
&lt;/h2&gt;

&lt;p&gt;Fiber can be seen as a lightweight thread or thread implemented at the programming language level instead of the OS level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;fiber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&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="c1"&gt;#...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt; &lt;span class="c1"&gt;# transfer / Fiber.schedule&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Fiber is a lightweight unit of execution that can be suspended and resumed at specific points. Because only one fiber can execute at a time, they are often referred to as a mechanism for cooperative concurrency. Theoretically, the advantage of Fibers is less context switching compared to Thread, but before Ruby 3, Fibers lacked the scheduler implementation to be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fiber scheduler
&lt;/h2&gt;

&lt;p&gt;Since Ruby 3, Fibers have been given superpowers in the form of the FiberScheduler.&lt;br&gt;
The Fiber Scheduler consists of two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fiber Scheduler interface&lt;/li&gt;
&lt;li&gt;Fiber Scheduler implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What Ruby 3 implements is the interface. It would not use the scheduler unless a scheduler implementation is included.&lt;br&gt;
If you want to enable the asynchronous behavior in Ruby, you need to set a Fiber Scheduler object.&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;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_scheduler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list of Fiber Scheduler implementations and their main differences can be found at &lt;a href="https://github.com/bruno-/fiber_scheduler_list" rel="noopener noreferrer"&gt;Fiber Scheduler List project&lt;/a&gt;.&lt;br&gt;
I recommend you read this article to understand more detail about &lt;a href="https://brunosutic.com/blog/ruby-fiber-scheduler" rel="noopener noreferrer"&gt;Fiber Scheduler&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Async gem:
&lt;/h2&gt;

&lt;p&gt;One of the most mature and common Fiber Scheduler implementations is by Samuel Williams. Furthermore, he not only implemented a Fiber Scheduler but created the gem called Async which was more powerful for working with asynchronous programming.&lt;br&gt;&lt;br&gt;
If you want to go to details about the Async gem and how to use it, you should read the document &lt;a href="https://socketry.github.io/async/guides/getting-started/index" rel="noopener noreferrer"&gt;Async Guides&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The simple use of async gem is quite straightforward, you just need to wrap the computation code in &lt;code&gt;Kernel#Async&lt;/code&gt; method, for 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="s1"&gt;'async'&lt;/span&gt;

&lt;span class="no"&gt;Async&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;task&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello World!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Simple asynchronous HTTP server
&lt;/h2&gt;

&lt;p&gt;In this part, I'll implement the simple HTTP server using Async gem.&lt;br&gt;
For simplicity, I will get the code from &lt;a href="https://blog.appsignal.com/2016/11/23/ruby-magic-building-a-30-line-http-server-in-ruby.html" rel="noopener noreferrer"&gt;Appsignal's article Building a 30 line HTTP server in Ruby&lt;/a&gt;. After that, add the async gem to make the code asynchronous.&lt;/p&gt;

&lt;p&gt;Using Fiber syntax + &lt;code&gt;Async::Scheduler&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'async/scheduler'&lt;/span&gt;

&lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_scheduler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Async&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Scheduler&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="c1"&gt;# Server bound to port 2000&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'200'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text/html'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Hello world! The time is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;    &lt;span class="c1"&gt;# Wait for a client to connect&lt;/span&gt;
    &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;

      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"HTTP/1.1 &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

      &lt;span class="n"&gt;headers&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&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="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;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

      &lt;span class="n"&gt;body&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;part&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;or using &lt;code&gt;Kernel#Async&lt;/code&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'async'&lt;/span&gt;

&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&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="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'200'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text/html'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Hello world! The time is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;    &lt;span class="c1"&gt;# Wait for a client to connect&lt;/span&gt;
    &lt;span class="no"&gt;Async&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;

      &lt;span class="c1"&gt;# ... same code as above&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;Our server is very simple but it is enough to demonstrate the usage and the advantage of Async gem. You can check the full code, and benchmark testing on [this repo].(&lt;a href="https://github.com/hungle00/async-http-server" rel="noopener noreferrer"&gt;https://github.com/hungle00/async-http-server&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;If you wonder about the Ruby asynchronous server that can be used in production, you can check &lt;a href="https://github.com/socketry/async-http" rel="noopener noreferrer"&gt;this gem&lt;/a&gt;, it is built on top of the Async gem.&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Vanilla JS Effect Methods</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Sat, 27 Jul 2024 02:52:20 +0000</pubDate>
      <link>https://forem.com/hungle00/vanilla-js-effect-methods-3pnp</link>
      <guid>https://forem.com/hungle00/vanilla-js-effect-methods-3pnp</guid>
      <description>&lt;p&gt;In the past, I worked quite a lot with jQuery and the thing I love about jQuery is that it provides many useful methods with simple and lightweight syntax. One of the most used jQuery methods is effects methods - which are used for creating animation effects for website.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hide() / show()&lt;/li&gt;
&lt;li&gt;toggle()&lt;/li&gt;
&lt;li&gt;fadeIn()&lt;/li&gt;
&lt;li&gt;fadeOut()&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;W3schools listed all JQuery effect methods &lt;a href="https://www.w3schools.com/jquery/jquery_ref_effects.asp" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But now with the growth of tons of JS libraries,  jQuery seems to be out of date, and in some projects, developers must replace jQuery code with pure JS.&lt;/p&gt;

&lt;p&gt;It's effortless to create the function act the same as &lt;code&gt;hide()&lt;/code&gt; / &lt;code&gt;show()&lt;/code&gt;, just edit the display style&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="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// or none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;but with more complex effects like &lt;code&gt;fadeIn()&lt;/code&gt;/&lt;code&gt;fadeOut()&lt;/code&gt;, we need to write more code.&lt;/p&gt;

&lt;p&gt;Another problem with the writing effect method in vanilla JS is verbose syntax. You can see that with the jQuery method:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.myClass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slideDown&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It is very readable and intuitive, you find the element with jQuery selector, and then call the &lt;code&gt;slideDown&lt;/code&gt; method as the method of the element.&lt;br&gt;
The code implements the same feature in vanilla JS if you define &lt;code&gt;slideToggle&lt;/code&gt; method before:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.myClass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;slideToggle&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You query the element and then pass it to the &lt;code&gt;slideToggle()&lt;/code&gt; function, it seems less native and less readable than the sample with jQuery.&lt;/p&gt;

&lt;p&gt;The trick here is to use &lt;code&gt;HTMLElement.prototype&lt;/code&gt; to add a method to the HTML element and you can use the effect function as a method of the HTML element. For simplicity, let's define the &lt;code&gt;hide()&lt;/code&gt; method:&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="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.myClass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For reuse purposes, I created some vanilla JS methods for effects here. Just put it somewhere on your codebase and use it as the native method of HTML element.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>jquery</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Lambda Calculus - Represent in Clojure</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Sun, 12 Nov 2023 15:56:24 +0000</pubDate>
      <link>https://forem.com/hungle00/lambda-caculus-represent-in-clojure-35hp</link>
      <guid>https://forem.com/hungle00/lambda-caculus-represent-in-clojure-35hp</guid>
      <description>&lt;p&gt;Recently, I've been spending my free time learning functional programming. To understand comprehensively about functional programming, it is need to mention Lambda Calculus - a completed computation model for functional programming. &lt;/p&gt;

&lt;p&gt;I am a developer and didn't have Lambda Calculus academic training, so at the beginning, I was very confused about some concepts. From my perspective, trying to implement it in a specific programming language makes it much easier to understand and explain to someone else. I chose Clojure for this purpose because it is a Lisp dialect language and has coherence and elegance of design.&lt;br&gt;
This post just focuses on representing λ-calculus in Clojure. I suppose that you have basic knowledge about λ-calculus, if not, try to read &lt;a href="https://eecs390.github.io/notes/theory.html"&gt;Lambda Calculus theory&lt;/a&gt; or search on Wikipedia.&lt;/p&gt;
&lt;h2&gt;
  
  
  Some basic λ expressions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Function abstraction&lt;/strong&gt;: All functions of the lambda calculus are anonymous and only take one parameter.&lt;br&gt;&lt;br&gt;
For example: &lt;code&gt;λx.e&lt;/code&gt; function accepts an argument x and returns the value of y. In Clojure, we'd write this as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Function application&lt;/strong&gt;:   &lt;code&gt;(f e)&lt;/code&gt; means the applied function f to e.  It has a similar form in Clojure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explore some other λ expressions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;λx.x&lt;/code&gt; called the identity function.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Applied identity function above to &lt;code&gt;a&lt;/code&gt; we have &lt;code&gt;(λx.x a)&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(λ f. λ x. f x)&lt;/code&gt; equivalent to:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Function composition&lt;/strong&gt;&lt;br&gt;
This function takes 2 functions as arguments and returns a function that is the composition of those.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="nb"&gt;comp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;λ&lt;/span&gt;&lt;span class="n"&gt;g.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;λ&lt;/span&gt;&lt;span class="n"&gt;f.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;λ&lt;/span&gt;&lt;span class="n"&gt;x.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll write Clojure code to represent &lt;code&gt;comp&lt;/code&gt; function and define 2 functions &lt;code&gt;add&lt;/code&gt; and &lt;code&gt;subtract&lt;/code&gt; for testing. In Clojure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;comp&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subtract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;comp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;;; =&amp;gt; 6&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next part, I'll introduce Church encoding - a tool for representing numbers, booleans, conditionals, and data structures in Lambda Calculus&lt;/p&gt;

&lt;h2&gt;
  
  
  Church encoding
&lt;/h2&gt;

&lt;p&gt;Church encoding, in Lambda calculus, is a way to encode data and operators using only a combination of anonymous functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Booleans
&lt;/h3&gt;

&lt;p&gt;Let's define an abstraction for the booleans.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;true = λx. λy. x
false = λx. λy. y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These functions receive two arguments and, If &lt;code&gt;true&lt;/code&gt;, the first argument is returned. If &lt;code&gt;false&lt;/code&gt;, the second argument is returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fst&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pair
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pair = λx. λy. λf. f xy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;pair&lt;/strong&gt; function takes two arguments x and y and returns a function that contains x and y in its body.&lt;br&gt;&lt;br&gt;
In Clojure, this term is equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given a pair, we can extract the first and second items using selection functions - these functions with similar form to boolean functions above.&lt;br&gt;
Clojure code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fst&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fst&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;;; =&amp;gt; 3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snd&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;;; =&amp;gt; 5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Numerals
&lt;/h2&gt;

&lt;p&gt;A Church numeral applies its first argument to its second argument n times. For example, the definition of numbers from 0-3 in λ-Calculus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 = λf.λx.x
1 = λf.λx.f x
2 = λf.λx.f (f x)
3 = λf.λx.f (f (f x))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In summary, the definition of the numeral n is the application of a given function f n times to a given value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; 0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;;; 1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;;; 2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can define a &lt;code&gt;succ&lt;/code&gt; function, which takes a Church numeral n and returns n + 1 by applying f to x one more time than the original number&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;succ = λn.λf.λx.f (n f x)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll try the function by applying function &lt;code&gt;two&lt;/code&gt; defined before and check the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;succ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c1"&gt;;; helper function to-int helps us check numerals&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to-int&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&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="w"&gt;
                     &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;to-int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;succ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;;; =&amp;gt; 3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;to-int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;succ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;succ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;;; =&amp;gt; 4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are more exercises in Church encoding, how to express conditional expressions, arithmetic operations, lists, etc and you can define them by yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  More resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Henri Borges'article &lt;a href="https://dev.to/hnrbs/functions-describe-the-world-2cj"&gt;Functions Describe the World
&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lambda calculus represented in both Clojure and Elixir: &lt;a href="https://hungle00.github.io/content.html?id=15"&gt;https://hungle00.github.io/content.html?id=15&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://matt.might.net/articles/implementing-a-programming-language/"&gt;Matthew Might's work&lt;/a&gt; on FP and Lambda calculus is amazing.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>clojure</category>
      <category>lambdacalculus</category>
      <category>functional</category>
    </item>
    <item>
      <title>Ruby's main object</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Sun, 01 Oct 2023 03:20:19 +0000</pubDate>
      <link>https://forem.com/hungle00/rubys-main-object-5hni</link>
      <guid>https://forem.com/hungle00/rubys-main-object-5hni</guid>
      <description>&lt;p&gt;In Java, every function must be wrapped by a class, but in Ruby, we can define a function without classes. Therefore, some years ago I used to suppose that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are no functions in Java, only methods. But for Ruby, we can have a function that isn't a method.     &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, these days I have realized that: "Every function in Ruby is also a method".&lt;br&gt;&lt;br&gt;
But why can we define the function at the top-level scope, without creating classes for them, like Java? Or which class does this function belong to?&lt;/p&gt;

&lt;p&gt;For example, I have the function &lt;code&gt;increment&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;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We try to call &lt;code&gt;increment&lt;/code&gt; this way:&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="c1"&gt;# or&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:increment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since &lt;code&gt;self&lt;/code&gt; keyword contains a reference to the current object, the receiver for the current method, &lt;code&gt;increment&lt;/code&gt; must be a method. Now let's check which object &lt;code&gt;self&lt;/code&gt; reference to.&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;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; main&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; Object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see puts &lt;code&gt;self&lt;/code&gt; code returns string "main", and it is an instance of &lt;code&gt;Object&lt;/code&gt;,  the root class of Ruby’s class hierarchy. So we can conclude that  &lt;code&gt;increment&lt;/code&gt; is an instance method in the &lt;code&gt;Object&lt;/code&gt; class. &lt;br&gt;
Technically speaking, Ruby automatically creates a &lt;code&gt;main&lt;/code&gt; object as the top self object, it is the default receiver for top-level methods. Actually, all Ruby functions are private methods of &lt;code&gt;Object&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="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;private_instance_methods&lt;/span&gt;
&lt;span class="c1"&gt;# [:increment, :DelegateClass, :sprintf, ...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Every function in Ruby is also a method. It's an instance method of &lt;code&gt;Object&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Ruby &lt;code&gt;main&lt;/code&gt; object allows you to write simple functions and, at the same time, ensure sophisticated OO design.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>oop</category>
    </item>
    <item>
      <title>Build a Masonry layout (Pinterest layout)</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Thu, 20 Jul 2023 15:11:30 +0000</pubDate>
      <link>https://forem.com/hungle00/build-a-masonry-layout-pinterest-layout-3glp</link>
      <guid>https://forem.com/hungle00/build-a-masonry-layout-pinterest-layout-3glp</guid>
      <description>&lt;p&gt;Recently, I worked on a side project that allows people to pin videos by link or upload images and I want to display them in the pinterest-style layout. After some research, I have figured out some effective methods to build that layout, including the use of some JS libraries. But for simplicity, I took the only CSS ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. CSS columns approach
&lt;/h2&gt;

&lt;p&gt;This approach will leverage the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/columns" rel="noopener noreferrer"&gt;CSS columns&lt;/a&gt; with column-count CSS property that breaks an element's content into the specified number of columns.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 css
.board-grid {
  column-count: 4;
  column-gap: 1em;
}


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

&lt;/div&gt;

&lt;p&gt;The CSS rules above will style the board into a three column layout without a defined vertical axis.&lt;/p&gt;

&lt;p&gt;If you have images with captions, then you want to avoid the stiuation where a the image and caption split across columns, you should use the &lt;code&gt;page-break-&lt;/code&gt; properties:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 css
.item {
    -webkit-column-break-inside: avoid;
    break-inside: avoid;
    page-break-inside: avoid;
}


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

&lt;/div&gt;

&lt;p&gt;It is even easier to build a responsive layout with Tailwind CSS, just use &lt;em&gt;columns-{count}&lt;/em&gt; with breakpoint. For example, &lt;code&gt;md:columns-3&lt;/code&gt; will apply the &lt;code&gt;columns-3&lt;/code&gt; at only medium screen sizes and above.&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"columns-2 md:columns-3 lg:columns-4 gap-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"break-inside-avoid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The demo I built by Tailwind CSS, but idea is the same with pure CSS:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/Lehung/embed/wvQywYq?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The only drawback of this approach is that the ordering of the masonry tiles are vertical — from top to bottom — rather than being shown horizontally from left to right.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. CSS grid approach
&lt;/h2&gt;

&lt;p&gt;The hard part of this approach is that CSS Grid creates two dimensional grids with strict rows and columns, whereas masonry is about letting elements fit themselves into space horizontally and vertically.&lt;br&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%2Fsux3smncq68sa0defda8.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%2Fuploads%2Farticles%2Fsux3smncq68sa0defda8.png" alt="A 2 dimensional grid"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The trick is using &lt;code&gt;grid-auto-rows&lt;/code&gt; property to sets the unit height for our masonry. Then adjusts each block's height to match the grid with a little bit of math to calculate how many rows they should span.&lt;br&gt;
With H as the height of the block, if we set &lt;code&gt;grid-auto-rows&lt;/code&gt; value to R, &lt;code&gt;grid-row-gap&lt;/code&gt; value to G, an appropriate span number (S) for any given block can be calculated by: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

S = H / (G + R)


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

&lt;/div&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 css
.board-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 300px);
  grid-auto-rows: 20px;
  grid-gap: 10px;
}


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

&lt;/div&gt;
&lt;p&gt;The value of &lt;code&gt;grid-auto-rows&lt;/code&gt; is 20px, and value of &lt;code&gt;grid-gap&lt;/code&gt; is 10px; so if the height of the element is 300px, it will span 10 rows.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="nc"&gt;.item&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-row-end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;The ideas of this approach I borrowed from &lt;a href="https://w3bits.com/css-grid-masonry/" rel="noopener noreferrer"&gt;https://w3bits.com/css-grid-masonry/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;View details on demo: &lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/Lehung/embed/zYMpWgZ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;View more about a side project I working on. It is called Mood Opus - a simple mood board that allows people to pin videos by link or upload images and display them in the Masonry layout.&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/hungle00/mood_opus" rel="noopener noreferrer"&gt;https://github.com/hungle00/mood_opus&lt;/a&gt; &lt;/p&gt;

</description>
      <category>css</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Rails nested form (new gem with Stimulus)</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Sun, 18 Jun 2023 01:58:26 +0000</pubDate>
      <link>https://forem.com/hungle00/rails-nested-form-new-gem-with-stimulus-4f4i</link>
      <guid>https://forem.com/hungle00/rails-nested-form-new-gem-with-stimulus-4f4i</guid>
      <description>&lt;p&gt;Nested forms are forms that handle nested models and attributes in one form; e.g. a project with its tasks or an invoice with its line items.&lt;br&gt;&lt;br&gt;
Before Rails 6, &lt;a href="https://github.com/nathanvda/cocoon" rel="noopener noreferrer"&gt;Cocoon&lt;/a&gt; is a good choice for creating dynamic nested forms. But Cocoon needs jQuery to work well, it's a very old library on modern-day frontend frameworks. &lt;br&gt;
When Stimulus is came out, Rails devs is suggested to use Stimulus as Javascript library in their projects. So, I created a gem for handling dynamic nested forms with Stimulus JS. &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hungle00" rel="noopener noreferrer"&gt;
        hungle00
      &lt;/a&gt; / &lt;a href="https://github.com/hungle00/rondo_form" rel="noopener noreferrer"&gt;
        rondo_form
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Same as Cocoon, but using StimulusJS https://rubygems.org/gems/rondo_form
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;RondoForm&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Handle dynamic nested forms, same as Cocoon, but using StimulusJS&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Install the gem and add to the application's Gemfile by executing:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;$ bundle add rondo_form
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or inside the Gemfile add the following&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;$ gem 'rondo_form', '~&amp;gt; 0.2.6'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run the installation task:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;$ rails g rondo_form:install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;For example, you have &lt;code&gt;Project&lt;/code&gt; model, which has &lt;code&gt;has_many&lt;/code&gt; relationship with &lt;code&gt;Task&lt;/code&gt; model:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;rails g scaffold Project name:string description:string
rails g model Task description:string done:boolean project:belongs_to
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You need to add &lt;code&gt;accepts_nested_attributes_for&lt;/code&gt; to &lt;code&gt;Project&lt;/code&gt; model:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;class Project &amp;lt; ApplicationRecord
  has_many :tasks, dependent: :destroy
  accepts_nested_attributes_for :tasks, reject_if: :all_blank, allow_destroy: true
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Sample with SimpleForm&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;The RondoForm gem adds two helper functions: &lt;code&gt;link_to_add_association&lt;/code&gt; and &lt;code&gt;link_to_remove_association&lt;/code&gt;.
The example below illustrates the way to use it.&lt;/p&gt;
&lt;p&gt;In your &lt;code&gt;projects/_form&lt;/code&gt; partial:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-erb notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;&amp;lt;%=&lt;/span&gt; simple_form_for(&lt;a class="mentioned-user" href="https://dev.to/project"&gt;@project&lt;/a&gt;) do |f| &lt;span class="pl-k"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;div&lt;/span&gt; &lt;span class="pl-c1"&gt;class&lt;/span&gt;="&lt;span class="pl-s"&gt;form-inputs&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="pl-k"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="pl-s1"&gt;f&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;input&lt;/span&gt; &lt;span class="pl-pds"&gt;:name&lt;/span&gt; &lt;span class="pl-k"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="pl-k"&gt;    &amp;lt;%=&lt;/span&gt; &lt;span class="pl-s1"&gt;f&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;input&lt;/span&gt; &lt;span class="pl-pds"&gt;:description&lt;/span&gt; &lt;span class="pl-k"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hungle00/rondo_form" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Rondo Form is easy to use, it has the same tag helpers name as cocoon: &lt;code&gt;link_to_add_association&lt;/code&gt;, &lt;code&gt;link_to_remove_association&lt;/code&gt;. &lt;br&gt;
This gem does not need JS dependencies to work with, when you run under command, it will generate &lt;code&gt;nested_rondo_controller.js&lt;/code&gt; in your &lt;code&gt;app/javascript/controllers/&lt;/code&gt; folder: &lt;/p&gt;

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

rails g rondo_form:install 


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

&lt;/div&gt;

&lt;p&gt;And auto import this controller into index.js.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 js
import NestedRondoController from "./nested_rondo_controller"
application.register("nested-rondo", NestedRondoController)


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

&lt;/div&gt;

&lt;p&gt;You must add &lt;code&gt;data-controller="nested-rondo"&lt;/code&gt; to an element, that wraps &lt;code&gt;fields_for&lt;/code&gt; and &lt;code&gt;link_to_add_association&lt;/code&gt; helper.&lt;br&gt;
For example, we have Project model, which has &lt;code&gt;has_many&lt;/code&gt; relationship with Task model. &lt;br&gt;
In your &lt;code&gt;projects/_form&lt;/code&gt; partial:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 erb
&amp;lt;%= simple_form_for(@project) do |f| %&amp;gt;
  &amp;lt;div class="form-inputs"&amp;gt;
    &amp;lt;%= f.input :name %&amp;gt;
    &amp;lt;%= f.input :description %&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;h3&amp;gt;Tasks&amp;lt;/h3&amp;gt;
  &amp;lt;div data-controller="nested-rondo"&amp;gt;
    &amp;lt;%= f.simple_fields_for :tasks do |task| %&amp;gt;
      &amp;lt;%= render "task_fields", f: task %&amp;gt;
    &amp;lt;% end %&amp;gt;
    &amp;lt;div class="links"&amp;gt;
      &amp;lt;%= link_to_add_association "Add Task", f, :tasks %&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;%= f.button :submit %&amp;gt;
&amp;lt;% end %&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;In your &lt;code&gt;_task_fields&lt;/code&gt; partial:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 erb
&amp;lt;div class="nested-fields"&amp;gt;
  &amp;lt;%= f.input :description %&amp;gt;
  &amp;lt;%= f.input :done, as: :boolean %&amp;gt;
  &amp;lt;%= link_to_remove_association "Remove Task", f %&amp;gt;
&amp;lt;/div&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;This sample I built with SimpleForm gem, but the idea is the same with Rails standard form.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>stimulus</category>
    </item>
    <item>
      <title>Ruby - DIY accessor methods</title>
      <dc:creator>hungle00</dc:creator>
      <pubDate>Mon, 05 Jun 2023 06:06:58 +0000</pubDate>
      <link>https://forem.com/hungle00/ruby-diy-accessor-methods-40dk</link>
      <guid>https://forem.com/hungle00/ruby-diy-accessor-methods-40dk</guid>
      <description>&lt;h2&gt;
  
  
  1. Ruby accessor methods
&lt;/h2&gt;

&lt;p&gt;Ruby does not allow instance variables to be accessed outside of methods for that object.&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;Person&lt;/span&gt;  
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'steve'&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="nf"&gt;name&lt;/span&gt;  &lt;span class="c1"&gt;# =&amp;gt;  undefined method `name=' for #&amp;lt;Person ..&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design ensures OOP &lt;em&gt;encapsulation&lt;/em&gt; - an object’s data is protected from the outside world.&lt;br&gt;&lt;br&gt;
In some languages like Java, to access private instance variables, you must define getter/setter methods for each attribute. But in Ruby, you can generate getter/setter methods by using one of the methods in the &lt;code&gt;Module#attr_*&lt;/code&gt; family.&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;Person&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;      &lt;span class="c1"&gt;# getter &lt;/span&gt;
  &lt;span class="nb"&gt;attr_writer&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;      &lt;span class="c1"&gt;# setter&lt;/span&gt;
  &lt;span class="c1"&gt;# attr_accessor :name, :age  # getter + setter&lt;/span&gt;
  &lt;span class="c1"&gt;# other code ....&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'steve'&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="nf"&gt;name&lt;/span&gt;  &lt;span class="c1"&gt;# =&amp;gt;  steve&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;age&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It just takes one or two lines for all of the attributes. These methods are called Class Macros which are class methods only used when in a class definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. DIY accessor methods
&lt;/h2&gt;

&lt;p&gt;There are advanced ways to access instance variables, like &lt;code&gt;instance_variable_get&lt;/code&gt;, &lt;code&gt;instance_eval&lt;/code&gt;.&lt;br&gt;
For 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;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'steve'&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="nf"&gt;instance_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_eval&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using some metaprogramming techniques, we can build attribute accessor methods by myself.&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;GetterSetter&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attr_getter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;attributes&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;attribute&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="nb"&gt;instance_variable_get&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;attribute&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="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;def&lt;/span&gt; &lt;span class="nf"&gt;attr_setter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;attributes&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;attribute&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;define_method&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="nb"&gt;instance_variable_set&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;attribute&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="n"&gt;value&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;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attr_getter_and_setter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;attr_getter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;attr_setter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;attributes&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 simplicity, I define getter/setter in separated module and include in &lt;code&gt;Person&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;GetterSetter&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;attr_getter&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;
  &lt;span class="n"&gt;attr_setter&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;
  &lt;span class="c1"&gt;# or &lt;/span&gt;
  &lt;span class="c1"&gt;# attr_getter_and_setter :name, :age&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Luke'&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="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'anakin'&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; anakin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Class macros are one of Ruby's magic methods that help developers get rid of a lot of tedious, boilerplate methods. In rails, class macros are used a lot, for associations ( &lt;code&gt;has_many&lt;/code&gt;, &lt;code&gt;belongs_to&lt;/code&gt; ), validation, ...&lt;br&gt;&lt;br&gt;
You can learn more about Ruby class macros or other metaprogramming techniques  in the Metaprogramming Ruby book ( &lt;a href="https://pragprog.com/titles/ppmetr2/metaprogramming-ruby-2/"&gt;https://pragprog.com/titles/ppmetr2/metaprogramming-ruby-2/&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>oop</category>
    </item>
  </channel>
</rss>
