<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Patrick Wendo</title>
    <description>The latest articles on DEV Community by Patrick Wendo (@w3ndo).</description>
    <link>https://dev.to/w3ndo</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F473910%2F2d6d4ad0-6f4d-4a79-959f-581b21e1b118.png</url>
      <title>DEV Community: Patrick Wendo</title>
      <link>https://dev.to/w3ndo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/w3ndo"/>
    <language>en</language>
    <item>
      <title>The problem with the happy path</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Sun, 16 Aug 2026 18:21:53 +0000</pubDate>
      <link>https://dev.to/w3ndo/the-problem-with-the-happy-path-23e6</link>
      <guid>https://dev.to/w3ndo/the-problem-with-the-happy-path-23e6</guid>
      <description>&lt;p&gt;I recently had to do a deployment for a client demo. On local everything worked fine. All the tests were passing. All the data was where it was supposed to be. And then I pushed it to production. Suddenly, queries were failing. And I could not figure out why. So I fixed the first query. Re-built it, pushed again. It worked, but another thing broke. Rinse repeat and another thing broke. &lt;/p&gt;

&lt;p&gt;All the queries that were failing had something in common. An error because a function was receiving nil or an empty enumerable. They were built for when data to be queried existed.  But in production, I had no data. I was working with an empty state. I had spent the past week designing for when the data is already there. Designing for when my database is already seeded. Designing for when queries were passing. But prod will get a blank slate. I was a bit angry and a bit embarrassed at myself for overlooking such a situation. &lt;/p&gt;

&lt;p&gt;It reminded me of old developer wisdom I had heard before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;80% of the time in software development is about discovering and covering the "unhappy" paths.

source: https://x.com/sihui_io/status/914908896371777536?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While I am not sure about that metric, the point stands.&lt;/p&gt;

&lt;p&gt;So now I have had to re-orient my process for writing new features. I have to define all possible states that the feature may be in and test all the states. That way I do not have to spend another 6 hours going through production wondering why that query is failing. It is good to assume failure and the unhappy path. Because then you can plan for it. &lt;/p&gt;

</description>
      <category>dev</category>
    </item>
    <item>
      <title>Why you should Homelab as a developer</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Mon, 03 Aug 2026 12:25:57 +0000</pubDate>
      <link>https://dev.to/w3ndo/why-you-should-homelab-as-a-developer-4i5h</link>
      <guid>https://dev.to/w3ndo/why-you-should-homelab-as-a-developer-4i5h</guid>
      <description>&lt;p&gt;There is a good chance you have heard, read or have been told by other developers that you should start your homelab. It does not need not be an expensive hobby. In fact, all it takes is one laptop. I have seen people like &lt;a href="//jeffgeerling.com"&gt;Jeff Geerling&lt;/a&gt; build and document amazing homelab setups and they have been my inspiration through this journey. In this post I explain how my homelab experience has been. Why I started, how it's going and lessons I have learned along the way and how they translate to my skillset.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I run on my homelab
&lt;/h2&gt;

&lt;p&gt;First let's get a quick overview of the things I run on my homelab.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Personal website. A simple html/css/js site built with &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;astro&lt;/a&gt;. Astro was very useful because I wanted something very lightweight to test out when I started.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="//jellyfin.org"&gt;Jellyfin&lt;/a&gt;. A homelab is never complete without a personal media library. Jellyfin was the first &lt;em&gt;big&lt;/em&gt; app I decided to run. I wanted to see how well the hardware could handle workloads like encoding and decoding media files. It is a pretty good stress test. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://immich.app/" rel="noopener noreferrer"&gt;Immich&lt;/a&gt;. I have been working to move away from google photos. Immich is a great open source alternative. With geotagging and machine learning to identify faces all running locally. And you can have multiple users with multiple accounts, this was a no brainer for me.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mailcow(currently exploring). While I have not entirely migrated off gmail, I am considering other self hosted mail providers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="//tailscale.com"&gt;Tailscale&lt;/a&gt;. A Zero Trust identity-based connectivity platform. Honestly, the swiss army knife of homelabbing in my opinion. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pet projects. Whenever I have a new phoenix, rails or node application I need to test in a production environment, I usually build it and run it on my homelab. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How it started.
&lt;/h2&gt;

&lt;p&gt;Well, like everything else, I wanted to practice my linux devops skills. Granted, I have been a linux user for about 8 years now, running a headless server was a new challenge for me. But I will admit, it has made me more confident that I will in fact figure it out if I do not know how something works. &lt;/p&gt;

&lt;p&gt;Initially, it was never exposed to the public internet. It was all running on my local network and I just used it with Jellyfin. This was great for media streaming with the companion apps jellyfin provides. &lt;/p&gt;

&lt;p&gt;Fastforward a couple months and I decided to buy a domain and I needed a website for said domain. I figured, let me build the website and point the domain to my homelab. This was not easy. Instead, I decided to buy the cheapest VPS I could find. I selected &lt;a href="//racknerd.com"&gt;Racknerd&lt;/a&gt; and at $24/year, this was a steal. But now I needed my local machine to talk to my racknerd vps. Enter tailscale.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="//tailsacle.com"&gt;Tailscale&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Tailscale allows you to create your own network for your devices. This network is not open to any devices not on the network. I made a network with my local machine and my racknerd vps. And now, if I need to route any traffic to my local machine, I push it through the racknerd server. &lt;/p&gt;

&lt;p&gt;However, as long as anything is exposed to the internet, there will be people trying to break in. For a while I did not have any issues with this, until I got hit by a random DDOS attack. No reason is known as to why, except that, I had a VPS server facing the open internet. So I had to reinforce the walls. Ladies and gentlemen, please welcome Fail2Ban.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/fail2ban/fail2ban" rel="noopener noreferrer"&gt;Fail2Ban&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;LESSON 1:&lt;/strong&gt; Trust nothing and nobody on the open internet.&lt;/p&gt;

&lt;p&gt;Security above all else. Fail2Ban will scan your log files and bans IP addresses conducting too many failed login attempts. As of right now, I am at about 9000 banned IPs on all my fail2ban jails. That is a lot more than I thought I would be at. &lt;/p&gt;

&lt;p&gt;Fail2Ban config is pretty straight-forward and well documented. &lt;/p&gt;

&lt;h2&gt;
  
  
  Local Homelab Machine
&lt;/h2&gt;

&lt;p&gt;I am first and foremost a distributed systems developer. So on my local homelab setup, I tend to push my esoteric distributed systems ideas to the limit. &lt;/p&gt;

&lt;p&gt;I also have regular projects running prod on my homelab. In fact, what made me want to write this post is the fact that I had to deploy an application for a client demo on my server and after spending the better part of Sunday evening working through production bugs, I managed to get the demo running. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LESSON 2:&lt;/strong&gt; Homelabbing could act as your own staging &amp;amp; production environments.&lt;/p&gt;

&lt;p&gt;I have previously deployed on services like &lt;a href="//gigalixir.com"&gt;gigalixir&lt;/a&gt;, &lt;a href="//heroku.com"&gt;Heroku&lt;/a&gt; and &lt;a href="//fly.io"&gt;Fly.io&lt;/a&gt;. While I agree that for full scale production applications, they are amazing. I would argue that a homelab server works amazing as your personal staging enviroment. Arguably also saves on time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vim, Emacs, Nano and the terminal.
&lt;/h2&gt;

&lt;p&gt;I am not at a level where I can use Vim as my default text editor or IDE. Far from it. However, I have come to appreciate never leaving the terminal in order to change a config file. I have learned enough that I can jump around the file with moderate success. But for daily development I will still use Zed. &lt;/p&gt;

&lt;p&gt;Further, I have learned the benefit of a customized terminal. While I initially started using &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;ohMyZsh&lt;/a&gt; for the aesthetic, the plugins have come in clutch at speeding up my workflows. For instance the syntax highlighting plugin has been a necessity on any system I am using.&lt;/p&gt;

&lt;h2&gt;
  
  
  System monitoring.
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;LESSON 3:&lt;/strong&gt; Manage and prioritize resource usage. &lt;/p&gt;

&lt;p&gt;I have 4GB on RAM on my homelab server. What this means is that, now more than ever, memory is at a premium, thanks AI (derogatory). I have had to learn what services/apps use a lot more memory than others. I have even written a script to only allow some apps to run at specific times when I know the resource usage will be low. Working within limits is amazing at teaching you to prioritize the important parts. You can trust that everything is running well, but it is much better to control when specific applications and services should run. For instance, because I am the sole user of my jellyfin service, I have it set to start up at 6pm and stop at 12am. This means that any heavy media processing workloads will never show up during the day when other services might need system resources more. &lt;/p&gt;

&lt;h2&gt;
  
  
  Docker and Kubernetes
&lt;/h2&gt;

&lt;p&gt;These will always show up when you are running multiple opensource applications on your homelab. &lt;/p&gt;

&lt;p&gt;I have spent more time the past 6 months writing and editing dockerfiles and configuring kubernetes than I have the past 3 years. Am I perfect? No. But I have developed a sense about what will break my docker config and those random bugs that show up preventing your container from running do not send me to stackoverflow as often. It is amazing practice for working with docker and K8s. &lt;/p&gt;

&lt;p&gt;Also this is an amazing story about &lt;a href="https://www.youtube.com/watch?v=sTLiqkMwJb4&amp;amp;t=721s&amp;amp;pp=ygUSd2h5IGt1YmVybmV0ZXMgd29u" rel="noopener noreferrer"&gt;kubernetes&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Trust nothing and nobody on the open internet.&lt;/li&gt;
&lt;li&gt;Only expose what needs to be exposed.&lt;/li&gt;
&lt;li&gt;Your homelab is your own personal staging environment.&lt;/li&gt;
&lt;li&gt;Manage and prioritize resource usage.&lt;/li&gt;
&lt;li&gt;Anything is possible. Build what you can(and what your hardware will allow). I have seen people running whole machine learning clusters on their homelab.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Future plans
&lt;/h2&gt;

&lt;p&gt;There are a lot of resources for self hosted application alternatives. &lt;a href="//selfh.st"&gt;selfh.st&lt;/a&gt; does a great job at cataloguing them. I have my eye on a couple when I have time. Most likely &lt;a href="https://plausible.io/?ref=selfh.st" rel="noopener noreferrer"&gt;Plausible&lt;/a&gt; will be the next self hosted OSS app I setup. &lt;/p&gt;

&lt;p&gt;Should the memory shortage stop being a shortage, I will get more memory for my server. Also did I mention the server uses only 65W of power at full power? &lt;/p&gt;

&lt;h2&gt;
  
  
  References &amp;amp; Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="//tailscale.com"&gt;Tailscale&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//selfh.st"&gt;Selfh.st&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fail2ban/fail2ban" rel="noopener noreferrer"&gt;Fail2Ban&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://plausible.io/?ref=selfh.st" rel="noopener noreferrer"&gt;Plausible&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;ohMyZsh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//github.com/zsh-users/zsh-syntax-highlighting/"&gt;OhMyZsh syntax highlighting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=sTLiqkMwJb4&amp;amp;t=721s&amp;amp;pp=ygUSd2h5IGt1YmVybmV0ZXMgd29u" rel="noopener noreferrer"&gt;The story of Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//gigalixir.com"&gt;gigalixir&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//heroku.com"&gt;Heroku&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//fly.io"&gt;Fly.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//racknerd.com"&gt;Racknerd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//jeffgeerling.com"&gt;Jeff Geerling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;astro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jellyfin.org/" rel="noopener noreferrer"&gt;jellyfin&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>homelab</category>
    </item>
    <item>
      <title>Applied for a job and found an edge case in their code they had not covered</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Tue, 19 May 2026 11:28:19 +0000</pubDate>
      <link>https://dev.to/w3ndo/applied-for-a-job-and-found-an-edge-case-in-their-code-they-had-not-covered-28p0</link>
      <guid>https://dev.to/w3ndo/applied-for-a-job-and-found-an-edge-case-in-their-code-they-had-not-covered-28p0</guid>
      <description>&lt;p&gt;I applied to a company for developer role. However, when I usually apply for jobs I usually add the company name to my email. For instance &lt;code&gt;myemail@test.com&lt;/code&gt; becomes &lt;code&gt;myemail+companyname@test.com&lt;/code&gt;. Totally valid way to keep track of who perhaps leaked your email. &lt;/p&gt;

&lt;p&gt;So now this company sent me a link to a test they wanted me to take with my email address as a query parameter. Question to the class, does anyone see the problem in this link?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://linktotest.com/schedule?email=myemail+companyname@test.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No? Let's talk about URL encoding. URLs use specific characters for structure. For instance, &lt;code&gt;:&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt; and &lt;code&gt;+&lt;/code&gt; are all valid structural parts of this URL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://www.mywebsite.com/path?type=star+wars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what happens when we want to use those same characters as data and not structure in the URL? &lt;/p&gt;

&lt;p&gt;Say that in the query params, we want to pass the email(Very insecure, do not do this)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://emailfinder.com?email=user@email.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but this user is like me and has a &lt;code&gt;+&lt;/code&gt; in their email so now we have&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://emailfinder.com?email=user+tracker@email.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
plaintext&lt;/p&gt;

&lt;p&gt;These query params are not the same. &lt;br&gt;
For the first one translates to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;email = "user@email.com"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
plaintext&lt;br&gt;
And the second&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;email = "user tracker@email.com"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
plaintext&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;+&lt;/code&gt; symbol encodes a single whitespace in the URL &lt;strong&gt;IFF(if and only if)&lt;/strong&gt; it occurs in query params!!! When &lt;code&gt;+&lt;/code&gt; occurs before the &lt;code&gt;?&lt;/code&gt; then it is just a plus symbol. And this explains why it causes bugs sometimes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Path:     /files/hello+world.txt     → + is a LITERAL plus sign
Query:    ?search=hello+world        → + is a SPACE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
plaintext&lt;/p&gt;

&lt;p&gt;So now the link the company sent me kept raising an error saying this email doesn't exist. &lt;/p&gt;

&lt;p&gt;The fix?&lt;/p&gt;

&lt;p&gt;Encode the &lt;code&gt;+&lt;/code&gt; symbol. so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://emailfinder.com?email=user%2Btracker@email.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read a breakdown on URL Encoding here &lt;a href="https://www.w3schools.com/tags//ref_urlencode.asp" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After making that substitution, it worked and I was able to schedule my interview. Fingers crossed I pass the interview, but it was a fun edge case to run into. &lt;/p&gt;

</description>
      <category>urlencoding</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Simulating Boids with ECSx and liveview</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Mon, 09 Mar 2026 10:06:24 +0000</pubDate>
      <link>https://dev.to/w3ndo/simulating-boids-with-ecsx-and-liveview-124m</link>
      <guid>https://dev.to/w3ndo/simulating-boids-with-ecsx-and-liveview-124m</guid>
      <description>&lt;p&gt;You can view the live simulation &lt;a href="https://boids-with-ecsx.gigalixirapp.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check the code &lt;a href="https://github.com/W3NDO/boids" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Entity Component Systems for Simulations
&lt;/h2&gt;

&lt;p&gt;Entity Component Systems are a new concept I have come to learn as I am learning more about Video Game development. I was then presently surprised to find that a developer &lt;a href="https://github.com/APB9785" rel="noopener noreferrer"&gt;Andrew Berrien&lt;/a&gt; had built a library, &lt;a href="https://github.com/ecsx-framework/ECSx" rel="noopener noreferrer"&gt;ecsx&lt;/a&gt; for developing entity component systems in Elixir. Naturally I had to play around with it. &lt;/p&gt;

&lt;p&gt;In this article, I will cover what entity component systems are, how we can use them to build a boid simulation(after all, what is a game but an interactive simulation) and how we can learn to optimize simulations. We also explore ECSx and ECSx Dashboard. All references used are at the end. &lt;/p&gt;

&lt;h3&gt;
  
  
  What are Entity component systems??
&lt;/h3&gt;

&lt;p&gt;They are an architectural pattern often used in video game development. They allow for re-usability by separating data from behavior. It is a pattern that obeys the "composition over inheritance principle"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ECS is an architecture for building real-time games and simulations, wherein data about Entities is stored in small fragments called Components, which are then read and updated by Systems.&lt;br&gt;
~Mike Binns, ecsx README.md&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Entities
&lt;/h4&gt;

&lt;p&gt;These represent a thing in a video game. A distinct object representing an actor in a simulated space. For instance, if we are playing flappy bird, the bird would be an entity. &lt;/p&gt;

&lt;p&gt;Entities have no actual data or behaviors. &lt;/p&gt;

&lt;h4&gt;
  
  
  Components
&lt;/h4&gt;

&lt;p&gt;They are data types consisting of unique behavior assigned to an entity. They are re-usable modules that are attached to entities, providing behavior, functionality and appearance forming an entity. &lt;/p&gt;

&lt;p&gt;In our flappy bird example, some components would be a y_coordinate. When attached to a bird entity we know how high on the screen the bird is.&lt;/p&gt;

&lt;h4&gt;
  
  
  Systems
&lt;/h4&gt;

&lt;p&gt;Systems provide the logic that operates on the components. &lt;/p&gt;

&lt;p&gt;For instance, in our flappy bird example, a jump system would update the y_coordinate of the bird when a click occurs. &lt;/p&gt;

&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System&lt;/strong&gt; : This listens to outside events and publishes updates to the components. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Components&lt;/strong&gt; : They listen to the system events and update their state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entities&lt;/strong&gt;: They gain behavior through changes in their associated components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ECSx
&lt;/h2&gt;

&lt;p&gt;The best intro I could give you for ecsx would be for you to watch &lt;a href="https://youtu.be/E9pZP5jUYZg?si=IVVny2LOJTQAeECV" rel="noopener noreferrer"&gt;Andrew Berrien's talk&lt;/a&gt; at ElixirConfUS 2023. &lt;/p&gt;

&lt;p&gt;The highlights are as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Entities within ecsx are conceptual.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Components and Systems have generators you can use&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix ecsx.gen.component component_name component_type
mix ecsx.gen.system system_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a manager that controls initialization components, and which components and systems are known to the program&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tags are a special type of component that do not have a type.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Boids and our simulation.
&lt;/h2&gt;

&lt;p&gt;Craig Reynolds introduced the Boid algorithm in 1986. It is a computational model that simulates flocking behavior based on 3 rules; separation, alignment and cohesion. It have since been used widely in computer graphics and robotics to model flocking and swarming behavior.&lt;/p&gt;

&lt;p&gt;To design a boids simulation with ecsx, we need to define our components and systems. We will have 4 systems, &lt;em&gt;separation&lt;/em&gt;, &lt;em&gt;cohesion&lt;/em&gt;, &lt;em&gt;alignment&lt;/em&gt; and &lt;em&gt;movement&lt;/em&gt;. With regards to components, we need to consider that we will require to do a lot of 2d vector manipulation. As such, most of our components will have an x component and a y component. For instance, Velocity will be split into 2 components XVelocity and YVelocity.&lt;/p&gt;

&lt;p&gt;The reason for this split is that ecsx only allows a small subset of types for the components. &lt;/p&gt;

&lt;p&gt;From the internal documentation of the library&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The type of value which will be stored in this component type.  Valid types are: &lt;code&gt;:atom, :binary, :datetime, :float, :integer&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But this is ok, because we can build helpers that make it easier to work with these separate components. &lt;/p&gt;

&lt;p&gt;Further, we also define a small utility to assist with some common calculations. We will call this utility Vector, because it helps us with Vector calculations. &lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the systems
&lt;/h2&gt;

&lt;p&gt;We have 4 systems defined, &lt;code&gt;movement&lt;/code&gt;, &lt;code&gt;cohesion&lt;/code&gt;, &lt;code&gt;separation&lt;/code&gt; and &lt;code&gt;alignment&lt;/code&gt;. The systems &lt;code&gt;cohesion&lt;/code&gt;, &lt;code&gt;separation&lt;/code&gt; and &lt;code&gt;alignment&lt;/code&gt; systems will update the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; components associated with those systems. For instance &lt;code&gt;x_cohesion&lt;/code&gt; and &lt;code&gt;y_cohesion&lt;/code&gt; are updated by the cohesion systems and so forth. &lt;/p&gt;

&lt;p&gt;The movement of a boid is defined by the sum of the result of these systems. The purpose of the movement system is to that sum these results and update the x and y position components. &lt;/p&gt;

&lt;h3&gt;
  
  
  Alignment System
&lt;/h3&gt;

&lt;p&gt;Alignment force helps the boids coordinate their direction with nearby boids. It encourages the boids to match the average velocity of their closest neighbors ensuring the group moves in a unified manner.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First we check if there are any neighbors within the alignment radius, if not the force is zero&lt;/li&gt;
&lt;li&gt;Sum all the neighbors' velocity vectors and average them to determine the group's general movement direction&lt;/li&gt;
&lt;li&gt;Instead of directly setting the velocity to the average, we subtract the boid's current velocity from it, creating a smooth transition instead of an abrupt change. &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Cohesion System
&lt;/h3&gt;

&lt;p&gt;The cohesion force ensures that boids stay close to their group, preventing them from drifting apart. It calculates the center of mass of the nearby boids and applying a force that pulls the boids towards this center&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We check if there are any neighboring boids. If not the force is set to a zero vector&lt;/li&gt;
&lt;li&gt;Average the position of all nearby boids to find the center of mass of the local group&lt;/li&gt;
&lt;li&gt;The boid then moves toward this center helping it stay within the flock.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Separation System
&lt;/h3&gt;

&lt;p&gt;Prevents boids from colliding with each other by making them steer away from nearby neighbors. We calculate the this by tracking the closest boids within a certain radius and calculate a repulsion force based on their distances. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The closer a neighboring boid is, the stronger the repulsion force should be. &lt;/li&gt;
&lt;li&gt;The &lt;code&gt;separation_intensity&lt;/code&gt; is a parameter that follows a hyperbolic function of the form &lt;code&gt;f(x) = a/x&lt;/code&gt; which means that as the distance decreases, the force increases. &lt;/li&gt;
&lt;li&gt;The repulsion forces from all the neighbors are summed and averages ensuring smooth movement instead of abrupt changes in direction.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Movement System
&lt;/h3&gt;

&lt;p&gt;We define weights to the 3 other forces, cohesion, separation and alignment. These allow us to provide greater control over their combined effect. After weighting, each force is added to the boid's velocity vector. &lt;/p&gt;

&lt;p&gt;Once the velocity is updated, the boid's position is recalculated. It's heading is adjusted to align with the velocity vector. We compute the orientation angle using &lt;code&gt;atan2&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  The ECSx Manager.
&lt;/h2&gt;

&lt;p&gt;Our manager is responsible for the startup state of the boids. According to the docs&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This module holds three critical pieces of data - component setup, a list of every valid component type, and a list of each game system in the order they are to be run.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our manager startup function will look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def startup do
    for _boid &amp;lt;- 1..@number_of_boids do
      entity = Base.encode16(:crypto.strong_rand_bytes(16), case: :lower)

      Boids.Components.BoidEntity.add(entity)
      Boids.Components.XCoord.add(entity, Enum.random(100..1400) * 1.0)
      Boids.Components.YCoord.add(entity, Enum.random(100..900) * 1.0)
      Boids.Components.Width.add(entity, 7.5)
      Boids.Components.Height.add(entity, 15.0)
      Boids.Components.Heading.add(entity, 360.0)
      Boids.Components.XVelocity.add(entity, Enum.random([-1, 1]) * 1.0)
      Boids.Components.YVelocity.add(entity, Enum.random([-1, 1]) * 1.0)
      Boids.Components.XCohesion.add(entity, 0.0)
      Boids.Components.YCohesion.add(entity, 0.0)
      Boids.Components.XAlignment.add(entity, 0.0)
      Boids.Components.YAlignment.add(entity, 0.0)
      Boids.Components.XSeparation.add(entity, 0.0)
      Boids.Components.YSeparation.add(entity, 0.0)
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We randomize the starting position to be random within our screen viewport. We also randomize our velocities. &lt;/p&gt;

&lt;h3&gt;
  
  
  Showing the boids
&lt;/h3&gt;

&lt;p&gt;To render our boids, we will use phoenix liveview with SVG graphics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def render(assigns) do
    ~H"""
    &amp;lt;div class="w-full p-4 bg-gray-800 h-screen border rounded-md border-black"&amp;gt;
      &amp;lt;div class="w-full flex items-center"&amp;gt;
        &amp;lt;p class="w-full text-white text-center text-2xl"&amp;gt;BOIDS&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;

    &amp;lt;!-- Render the Boids ViewFrame --&amp;gt;
      &amp;lt;div class="flex items-center justify-around"&amp;gt;
        &amp;lt;svg version="1.1" width="1500" height="900" xmlns="http://www.w3.org/2000/svg"&amp;gt;
          &amp;lt;rect width="100%" height="100%" fill="black" /&amp;gt;

          &amp;lt;%= for {height, l_position, r_position} &amp;lt;- @boids do %&amp;gt;
            &amp;lt;% [{h_1, h_2}, {l_1, l_2}, {r_1, r_2}] = [height, l_position, r_position] %&amp;gt;
            &amp;lt;polygon
              points={"#{h_1},#{h_2} #{l_1},#{l_2} #{r_1},#{r_2}"}
              fill={random_fill()}
            /&amp;gt;
          &amp;lt;% end %&amp;gt;
        &amp;lt;/svg&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    """
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each component &lt;code&gt;height, l_position&lt;/code&gt; and &lt;code&gt;r_position&lt;/code&gt; all have an &lt;code&gt;x,y&lt;/code&gt; component. These will be used to render the 3 vertices of a triangle. The boids will also have random colors for now. &lt;/p&gt;

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

&lt;p&gt;As it stands, there are 2 optimizations I am considering. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hash Partitioning&lt;/li&gt;
&lt;li&gt;Using Nx to perform vector math. &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Hash Partitioning
&lt;/h3&gt;

&lt;p&gt;Instead of calculating the forces separation, cohesion and separation for the show system, we can divide the viewport into quadrants and calculates forces for the boids in those quadrants.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Nx
&lt;/h3&gt;

&lt;p&gt;Numerical Elixir is optimized for faster mathematical computation in elixir. Right now I am doing Vector math with my own small &lt;a href="https://github.com/W3NDO/boids/blob/main/lib/boids/utils/vector.ex" rel="noopener noreferrer"&gt;vector module&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nx would do the math faster, however, in testing, I found that it actually increases system latency. &lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarking
&lt;/h2&gt;

&lt;p&gt;For benchmarking, we will render 50 boids on a view-port of 1500x1000 px. We use &lt;a href="https://github.com/ecsx-framework/ecsx_live_dashboard" rel="noopener noreferrer"&gt;ECSx-live-dashboard&lt;/a&gt; to check performance of the systems.&lt;/p&gt;

&lt;p&gt;First we look at performance without hash partitioning and without using Nx.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vkfadhk368ixkumuhmw.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%2F3vkfadhk368ixkumuhmw.png" alt="systems performance of 50 boids without hash partitioning and without nx" width="696" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we check how the boids perform with hash partitioning. We use 81 partitions&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy10cmrdhpjgfqbeljjoi.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%2Fy10cmrdhpjgfqbeljjoi.png" alt="systems performance of 50 boids with hash partitioning and without nx" width="696" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally we check performance with Nx and no hash partitioning. The Nx utility used can be found &lt;a href="https://github.com/W3NDO/boids/blob/develop/adding_nx/lib/boids/utils/vector_nx.ex" rel="noopener noreferrer"&gt;here&lt;/a&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsi1p2md4g2powmoova0w.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%2Fsi1p2md4g2powmoova0w.png" alt="systems performance of 50 boids without hash partitioning but using nx" width="696" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Well, none of my optimizations performed better, which is OK. It just means there is room for improvement. The first implementation appears to be the best performing one. &lt;/p&gt;

&lt;p&gt;I find that the hash partitioning makes the boids roughly align along the partition edges&lt;br&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%2Ftkvf2h45cjk90gd08bt0.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%2Ftkvf2h45cjk90gd08bt0.png" alt="hash partitioning boids makes rough alignment on the partition edges" width="800" height="839"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Further, I think the reason for terrible performance when using Nx, is that I am not using the correct functions. Perhaps using broadcast capabilities of Nx might be a better fit.&lt;/p&gt;

&lt;p&gt;Another optimization might be combining both Nx and hash partitioning. &lt;/p&gt;

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

&lt;p&gt;There is a lot more work to be done to make the system performance manageable. &lt;/p&gt;

&lt;p&gt;I think ECSx is very very neat for what it offers. It is simple to setup and understand. Further, Nx, while primarily for machine learning, is still an amazing library fr when you need high performance calculations. &lt;/p&gt;

&lt;p&gt;Liveview is still a work of art every time I use it. It is simple and gets more intuitive with practice. &lt;/p&gt;

&lt;p&gt;In the github repo, I have separate branches for &lt;a href="https://github.com/W3NDO/boids/tree/develop/hash_partitioning" rel="noopener noreferrer"&gt;hash_partitioning&lt;/a&gt; and &lt;a href="https://github.com/W3NDO/boids/tree/develop/adding_nx" rel="noopener noreferrer"&gt;nx optimisations. &lt;/a&gt;&lt;/p&gt;

</description>
      <category>ecsx</category>
      <category>liveview</category>
      <category>elixir</category>
      <category>simulation</category>
    </item>
    <item>
      <title>Let's build a test runner in Elixir.</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Mon, 03 Nov 2025 20:15:46 +0000</pubDate>
      <link>https://dev.to/w3ndo/lets-build-a-test-runner-in-elixir-4cl0</link>
      <guid>https://dev.to/w3ndo/lets-build-a-test-runner-in-elixir-4cl0</guid>
      <description>&lt;p&gt;I have not interacted much with metaprogramming in Elixir, but I thought it was time to check it out. &lt;/p&gt;

&lt;p&gt;For this article, we will be building a test runner to explore the topic. &lt;/p&gt;

&lt;p&gt;So let's begin. &lt;/p&gt;

&lt;h2&gt;
  
  
  Quote
&lt;/h2&gt;

&lt;p&gt;First, we need to understand how ExUnit tests are internally represented on the AST. &lt;/p&gt;

&lt;p&gt;We use Elixir's &lt;code&gt;quote&lt;/code&gt; and &lt;code&gt;unquote&lt;/code&gt; methods. &lt;code&gt;Quote&lt;/code&gt; allows you to get an internal representation of your expression as the AST. For intance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex&amp;gt; quote do: 1+2

{:+, [context: Elixir, imports: [{1, Kernel}, {2, Kernel}]], [1, 2]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The first element, &lt;code&gt;:+&lt;/code&gt;, is an atom denoting the function call, or another tuple, representing a nested node in the AST.&lt;/li&gt;
&lt;li&gt;The second element, &lt;code&gt;[context: Elixir, imports: [{1, Kernel}, {2, Kernel}]]&lt;/code&gt;, represents metadata about the expression&lt;/li&gt;
&lt;li&gt;The third element is a list of arguments for the function call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interestingly, this AST representation &lt;code&gt;{:+, [context: Elixir, imports: [{1, Kernel}, {2, Kernel}]], [1, 2]}&lt;/code&gt; is exactly what the compiler sees. Other languages, such as flavours of Lisp will have you write the source directly as an AST. Elixir however allows you to convert from high-level source to low-level AST with a simple &lt;code&gt;quote&lt;/code&gt; invocation. &lt;/p&gt;

&lt;p&gt;Next thing we need to understand about ASTs in Elixir is that some literals will have the same representation as high-level literals. Namely, &lt;code&gt;atoms, numbers, strings, lists&lt;/code&gt; and &lt;code&gt;tuples&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex&amp;gt; quote do: :atom #atoms
:atom
iex&amp;gt; quote do: 123 #numbers
123
iex&amp;gt; quote do: 3.14 #numbers
3.14
iex&amp;gt; quote do: [1, 2, 3] #lists
[1, 2, 3]
iex&amp;gt; quote do: "string" #strings
"string"
iex&amp;gt; quote do: {:ok, 1} #tuples
{:ok, 1}
iex&amp;gt; quote do: {:ok, [1, 2, 3]} #tuples
{:ok, [1, 2, 3]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Unquote
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;unquote&lt;/code&gt; Macro allows values to be injected into the AST that is being defined. These allows the outside bound &lt;code&gt;variables, epxressions&lt;/code&gt; and &lt;code&gt;blocks&lt;/code&gt; to be injected into the AST. For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex&amp;gt; number = 5
iex&amp;gt; ast = quote do
...&amp;gt;    number * 10
...&amp;gt; end
{:*, [context: Elixir, import: Kernel], [{:number, [], Elixir}, 10]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we see that the value of &lt;code&gt;number&lt;/code&gt; was not injected into the AST. We instead got a local reference for &lt;code&gt;number&lt;/code&gt; provided. However,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex&amp;gt; number = 5
iex&amp;gt; ast = quote do
...&amp;gt;    unquote(number) * 10
...&amp;gt; end
{:*, [context: Elixir, import: Kernel], [5, 10]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here because we called &lt;code&gt;unquote(number)&lt;/code&gt; the value 5 gets injected into the AST.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Quote&lt;/code&gt; and &lt;code&gt;Unquote&lt;/code&gt; allow you to build ASTs without fumbling with the AST by hand. &lt;/p&gt;

&lt;h2&gt;
  
  
  Building the test runner.
&lt;/h2&gt;

&lt;p&gt;Now let's start from the bottom. What is the basic building block of a test? I think it is the &lt;code&gt;assert&lt;/code&gt; call. This checks the equivalency of your function being tested and the expected result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assert MyFun.sum(2,5) == 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think we should build an implementation of this function.&lt;/p&gt;

&lt;p&gt;Side Note: I am a big proponent for TDD, so we start by builidng specs for our assert function. &lt;/p&gt;

&lt;p&gt;We define a function &lt;code&gt;assert/1&lt;/code&gt; which takes one argument that must be an expression checking equivalency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex&amp;gt; Tahinix.assert(2 == 2)
:ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our tests, we check the assert cases where we use &lt;code&gt;:!=&lt;/code&gt; and &lt;code&gt;:==&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule TahinixTest do
  require Tahinix

  use ExUnit.Case

  test "assert returns non-zero value when tests fail equivalence" do
    assert Tahinix.assert(2 == 4) == {:error, "Assertion failed: 2 == 4"}
  end

  test "assert returns non-zero value when tests fail non-equivalence" do
    assert Tahinix.assert(2 != 2) == {:error, "Assertion failed: 2 != 2"}
  end

  test "assert returns 0 when tests pass equivalence" do
    assert Tahinix.assert(2 != 4) == :ok
  end

  test "assert returns 0 when tests pass non-equivalence" do
    assert Tahinix.assert(2 == 2) == :ok
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now to make our tests pass, we start by implementing the code for &lt;code&gt;:==&lt;/code&gt; function first. We use pattern matching in the atributes in order to get our left hand side(lhs) and right hand side(rhs)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  #{:==, _, [lhs, rhs]}
  defmacro assert({:==, _, [lhs, rhs]}) do
    quote do
      lhs = unquote(lhs)
      rhs = unquote(rhs)
      result = lhs == rhs

      case result do
        false -&amp;gt; {:error, "Assertion failed: #{lhs} == #{rhs}"}
        _ -&amp;gt; :ok
      end
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will match expressions of the form &lt;code&gt;lhs == rhs&lt;/code&gt;, eg: &lt;code&gt;2 == 2&lt;/code&gt;. By having &lt;code&gt;lhs&lt;/code&gt; and &lt;code&gt;rhs&lt;/code&gt; in the head, we bind these values to the variable names, which we then pass to &lt;code&gt;unquote&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We then use case to get our return value. If the result if false, we return a tuple &lt;code&gt;{:error, reason_for_error}&lt;/code&gt;. Otherwise we retun &lt;code&gt;:ok&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Similarly for the assert &lt;code&gt;:!=&lt;/code&gt; we pattern match in the function head to get&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#{:!=, _, [lhs, rhs]}
  defmacro assert({:!=, _, [lhs, rhs]}) do
    quote do
      lhs = unquote(lhs)
      rhs = unquote(rhs)
      result = lhs != rhs

      case result do
        false -&amp;gt; {:error, "Assertion failed: #{lhs} != #{rhs}"}
        _ -&amp;gt; :ok
      end
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we run our tests, we find that they are all green now. &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwnu65cz1d8a8bp7qqbx.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%2Fvwnu65cz1d8a8bp7qqbx.png" alt=" " width="474" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The question now remains, how complex an expression could we include in the left hand side? Let us right some tests and see what we can add in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test "Assert returns :ok when tests pass with a complex lhs function" do
    assert Tahinix.assert(5 * 2 == 10) == :ok
end

test "Assert returns :error when tests fail with complex lhs" do
    assert Tahinix.assert(5 * 2 == 12) == {:error, "Assertion failed: 5 * 2 == 12"}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, can we test assertions on the output of functions? For instance,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    iex&amp;gt; Tahinix.assert(MyMod.say_hello == :hello)
    :ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test blocks
&lt;/h2&gt;

&lt;p&gt;The next thing we would need to figure out is how to implement a test block in our runner. Let us inspect the AST that ExUnit test case returns when we pass it into quote.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex&amp;gt; test_ast = quote do
        test "sum test" do
            assert 2 + 2 == 4
        end
    end

{:test, [],
 [
   "sum test",
   [
     do: {:assert, [],
      [
        {:==, [context: Elixir, imports: [{2, Kernel}]],
         [
           {:+, [context: Elixir, imports: [{1, Kernel}, {2, Kernel}]], [2, 4]},
           6
         ]}
      ]}
   ]
 ]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember the breakdown we did earlier on the parts of the return value of &lt;code&gt;quote&lt;/code&gt;? &lt;/p&gt;

&lt;p&gt;Here we see that the function name is &lt;code&gt;test&lt;/code&gt; with no metadata(2nd arg), and its arguments are a do block. All we need to do for our test runner is build a function that returns a similar AST.&lt;/p&gt;

&lt;p&gt;We start by writing our tests as usual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe "testing test blocks." do
    test "A test block of a passing equivalence test" do
      assert Tahinix.test("equivalence of 2 and 2", do: Tahinix.assert(2 == 2)) ==
               {:ok, ["equivalence of 2 and 2"]}
    end

    test "A test block of a passing non-equivalence test" do
      assert Tahinix.test("non-equivalence of 2 and 2", do: Tahinix.assert(2 != 2)) ==
               {:error, ["non-equivalence of 2 and 2", "Assertion failed: 2 != 2"]}
    end

    test "A test block of a failing equivalence test" do
      assert Tahinix.test("equivalence of 2 and 3", do: Tahinix.assert(2 == 3)) ==
               {:error, ["equivalence of 2 and 3", "Assertion failed: 2 == 3"]}
    end

    test "A test block of a failing non-equivalence test" do
      assert Tahinix.test("non-equivalence of 2 and 3", do: Tahinix.assert(2 != 3)) ==
               {:ok, ["non-equivalence of 2 and 3"]}
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need an implementation to make the tests green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmacro test(test_name, do: block) do
    quote do
      test_name = unquote(test_name)
      block = unquote(block)

      case block do
        :ok -&amp;gt; {:ok, [test_name]}
        {:error, reason} -&amp;gt; {:error, [test_name, reason]}
      end
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function will execute whatever is passed in the do block (in our case we will assume it is an assertion). If the block runs without error, we return an &lt;code&gt;{:ok, [test_name]}&lt;/code&gt; tuple. Otherwise, we return a &lt;code&gt;{:error, [test_name, reason]}&lt;/code&gt; tuple. &lt;/p&gt;

&lt;p&gt;And now we can write blocks of code like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tahinix.test "Adding 2 numbers" do
    Tahinix.assert(MyFun.sum(2,1) == 3)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it will return whether or not our test has passed. &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;With all this, we already have a sample test runner with the basics working. Further improvements might be adding in describe blocks, accumulating the test that pass and fail and showing them to the users, and adding in other important functions like &lt;code&gt;refute&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And now we also have a better understanding of how metaprogramming in Elixir can help us write utilities like test runners. But remember, the first rule of writing Macros, &lt;code&gt;Don't write Macros&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The full repo can be found &lt;a href="https://github.com/W3NDO/tahinix" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;References: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://pragprog.com/book/cmelixir/metaprogramming-elixir" rel="noopener noreferrer"&gt;Metaprogramming in Elixir by Chris McCord&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>elixir</category>
      <category>metaprogramming</category>
    </item>
    <item>
      <title>Convention over configuration in the age of AI. ( Happy accident ? )</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Sun, 13 Jul 2025 18:58:27 +0000</pubDate>
      <link>https://dev.to/w3ndo/convention-over-configuration-in-the-age-of-ai-happy-accident--1b9j</link>
      <guid>https://dev.to/w3ndo/convention-over-configuration-in-the-age-of-ai-happy-accident--1b9j</guid>
      <description>&lt;p&gt;&lt;code&gt;Convention over configuration&lt;/code&gt; is a dogma touted in the ruby and rails ecosystem. What does this mean??&lt;/p&gt;

&lt;p&gt;Often when working with rails you find that a lot of the libraries focus on building in line with the Rails conventions. This is great! Makes it easier to get up to speed with new libraries in Ruby/Rails. &lt;/p&gt;

&lt;p&gt;But now we have LLMs. And while LLMs are great they tend to provide code that doesn't always follow convention. Not always, but it happens. &lt;/p&gt;

&lt;p&gt;I read &lt;a href="https://github.com/ash-project/.github/blob/main/AI_POLICY.md" rel="noopener noreferrer"&gt;this&lt;/a&gt; post for the contribution policy on AI for the &lt;a href="https://ash-hq.org/" rel="noopener noreferrer"&gt;Ash Framework in Elixir&lt;/a&gt; and it got me thinking. With RAG(Retrieval augemnted generation) trying to control(somewhat) the results AI gives us, does that not mean that systems with built convention would tend to be more correct than those without? Especially because all the code these LLMs would be trained on would have (for the most part) followed convention?&lt;/p&gt;

&lt;p&gt;I think that the Rails community was onto something with convention over configuration, not even because of AI. But because it helps developers understand code faster. That AI would learn from this would be a great boon. &lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Exploring elixir processes using merge sort</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Mon, 14 Apr 2025 22:41:51 +0000</pubDate>
      <link>https://dev.to/w3ndo/exploring-elixir-processes-using-merge-sort-27m2</link>
      <guid>https://dev.to/w3ndo/exploring-elixir-processes-using-merge-sort-27m2</guid>
      <description>&lt;p&gt;In this article, I use the word &lt;strong&gt;parallel&lt;/strong&gt; and &lt;strong&gt;concurrent&lt;/strong&gt; interchangeably. I need the readers to know that they are not the same. Parallel means solving smaller problems of the same chunk of work at the same time. Concurrent means solving multiple problems in what &lt;em&gt;appears to be&lt;/em&gt; the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sorting.
&lt;/h2&gt;

&lt;p&gt;A classic problem in computer science. We always need things in the right order for a variety of reasons. There are many sorting algorithms each with their merits and demerits and some made for the fun of it(I'm looking at you Bogosort). A popular algorithm is merge sort. This algorithm uses a divide and conquer approach, subdividing the list into individual elements and then merging those elements in the correct order. This is a great problem to teach yourself how to get around the recursive nature of functional languages like Elixir or Erlang. &lt;/p&gt;

&lt;h2&gt;
  
  
  Elixir &amp;amp; its processes.
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Elixir runs on the Erlang VM, known for creating low latency, distributed, and fault-tolerant systems. &lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir Docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A cool thing that Elixir inherits from Erlang are its processes. All Elixir code runs inside processes that are isolated from each other, run concurrently and communicate via message passing. This allows Elixir developers to build distributed and fault tolerant processes. Of importance is the fact that &lt;strong&gt;Elixir processes are not Operating system processes.&lt;/strong&gt; Instead, they are lightweight in terms of memory and CPU, which allows a programmer to run tens or even hundreds of thousands of processes running simultaneously.There is a limit to how many processes you can spawn though(&lt;em&gt;definitely not foreshadowing&lt;/em&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Light weight processes and merge sort
&lt;/h2&gt;

&lt;p&gt;Well, if we have the ability to spawn light weight processes, a functional language and a sorting algorithm which we may be able to parallelize, the obvious next step is to absolutely misuse all of these ingredients until we have a working algorithm, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's cook
&lt;/h2&gt;

&lt;p&gt;Ok, if I recall from my multicore programming classes, the first thing you want to do when attempting to parallelize a problem that is  sequential is to identify which parts of the algorithm can be done in parallel. So we look at the parts of merge sort&lt;/p&gt;

&lt;h3&gt;
  
  
  Parts of the merge sort algorithm
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Find the mid-point of an array and divide the array into two halves, left and right&lt;/li&gt;
&lt;li&gt;Perform Mergesort on the left &lt;/li&gt;
&lt;li&gt;Perform Mergesort on the right&lt;/li&gt;
&lt;li&gt;Merge the two sorted halves(left and right)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation 1
&lt;/h2&gt;

&lt;p&gt;What if in our first solution, every time we want to perform merge sort on a sub_array, we spawn a new process and let it handle that sorting for us? That sounds like a great idea. Let us do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  def psort([]), do: []
  def psort([x]), do: [x]

  def psort(list) do
    {sub_arr1, sub_arr2} = Enum.split(list, floor(length(list) / 2))
    parent = self()

    child1 = spawn(fn -&amp;gt; send(parent, {:sorted, self(), psort(sub_arr1)}) end)
    child2 = spawn(fn -&amp;gt; send(parent, {:sorted, self(), psort(sub_arr2)}) end)

    sorted_sub_arr1 = receive_sorted(child1)
    sorted_sub_arr2 = receive_sorted(child2)

    merge(sorted_sub_arr1, sorted_sub_arr2)
  end

  defp receive_sorted(child_pid) do
    receive do
      {:sorted, ^child_pid, sorted_list} -&amp;gt;
        sorted_list

      # {:die} -&amp;gt;
      #   Process.exit(self(), :normal)
    end
  end

  def merge(l1, l2) do
    new_list = has_elements(l1, l2)
    new_list
  end

  def has_elements([], l2, c), do: Enum.reverse(c, l2)
  def has_elements(l1, [], c), do: Enum.reverse(c, l1)

  def has_elements([hd1 | r1], [hd2 | r2], c \\ []) do
    if hd1 &amp;gt; hd2 do
      has_elements([hd1 | r1], r2, [hd2 | c])
    else
      has_elements(r1, [hd2 | r2], [hd1 | c])
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, that's a lot of code, but let us focus on this part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    {sub_arr1, sub_arr2} = Enum.split(list, floor(length(list) / 2))
    parent = self()

    child1 = spawn(fn -&amp;gt; send(parent, {:sorted, self(), psort(sub_arr1)}) end)
    child2 = spawn(fn -&amp;gt; send(parent, {:sorted, self(), psort(sub_arr2)}) end)

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

&lt;/div&gt;



&lt;p&gt;We split the list into 2 sub_arrays and then spawn 2 processes to implement the merge_sort on those sub_arrays. `{:sorted, self(), psort(list)} is a message pattern that the parent process will expect to receive. You can read more about &lt;a href="https://dev.toSending%20and%20receiving%20messages"&gt;sending and receiving messages for processes in Elixir here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarking implementation 1 vs a sequential sort algorithm.
&lt;/h2&gt;

&lt;p&gt;You only ever see the benefits of parallelization when you get some benchmarks and comparison. So let us compare our implementation with a sequential merge sort algorithm. &lt;/p&gt;

&lt;p&gt;In our benchmark, we do 40 runs of random lists with varying sizes for both the sequential and parallel implementations.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa901qj9nolexgar7kpvs.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%2Fa901qj9nolexgar7kpvs.png" alt=" " width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wait, that's not what we want. Our parallel execution is consistently slower, regardless of the number of elements. We wanted to see some form of efficiency. Why?&lt;/p&gt;

&lt;h3&gt;
  
  
  Processes are not a free lunch
&lt;/h3&gt;

&lt;p&gt;Typically, parallelization will incur some overhead. Spawning process takes some time. Message passing takes some more time. All the while the sequential algorithm just chugs along undisturbed(so to speak).&lt;/p&gt;

&lt;p&gt;The way to use processes, it to use them when they would offer a clear benefit over a a sequential execution. So how do we do that?&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation 2
&lt;/h2&gt;

&lt;p&gt;What if we kept the implementation 1 method, but only do it depending on the size of a list passed. This means that is we want to sort a list of 100 elements, we could do this sequentially, but if the list has 1_000_000 elements, perhaps we could split the load among multiple processes?&lt;/p&gt;

&lt;p&gt;We add a simple check before splitting the array and instruct it to use our sequential merge sort implementation&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
if length(list) &amp;lt;= @list_threshold do&lt;br&gt;
      SequentialMergeSort.seq_sort(list)&lt;br&gt;
else&lt;br&gt;
   ...&lt;br&gt;
end&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;br&gt;
I set the threshold at 1000 elements in a list.&lt;/p&gt;

&lt;p&gt;This gives us the following results.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvhu3fybqrqbz3g7sfhd.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%2Fpvhu3fybqrqbz3g7sfhd.png" alt=" " width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we notice is that all we end up with a similar trend to the sequential algorithm. Which means that we are not really getting any speed up. And if we are, it is practically negligible. Interestingly though, the initial parallel implementation fails when we get to 1 million elements in a list because we spawn too many processes. But the simple solution allows us to go up to a million with no ill effect. So there is some benefit, but there has to be a better improvement somewhere. &lt;/p&gt;

&lt;p&gt;I will be working on this a bit more to find out more performance optimizations where I can find them. Feel free to checkout and contribute to the &lt;a href="https://github.com/W3NDO/Parallel-Merge-Sort" rel="noopener noreferrer"&gt;github repo&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>elixir</category>
      <category>sorting</category>
      <category>parallelcomputing</category>
      <category>distributedcomputing</category>
    </item>
    <item>
      <title>Using regex named capture groups to process lines in a CSV file in ruby.</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Sun, 16 Mar 2025 07:07:50 +0000</pubDate>
      <link>https://dev.to/w3ndo/using-regex-named-capture-groups-to-process-lines-in-a-csv-file-in-ruby-5egj</link>
      <guid>https://dev.to/w3ndo/using-regex-named-capture-groups-to-process-lines-in-a-csv-file-in-ruby-5egj</guid>
      <description>&lt;p&gt;Imagine you are processing a CSV file with information from weather stations.  single line might look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hamburg;22.0,Berlin;18.45,Tokyo;11.23,New York;4.20\n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The regex library in ruby allows for named captures. What this means is that you can specify a name for the text being matched. For instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/(?&amp;lt;city&amp;gt;[\w\s]+)/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;?&amp;lt;city&amp;gt;&lt;/code&gt; is the name of the capture group &lt;code&gt;w+(\s\w+)&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If we run the code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Hamburg;22.0".match(/(?&amp;lt;city&amp;gt;[\w\s]+)/).named_captures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return a ruby Hash that looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"city"=&amp;gt;"Hamburg"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use multiple named captures like this. However, if we want to return all matched captures in the string, we use the &lt;code&gt;.scan&lt;/code&gt; method instead. This will return an array of arrays, with each internal array being a matched capture. &lt;/p&gt;

&lt;p&gt;For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Hamburg;22.0,Berlin;18.45,Tokyo;11.23,New York;4.20\n".scan(/(?&amp;lt;city&amp;gt;[\w\s]+);(?&amp;lt;temp&amp;gt;\d+(\.\d+)?)/)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will return&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[["Hamburg", "22.0"], ["Berlin", "18.45"], ["Tokyo", "11.23"], ["New York", "4.20"]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it markedly easier to process the data. &lt;/p&gt;

&lt;p&gt;Named captures are pretty cool.&lt;/p&gt;

&lt;p&gt;Reference: &lt;br&gt;
&lt;a href="https://docs.ruby-lang.org/en/3.2/regexp_rdoc.html#label-Named+Captures" rel="noopener noreferrer"&gt;Regex Named Captures&lt;/a&gt;&lt;br&gt;
&lt;a href="https://rubular.com/" rel="noopener noreferrer"&gt;Rubular (Regex playground for Ruby)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>regex</category>
    </item>
    <item>
      <title>Using meta-programming in Ruby to build a REST API from a JSON file</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Mon, 21 Oct 2024 17:47:50 +0000</pubDate>
      <link>https://dev.to/w3ndo/using-meta-programming-in-ruby-to-build-a-rest-api-from-a-json-file-909</link>
      <guid>https://dev.to/w3ndo/using-meta-programming-in-ruby-to-build-a-rest-api-from-a-json-file-909</guid>
      <description>&lt;p&gt;So I asked ChatGPT, as you do, for stuff I could do with meta-programming in Ruby. If you don't know Ruby has amazing meta-programming support. And meta-programming is basically code that can modify itself while it's running. if you want to get technical with it, meta-programming is a computer programming technique in which computer programs have the ability to treat other programs as their data. Sometimes the program being used as data is the program running. Ruby excels at this. &lt;/p&gt;

&lt;p&gt;In this post I would introduce you to AutoAPI. The goal is that you could just write your endpoint specifications in a JSON file and then  the program starts a sinatra server that then has all the endpoints. As of now, the program only works for GET endpoints and I will update it as I go. It also returns either JSON or static HTML files. Further  updates would be to serve other MIME types. Let's begin.&lt;/p&gt;

&lt;p&gt;First of all, I wanted to run this as a shell script (cause I want to practice my scripting) so we start that shebang &lt;code&gt;#!/usr/bin/env ruby&lt;/code&gt; at the top of our file. Because I wanted it to be a shell script that would run from any folder, we have the script looking for a json file called &lt;code&gt;endpoints.json&lt;/code&gt; in the current folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Get the file and parse the entire file into a ruby hash
file = File.read("#{Dir.pwd}/endpoints.json")
endpoints_hash = JSON.parse(file)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use JSON to parse the file into a ruby Hash. This provides us with quality of life methods that we could use later, should we need to.&lt;/p&gt;

&lt;p&gt;Now because we are using &lt;a href="https://sinatrarb.com" rel="noopener noreferrer"&gt;Sinatra&lt;/a&gt; as our server, we would need a way to dynamically define new endpoints from the file. Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort. &lt;/p&gt;

&lt;p&gt;Let's first think about the method &lt;code&gt;send&lt;/code&gt; or &lt;code&gt;public_send&lt;/code&gt;. What these methods do is that they basically perform a method call to a class instance. For instance, if we have a class&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Class
    def hello
        puts "hello"
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we did &lt;code&gt;class.send(:hello)&lt;/code&gt; our result would be &lt;code&gt;hello&lt;/code&gt;.&lt;br&gt;
You could also pass parameters to send if the method takes in any arguments. &lt;/p&gt;

&lt;p&gt;To create a GET endpoint in Sinatra, we would write&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get '/hello' do
  'Hello world!'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we could dissect this and find that &lt;code&gt;:get&lt;/code&gt; is the method name, &lt;code&gt;'/hello&lt;/code&gt; is the path name and everything in the do block we shall call the &lt;code&gt;do_block&lt;/code&gt;. We could therefore also mentally re-write this as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get('/hello') do
    'Hello World'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;send(:get, '/hello', &amp;amp;block)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;&amp;amp;block&lt;/code&gt; is is the &lt;code&gt;do_block&lt;/code&gt; passed to the send method. &lt;/p&gt;

&lt;p&gt;All this to say, we need to define a method that will read the endpoint names and their associated methods to define new routes. It looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def create_endpoint(method, name, &amp;amp;block)
  Sinatra::Application.instance_eval do
    name = "/#{name}" if not name.start_with?(/\//)
    send(method, name, &amp;amp;block)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes in the REST method(GET, POST, PUT, DELETE), endpoint name and a code block. It then uses instance_eval to create a new route on the running instance of Sinatra. I added a check to ensure that the the endpoint name is preceded with a forward-slash because I ran into this random bug where the route would seem to be defined, but not accessible because it does not start with a forward-slash. finally, we just send the method as shown above. Simple, right?? Honestly, it's that simple.&lt;/p&gt;

&lt;p&gt;Now we should specify that the &lt;code&gt;endpoints.json&lt;/code&gt; file has a specific structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "GET": {
        "json_response": {
            "header": {"Content-Type": "application/json"},
            "response": {
                "content": { "message": "Hello AutoAPI"},
                "file": false
            }
        },
        "json_response_file": {
            "header": {"Content-Type": "application/json"},
            "response": {
                "file": true,
                "content": "endpoints.json"
            }
        },
        "html_file": {
            "header": {"Content-Type": "text/html"},
            "response": {
                "file": true,
                "content": "test.html"
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a sample of the endpoints file. We shall go through each endpoint one at a time. So all the REST verbs act as keys, that way all the GET routes are in the GET values etc etc. Our first endpoint is the &lt;code&gt;json_response&lt;/code&gt;. This one was supposed to test if I could get a hard coded response. The header is a nested object containing what you would expect in a typical HTTP GET request. Here we are passing the Content-Type only. For the response, we have a file key that specifies if the endpoint should send a file, or if it should send the value of content as JSON. This file should also be in the same folder as the script when running it. In this example, the second endpoint actually just returns the &lt;code&gt;endpoints.json&lt;/code&gt; file. The third endpoint has a different content type and returns a HTML file. &lt;/p&gt;

&lt;p&gt;To process these endpoints, this is the code I wrote&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;endpoints_hash.each do |method, paths|
  paths.each do |path, params|
    create_endpoint(method.downcase.to_s, "#{path.to_s}") do
      content_type :json if params["Content-Type"] == "application/json"
      content_type :html if params["Content-type"] == "text/html"
      if params["response"]["file"]
        send_file "#{Dir.pwd}/#{params["response"]["content"]}"
      else
        params["response"]["content"].to_json 
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All it does is iterate through all the endpoints and paths and defines routes for them. Note that I have to specify the content_type and I just use the passed headers to figure that out. The code might be a bit breakable and is a work in progress, but thus far, it works as seen below&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68h1q7iqvbwa0yj832rh.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%2F68h1q7iqvbwa0yj832rh.png" alt="JSON response file" width="799" height="254"&gt;&lt;/a&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3rxgghhbbb459tecyj5.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%2Fp3rxgghhbbb459tecyj5.png" alt="HTML file" width="799" height="254"&gt;&lt;/a&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd40oz12h8892zhiinp02.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%2Fd40oz12h8892zhiinp02.png" alt="JSON response" width="799" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For all 3 defined routes, we manage to get a response. And all with about 29 lines of code. &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasxixv7dhkrj8w4kcltl.jpg" 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%2Fasxixv7dhkrj8w4kcltl.jpg" alt="METAPROGRAMMING" width="799" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All code is available at &lt;a href="https://github.com/W3NDO/AutoAPI" rel="noopener noreferrer"&gt;my github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>metaprogramming</category>
      <category>ruby</category>
      <category>sinatra</category>
    </item>
    <item>
      <title>Random VS Code finds</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Fri, 11 Oct 2024 08:55:04 +0000</pubDate>
      <link>https://dev.to/w3ndo/random-vs-code-finds-3m11</link>
      <guid>https://dev.to/w3ndo/random-vs-code-finds-3m11</guid>
      <description>&lt;p&gt;Code OSS, (or VS Code) as an IDE are both really powerful when you get to leverage their available extensions. If you follow the VS Code tiktok you always find helpful tips and extensions that come in handy. In this series I will try and highlight some of my favourite extensions. &lt;/p&gt;

&lt;h2&gt;
  
  
  Thunderclient
&lt;/h2&gt;

&lt;p&gt;If you develop APIs and you want to test as you go, &lt;a href="https://www.thunderclient.com/" rel="noopener noreferrer"&gt;Thunderclient&lt;/a&gt; is a great alternative to Postman or Curl. With it being within your IDE you don't have to jump between applications whenever testing is required. I came across it when I was building a rails backend for a react application I was working on. Their documentation is easy enough to understand. If you are in any way versed with Postman, you will feel right at home with Thunderclient. &lt;/p&gt;

&lt;h2&gt;
  
  
  Todo Tree
&lt;/h2&gt;

&lt;p&gt;I write a lot on paper when I am working on a problem. The problem is usually that when I need to move around my work always gets messed up. I have also been in meetings where I add all my todos in a &lt;code&gt;todo.txt&lt;/code&gt; file. &lt;a href="https://github.com/Gruntfuggly/todo-tree" rel="noopener noreferrer"&gt;Todo Tree&lt;/a&gt; however is a bit of a saving grace. By just adding a comment starting with the word todo for instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// TODO write tests for this function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can then check the todo side tab and see all the todos left for me to do. Just don't forget to clear your todos before pushing to prod :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawio Integration
&lt;/h2&gt;

&lt;p&gt;During my college days I used to use &lt;a href="https://drawio.com" rel="noopener noreferrer"&gt;Drawio&lt;/a&gt; to draw wireframes and flowcharts. When I found that there is a &lt;a href="https://open-vsx.org/extension/hediet/vscode-drawio" rel="noopener noreferrer"&gt;VS Code extension&lt;/a&gt; that allows me to do it in the IDE it was a no brainer. I have found it is also useful whenever I am screen sharing to use it as a whiteboard during meetings. All you have to do is create a new file with the &lt;code&gt;.drawio&lt;/code&gt; extension and you're off to the races. You can then export to &lt;code&gt;.svg&lt;/code&gt; and &lt;code&gt;.png&lt;/code&gt; as you see fit. However, it is an unofficial extension, so you should beware. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwindcss Intellisense
&lt;/h2&gt;

&lt;p&gt;This was a recommendation from a friend. &lt;a href="https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss" rel="noopener noreferrer"&gt;Tailwindcss Intellisense&lt;/a&gt; provides insight into which CSS properties your tailwind code affects. Great for anyone who  does a lot of frontend with tailwindcss. &lt;/p&gt;

</description>
      <category>development</category>
      <category>vscode</category>
    </item>
    <item>
      <title>What are some fun or scary vulnerabilities you heard about recently??</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Mon, 12 Feb 2024 11:18:40 +0000</pubDate>
      <link>https://dev.to/w3ndo/what-are-some-fun-or-scary-vulnerabilities-you-heard-about-recently-mml</link>
      <guid>https://dev.to/w3ndo/what-are-some-fun-or-scary-vulnerabilities-you-heard-about-recently-mml</guid>
      <description></description>
      <category>discuss</category>
      <category>security</category>
    </item>
    <item>
      <title>Rails is Pretty Intuitive once you get used to it.</title>
      <dc:creator>Patrick Wendo</dc:creator>
      <pubDate>Mon, 21 Aug 2023 10:47:53 +0000</pubDate>
      <link>https://dev.to/w3ndo/rails-is-pretty-intuitive-once-you-get-used-to-it-295c</link>
      <guid>https://dev.to/w3ndo/rails-is-pretty-intuitive-once-you-get-used-to-it-295c</guid>
      <description>&lt;p&gt;I am currently working with background jobs in Rails through Active Job. And I needed some code to run before the job starts. So I thought, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Typically when you are working with the controllers and you want to run some code before an action you have a &lt;code&gt;before_action&lt;/code&gt; callback. In stimulus reflex, we have the &lt;code&gt;before_reflex&lt;/code&gt; callback. It should be safe to assume that Active Job must have a &lt;code&gt;before_perform&lt;/code&gt; callback&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I just set that up real quick and what do you know it run. I checked the docs just to make sure I was not having some vivid hallucination and true enough there is a &lt;code&gt;before_perform&lt;/code&gt; callback, as well as a couple other useful ones. &lt;/p&gt;

&lt;p&gt;This may not seem like much, but the ability to guesstimate what type of functions may be defined on a class is one of the few things I love about rails and ruby in general. It's like how the Enumerable class from which arrays, sets and hashes all inherit allows you to know that there are some methods that will always exist between these classes. And how a function ending in a question mark will return a boolean while one ending in an exclamation mark will do some potentially destructive action on whatever object calls it.&lt;/p&gt;

&lt;p&gt;Rails and Ruby is pretty awesome when it comes to the developer experience.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>coding</category>
    </item>
  </channel>
</rss>
