<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Developers Journey]]></title><description><![CDATA[I am a full-stack Ruby on Rails developer with experience in TDD, PostgreSQL, Docker, Heroku, performance optimization and Flutter mobile app development.]]></description><link>https://blog.przbadu.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1704337147504/orMIk0AuV.png</url><title>The Developers Journey</title><link>https://blog.przbadu.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 13 Sep 2026 08:48:36 GMT</lastBuildDate><atom:link href="https://blog.przbadu.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building a Real-Life Jarvis with OpenClaw]]></title><description><![CDATA[I built a Jarvis. Not the movie kind — but close enough.
5 AI agents running on a mini PC. They write code, fix bugs, create content, and give me morning briefings in a voice that doesn't sound like a robot.
In this post I'll show you how I did it an...]]></description><link>https://blog.przbadu.dev/building-a-real-life-jarvis-with-openclaw</link><guid isPermaLink="true">https://blog.przbadu.dev/building-a-real-life-jarvis-with-openclaw</guid><category><![CDATA[AI]]></category><category><![CDATA[automation]]></category><category><![CDATA[Homelab]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Sun, 15 Feb 2026 13:08:20 GMT</pubDate><content:encoded><![CDATA[<p>I built a Jarvis. Not the movie kind — but close enough.</p>
<p>5 AI agents running on a mini PC. They write code, fix bugs, create content, and give me morning briefings in a voice that doesn't sound like a robot.</p>
<p>In this post I'll show you how I did it and how you can set this up yourself.</p>
<h2 id="heading-the-setup">The Setup</h2>
<p><a target="_blank" href="https://openclaw.com">OpenClaw</a> lets you run AI agents locally. I set up 5 of them:</p>
<ul>
<li><strong>Mia</strong> — Manager. Coordinates everything, delegates tasks. She is my Jarvis.</li>
<li><strong>Kai</strong> — Dev agent. Writes code, commits, pushes</li>
<li><strong>Luna</strong> — Content. Blog posts, social media, YouTube scripts</li>
<li><strong>Hawk</strong> — QA. Watches for bugs, reviews Kai's work</li>
<li><strong>Tensor</strong> — ML. Handles anything machine learning</li>
</ul>
<p>They run on an AMD Strix Halo mini PC inside a Proxmox VM. That's it. No cloud bills. No expensive NVIDIA GPUs.</p>
<h2 id="heading-mission-control">Mission Control</h2>
<p>First thing I needed — a way to see what they're doing.</p>
<p>Built a kanban board in Rails 8. Agents create their own tasks, move them through columns (Backlog → In Progress → Review → Done). I can see everything from one dashboard.</p>
<p>Think Trello, but your cards write themselves.</p>
<h2 id="heading-morning-briefings">Morning Briefings</h2>
<p>This is the Jarvis part.</p>
<p>Every morning, Mia compiles:</p>
<ul>
<li>Weather</li>
<li>Today's tasks</li>
<li>Trending on X</li>
<li>YouTube recommendations</li>
<li>Reddit highlights</li>
</ul>
<p>Then Kokoro TTS reads it out loud. Runs on CPU — no GPU needed. Sounds surprisingly natural.</p>
<p>I wake up, grab coffee, and my computer tells me what's going on. That's it.</p>
<h2 id="heading-how-agents-work-together">How Agents Work Together</h2>
<p>Agents don't just sit idle waiting for commands. They have a smart heartbeat — periodic check-ins where they decide if something needs attention. Check emails, review PRs, monitor builds.</p>
<p>Kai pushes code. Hawk reviews it. If Hawk finds issues, it creates a task and assigns it back to Kai. I don't touch anything unless I want to.</p>
<h2 id="heading-content-pipeline">Content Pipeline</h2>
<p>Luna handles content:</p>
<ul>
<li>Blog drafts go to <code>blog/drafts/</code></li>
<li>I review and move to <code>blog/approved/</code></li>
<li>Then it gets posted</li>
</ul>
<p>Same flow for social media. And YouTube — using Remotion.js for automated video generation.</p>
<h2 id="heading-model-choices-pick-your-budget">Model Choices — Pick Your Budget</h2>
<p>This is the best part. You're not locked into expensive APIs.</p>
<p><strong>Free/local:</strong> GLM-4.7-Flash, Kimi K2.5, Qwen3-Coder — run on your hardware, $0 ongoing. If you have a Mac Studio M3 Ultra, you can run these smart models for free.</p>
<p><strong>Cheap:</strong> DeepSeek, Kimi API — $5-20/month for solid reasoning.</p>
<p><strong>Premium:</strong> Claude Opus/Sonnet, Gemini — $50-200/month but the best agent behavior.</p>
<p>I went with Claude because I wanted agents that actually work reliably. But you can start free and upgrade later.</p>
<h2 id="heading-the-stack">The Stack</h2>
<pre><code>Hardware:  AMD Strix Halo mini PC
<span class="hljs-attr">Infra</span>:    Proxmox VM (Ubuntu)
<span class="hljs-attr">Agents</span>:   OpenClaw (<span class="hljs-number">5</span> agents)
<span class="hljs-attr">Models</span>:   Claude Opus/Sonnet (or your choice)
<span class="hljs-attr">Dashboard</span>: Rails <span class="hljs-number">8</span> (Mission Control)
<span class="hljs-attr">TTS</span>:      Kokoro (local, CPU-only)
<span class="hljs-attr">Video</span>:    Remotion.js
<span class="hljs-attr">GPU</span>:      None
</code></pre><h2 id="heading-whats-next">What's Next</h2>
<ul>
<li>Voice commands (bidirectional — talk to agents, not just listen)</li>
<li>Smarter agent collaboration</li>
<li>More automations as I find things to automate</li>
</ul>
<h2 id="heading-try-it">Try It</h2>
<p>If you've got a decent machine, you can set this up yourself. OpenClaw is the backbone — the rest is just wiring things together.</p>
<p>I just built mine today.</p>
<p>Enjoy!!</p>
]]></content:encoded></item><item><title><![CDATA[Setting up Claude-Code with API Key]]></title><description><![CDATA[As of the writing of this blog post, there was no straight forward way to use API key with Claude code.
I found following way
I have figured out following way, but not sure if they will change something in future which will break this, by the time yo...]]></description><link>https://blog.przbadu.dev/setting-up-claude-code-with-api-key</link><guid isPermaLink="true">https://blog.przbadu.dev/setting-up-claude-code-with-api-key</guid><category><![CDATA[claude.ai]]></category><category><![CDATA[#anthropic]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Mon, 09 Jun 2025 11:49:47 GMT</pubDate><content:encoded><![CDATA[<p>As of the writing of this blog post, there was no straight forward way to use API key with Claude code.</p>
<h2 id="heading-i-found-following-way">I found following way</h2>
<p>I have figured out following way, but not sure if they will change something in future which will break this, by the time you read this article.</p>
<p>But anyways, here is the simple steps:</p>
<ol>
<li><h3 id="heading-open-claudesettingsjson-file-in-your-editor-and-put-following-code">Open <code>~/.claude/settings.json</code> file in your editor and put following code:</h3>
</li>
</ol>
<pre><code class="lang-bash">cat ~/.claude/settings.json

{
  <span class="hljs-string">"apiKeyHelper"</span>: <span class="hljs-string">"~/.claude/anthropic_key.sh"</span>
}
</code></pre>
<ol start="2">
<li><strong>Now create that shell script file, and open in your code editor e.g:</strong> <code>vi ~/.claude/anthropic_key.sh</code> <strong>and put following content in it, replace with your correct API key here.</strong></li>
</ol>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"sk_your_anthropic_api_key"</span>
</code></pre>
<ol start="3">
<li><strong>Make the shell script executable</strong></li>
</ol>
<pre><code class="lang-bash">sudo chmod +x ~/.claude/anthropic_key.sh
</code></pre>
<p>That’s it, now if you run <code>claude</code> it should confirm with you do you really want to API key, you should say <strong>yes</strong> or choose the option that says <strong>yes</strong></p>
<p>Enjoy!!</p>
]]></content:encoded></item><item><title><![CDATA[Flameshot shortcut in Ubuntu]]></title><description><![CDATA[Flameshot is powerful, yet simple to use open-source screenshot software.
How to install
They have really nice documentation on installing flameshot in different OS and distros, follow https://flameshot.org/docs/installation/installation-linux/
Ubunt...]]></description><link>https://blog.przbadu.dev/flameshot-shortcut-in-ubuntu</link><guid isPermaLink="true">https://blog.przbadu.dev/flameshot-shortcut-in-ubuntu</guid><category><![CDATA[flameshot]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Screenshot]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Sun, 08 Jun 2025 01:56:07 GMT</pubDate><content:encoded><![CDATA[<p><a target="_blank" href="https://flameshot.org/">Flameshot</a> is powerful, yet simple to use open-source screenshot software.</p>
<h2 id="heading-how-to-install">How to install</h2>
<p>They have really nice documentation on installing flameshot in different OS and distros, follow <a target="_blank" href="https://flameshot.org/docs/installation/installation-linux/">https://flameshot.org/docs/installation/installation-linux/</a></p>
<p><strong>Ubuntu</strong></p>
<pre><code class="lang-bash">sudo apt install flameshot
</code></pre>
<h2 id="heading-shortcut-key">Shortcut Key</h2>
<p>I really like to quickly launch flameshot using <code>ctrl+PrintScreen</code> keyboard shortcut. Here is how you can configure shortcut key on the Ubuntu.</p>
<ul>
<li><p>Open the settings app</p>
</li>
<li><p>Open the <code>keyboard -&gt; View and customize Shortcuts -&gt; Custom Shortcuts -&gt; Add</code></p>
</li>
<li><p>And Set following arguments</p>
</li>
</ul>
<blockquote>
<p>NOTE: You can give it your own preferred name. Use <code>which flameshot</code> to find the flameshot installation path. And use your own shortcut key.</p>
</blockquote>
<pre><code class="lang-bash">Name: flameshot
Command: /usr/bin/flameshot gui
shortcut: ctrl + <span class="hljs-built_in">print</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749347699178/2c44106c-ca12-409a-81fb-c29f8c082690.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[How to setup new Dev machine for Ruby On Rails]]></title><description><![CDATA[Omakub for Ubuntu
https://omakub.org/ is great and after installing ubuntu on my machine, the first thing I do is install omakub, this setup Dev environment with different softwares, Docker, etc. Check https://omakub.org/ for more information.
LazyVi...]]></description><link>https://blog.przbadu.dev/how-to-setup-new-dev-machine-for-ruby-on-rails</link><guid isPermaLink="true">https://blog.przbadu.dev/how-to-setup-new-dev-machine-for-ruby-on-rails</guid><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Thu, 23 Jan 2025 12:06:55 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-omakub-for-ubuntu">Omakub for Ubuntu</h2>
<p><a target="_blank" href="https://omakub.org/">https://omakub.org/</a> is great and after installing ubuntu on my machine, the first thing I do is install omakub, this setup Dev environment with different softwares, Docker, etc. Check <a target="_blank" href="https://omakub.org/">https://omakub.org/</a> for more information.</p>
<h2 id="heading-lazyvim">LazyVim</h2>
<p><a target="_blank" href="https://www.lazyvim.org/">https://www.lazyvim.org/</a> - This is really awesome IDE for neovim, if you are a vim user. Otherwise go with Vscode.</p>
<h2 id="heading-1-install-ruby-nodejs-and-postgresql">1. Install Ruby, nodejs and Postgresql</h2>
<blockquote>
<p>It uses <a target="_blank" href="https://github.com/jdx/mise">mise</a>, but you can use <a target="_blank" href="https://github.com/asdf-vm/asdf">asdf</a>. Because these tools helps you install different versions of ruby, node from single tools. If your system is already using different plugin manager for different tools e.g: <a target="_blank" href="https://github.com/rbenv/rbenv">rbenv</a> for ruby and <a target="_blank" href="https://github.com/nvm-sh/nvm">nvm</a> for node, you are good as well.</p>
</blockquote>
<p>Install Ruby, nodejs and Postgresql using <a target="_blank" href="https://gorails.com/setup/ubuntu/24.04">https://gorails.com/setup/ubuntu/24.04</a></p>
<ul>
<li>make sure to install following versions:</li>
</ul>
<pre><code class="lang-sh">ruby: 3.1.6
rails: 6.1.7.6
postgresql: latest (should work), upto version 15 is tested.
nodejs: 16.16.0 (use this exact version)

And also install `npm install -g yarn` because rails uses `yarn` package manager.
</code></pre>
<h2 id="heading-2-install-elastic-search">2. Install Elastic search</h2>
<ul>
<li>If you are using docker, you can simply use:</li>
</ul>
<pre><code class="lang-sh">docker start elesticsearch_7 || docker run -d --name elesticsearch_7 -p 9200:9200 -e \"http.host=0.0.0.0\" -e \"transport.host=127.0.0.1\" docker.elastic.co/elasticsearch/elasticsearch:7.10.1
</code></pre>
<p>And your elastic search should be setup for the project. Otherwise you can install elastic search in your system depending on OS e.g: <code>brew install elasticsearch@7.10.1</code> or using <code>apt get install ....</code></p>
<h2 id="heading-3-install-redis">3. Install Redis</h2>
<p>Install redis using <a target="_blank" href="https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/">https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/</a> for os of your choice</p>
<h2 id="heading-4-imagemagick">4. Imagemagick</h2>
<p><strong>ubuntu</strong></p>
<pre><code class="lang-sh">sudo apt-get install imagemagick
</code></pre>
<p><strong>macos</strong></p>
<pre><code class="lang-sh">brew install imagemagick
</code></pre>
<h2 id="heading-5-once-all-the-dependencies-are-installed-you-can-clone-the-project-and-install-required-dev-dependencies">5. Once all the dependencies are installed, You can clone the project and install required dev dependencies:</h2>
<pre><code class="lang-sh">git <span class="hljs-built_in">clone</span> git@github.com:&lt;appname&gt;.git
<span class="hljs-built_in">cd</span> &lt;my-app&gt;
bundle install
</code></pre>
<h2 id="heading-6-configure-config-variables-and-database-configuration">6. Configure config variables and database configuration</h2>
<p><strong>copy config/database.yml and adjust the username and password if needed</strong>. If you followed <a target="_blank" href="https://gorails.com/setup/macos/15-sequoia">https://gorails.com/setup/macos/15-sequoia</a> postgresql installation, you should have created your system user in your database, which don't require you to enter password to access the DB. In anycase, change your host, username and password depending on how you installed postgresql (e.g: docker has different host).</p>
<pre><code class="lang-sh">cp config/database.sample.yml config/database.yml
</code></pre>
<p><strong>copy config variables, or</strong> <code>.env</code> files depending on your project’s requirement.</p>
<pre><code class="lang-sh">touch config/application.yml
</code></pre>
<p>And ask for credentials from DEV.</p>
<h3 id="heading-final-step">Final step</h3>
<p>Once all the dependencies are installed and project is setup correctly, we can create database and generate database migration and start using application with:</p>
<blockquote>
<p>NOTE: make sure to run <code>rails db:schema:load</code> If there are corrupted migrations in the project.</p>
</blockquote>
<pre><code class="lang-sh"><span class="hljs-built_in">cd</span> &lt;my-app&gt; <span class="hljs-comment"># if not already</span>

rails db:create
rails db:schema:load
rails db:migrate
rails db:seed
</code></pre>
<p><strong>Run the project</strong></p>
<p>run these scripts in different terminal tabs or windows:</p>
<p><strong>rails app</strong></p>
<pre><code class="lang-sh">bin/setup
rails server
</code></pre>
<p><strong>sidekiq</strong></p>
<pre><code class="lang-sh">bundle <span class="hljs-built_in">exec</span> sidekiq
</code></pre>
<p>and visit <a target="_blank" href="http://localhost:3000">http://localhost:3000</a></p>
<p>If everything is setup correctly, your app should work.</p>
<h3 id="heading-setup-stripe-if-you-are-using-them">Setup stripe (if you are using them)</h3>
<p>Because you can't access our app without trial payment, you need to run the stripe webhooks to add subscriptions, to setup stripe follow:</p>
<p><strong>One time steps:</strong></p>
<ul>
<li><p>Install stripe cli: <a target="_blank" href="https://docs.stripe.com/stripe-cli">https://docs.stripe.com/stripe-cli</a></p>
</li>
<li><p>Login to stripe pex account. Ask for permission if needed.</p>
</li>
</ul>
<pre><code class="lang-sh">stripe login
</code></pre>
<p><strong>To listen to stripe webhooks</strong></p>
<pre><code class="lang-sh">stripe listen --forward-to localhost:3000/pay/webhooks/stripe
</code></pre>
<h3 id="heading-ultrahook-or-ngrok-as-a-dev-webhooks-listeners">Ultrahook or Ngrok as a Dev webhooks listeners</h3>
<ul>
<li><p><a target="_blank" href="https://www.ultrahook.com/">https://www.ultrahook.com/</a></p>
</li>
<li><p><a target="_blank" href="https://ngrok.com/">https://ngrok.com/</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Setting up New DEV machine with Omakub]]></title><description><![CDATA[Turn a fresh Ubuntu installation into a fully-configured, beautiful, and modern web development system by running a single command. That’s the one-line pitch for Omakub. No need to write bespoke configs for every essential tool just to get started or...]]></description><link>https://blog.przbadu.dev/setting-up-new-dev-machine-with-omakub</link><guid isPermaLink="true">https://blog.przbadu.dev/setting-up-new-dev-machine-with-omakub</guid><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Tue, 19 Nov 2024 05:22:36 GMT</pubDate><content:encoded><![CDATA[<p>Turn a fresh Ubuntu installation into a fully-configured, beautiful, and modern web development system by running a single command. That’s the one-line pitch for Omakub. No need to write bespoke configs for every essential tool just to get started or to be up on all the latest command-line tools. Omakub is an opinionated take on what Linux can be at its best.</p>
<p>We are going to use [omakub](<a target="_blank" href="https://omakub.org/">https://omakub.org/</a>) for that</p>
<pre><code class="lang-bash"> wget -qO- https://omakub.org/install | bash
</code></pre>
<p>This is the one line command that will setup bunch of applications that you can find here in the official docs: [omakub](<a target="_blank" href="https://omakub.org/">https://omakub.org/</a>).</p>
<h3 id="heading-postgresql-setup">PostgreSQL setup</h3>
<p>If you are using PostgreSQL for your local development database, omakub will install that inside Docker container. But what if you want to restore this database with the db dump file that you already have, here are the steps I took to setup my machine.</p>
<ol>
<li>Because postgres docker localhost:5432 host and port are exposed to client’s machine, we are able to run following command to access postgresql console</li>
</ol>
<pre><code class="lang-bash">psql -U postgres -h localhost
psql (16.4 (Ubuntu 16.4-0ubuntu0.24.04.2), server 16.5 (Debian 16.5-1.pgdg120+1))
Type <span class="hljs-string">"help"</span> <span class="hljs-keyword">for</span> <span class="hljs-built_in">help</span>.

postgres=<span class="hljs-comment">#</span>
</code></pre>
<p>Now lets say your ubuntu machine’s username is <code>john</code> we will create database and role to match this username.</p>
<pre><code class="lang-bash">postgres=<span class="hljs-comment"># CREATE ROLE john with LOGIN;</span>
CREATE ROLE
postgres=<span class="hljs-comment"># ALTER ROLE john with superuser;</span>
ALTER ROLE
postgres=<span class="hljs-comment"># create database john with owner = john;</span>
CREATE DATABASE
postgres=<span class="hljs-comment">#\q</span>
</code></pre>
<p>Add <code>superuser</code> and <code>login</code> roles to this user, you can also provide password to this user if you want.</p>
<p>Now lets say your dump file is located in <code>~/Downloads/latest.dump</code> we can restore this file with following command:</p>
<pre><code class="lang-bash">pg_restore --verbose --clean --no-acl --no-owner -h localhost -U john -d my-db ~/Downloads/latest.dump
</code></pre>
<p>And your pg restore should start.</p>
]]></content:encoded></item><item><title><![CDATA[Remap VScode keybinds to match with neovim]]></title><description><![CDATA[If you constantly switch between vscode and neovim (I am using lazyvim for my neovim config), then you might feel uncomfortable with different keybinds being used in different editor and your muscle memory will try to use vscode as a neovim but it do...]]></description><link>https://blog.przbadu.dev/remap-vscode-keybinds-to-match-with-neovim</link><guid isPermaLink="true">https://blog.przbadu.dev/remap-vscode-keybinds-to-match-with-neovim</guid><category><![CDATA[neovim]]></category><category><![CDATA[vim]]></category><category><![CDATA[vscode]]></category><category><![CDATA[editors]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Sun, 22 Sep 2024 04:43:24 GMT</pubDate><content:encoded><![CDATA[<p>If you constantly switch between <a target="_blank" href="https://code.visualstudio.com/">vscode</a> and <a target="_blank" href="https://neovim.io/">neovim</a> (I am using <a target="_blank" href="https://www.lazyvim.org/">lazyvim</a> for my neovim config), then you might feel uncomfortable with different keybinds being used in different editor and your muscle memory will try to use vscode as a neovim but it do not work.</p>
<p>Here are the common keybinds that I use in neovim and I am remapping vscode to use the same.</p>
<p><strong>Prerequisite</strong></p>
<p>Install <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=vscodevim.vim">https://marketplace.visualstudio.com/items?itemName=vscodevim.vim</a> plugin in your vscode, because all of the configs we are doing today depends on this plugin.</p>
<p><strong>Setup steps</strong></p>
<p>Lets open the user <code>keybinds.json</code> file in the vscode. For me it is located in <code>~/.config/Code/User/keybinds.json</code>.</p>
<pre><code class="lang-javascript"># keybinds.json
[
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"ctrl-h"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.navigateLeft"</span>,
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"ctrl-l"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.navigateRight"</span>,
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"ctrl-j"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.navigateDown"</span>,
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"ctrl-k"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.navigateUp"</span>,
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space e"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.toggleSidebarVisibility"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; (editorTextFocus || !inputFocus)"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"shift-l"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.nextEditorInGroup"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"(vim.mode == 'Normal' || vim.mode == 'Visual') &amp;&amp; (editorTextFocus || !inputFocus)"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"shift-h"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.previousEditorInGroup"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"(vim.mode == 'Normal' || vim.mode == 'Visual') &amp;&amp; (editorTextFocus || !inputFocus)"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space f f"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.quickOpen"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; (editorTextFocus || !inputFocus)"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space ,"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.showAllEditors"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; (editorTextFocus || !inputFocus)"</span>
  },

  <span class="hljs-comment">// Coding</span>
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"shift-j"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"editor.action.moveLinesDownAction"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode != 'Insert' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"shift-k"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"editor.action.moveLinesUpAction"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode != 'Insert' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"shift-k"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"editor.action.showHover"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space c a"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"editor.action.codeAction"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space c r"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"editor.action.rename"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space c s"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.gotoSymbol"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space b d"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.closeActiveEditor"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space g d"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"editor.action.revealDefinition"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space g r"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"editor.action.goToReferences"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; editorTextFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space s g"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.action.findInFiles"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; (editorTextFocus || !inputFocus)"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"space g g"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"workbench.view.scm"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"vim.mode == 'Normal' &amp;&amp; (editorTextFocus || !inputFocus)"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"ctrl-n"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"editor.action.addSelectionToNextFindMatch"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"(vim.mode == 'Normal' || vim.mode == 'Visual') &amp;&amp; (editorTextFocus || !inputFocus)"</span>
  },

  <span class="hljs-comment">// File Explorer</span>
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"r"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"renameFile"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"y"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"filesExplorer.copy"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"c"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"filesExplorer.copy"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"x"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"filesExplorer.cut"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"p"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"filesExplorer.paste"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"d"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"deleteFile"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"a"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"explorer.newFile"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"shift-a"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"explorer.newFolder"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"s"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"explorer.openToSide"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"shift+s"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"runCommands"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>,
    <span class="hljs-string">"args"</span>: {
      <span class="hljs-string">"commands"</span>: [
        <span class="hljs-string">"workbench.action.splitEditorDown"</span>,
        <span class="hljs-string">"explorer.openAndPassFocus"</span>,
        <span class="hljs-string">"workbench.action.closeOtherEditors"</span>
      ]
    }
  },
  {
    <span class="hljs-string">"key"</span>: <span class="hljs-string">"enter"</span>,
    <span class="hljs-string">"command"</span>: <span class="hljs-string">"explorer.openAndPassFocus"</span>,
    <span class="hljs-string">"when"</span>: <span class="hljs-string">"filesExplorerFocus &amp;&amp; foldersViewVisible &amp;&amp; !explorerResourceIsRoot &amp;&amp; !explorerResourceReadonly &amp;&amp; !inputFocus"</span>,
  },
]
</code></pre>
<p><strong>Keybinds Summary</strong></p>
<ul>
<li><p><code>ctrl+h</code>: It will move your cursor to the Left editor pane from horizontal split.</p>
</li>
<li><p><code>ctrl+l</code>: It will move your cursor to the Right editor pane from horizontal split</p>
</li>
<li><p><code>ctrl+j</code>: It will move your cursor to the Down editor pane from vertical split</p>
</li>
<li><p><code>ctrl+k</code>: It will move your cursor to the Up editor pane from vertical split</p>
</li>
<li><p><code>space+e</code>: Open/Close sidebar</p>
</li>
<li><p><code>shift+l</code>: Open next tab in the editor group (You can also remap this to <code>tab</code>)</p>
</li>
<li><p><code>shift+h</code>: Open previous tab in the editor group (You can also remap this to <code>shift+tab</code>)</p>
</li>
<li><p><code>space f f</code>: Show list of files in current project</p>
</li>
<li><p><code>space ,</code>: show list of files that are open in the buffer</p>
</li>
<li><p><code>shift+j</code>: If you have visual selection line(s), move those selected lines one step down</p>
</li>
<li><p><code>shift+k</code>: If you have visual selection line(s), move those selected lines one step up</p>
</li>
<li><p><code>shift+k</code>: In normal mode show hover documentation</p>
</li>
<li><p><code>space c a</code>: code action</p>
</li>
<li><p><code>space c r</code>: code reference</p>
</li>
<li><p><code>space c s</code>: go to symbol</p>
</li>
<li><p><code>space b d</code>: close active buffer/editor</p>
</li>
<li><p><code>space g d</code>: go to definition</p>
</li>
<li><p><code>space g r</code>: go to references</p>
</li>
<li><p><code>space s g</code>: find in files (search ripgrep in neovim)</p>
</li>
<li><p><code>space g g</code>: Git</p>
</li>
<li><p><code>ctrl+n</code>: multi selection</p>
</li>
</ul>
<p><strong>File explorer mappings:</strong></p>
<ul>
<li><p><code>r</code>: rename file/folder</p>
</li>
<li><p><code>y</code> / <code>c</code>: yank/copy file/folder</p>
</li>
<li><p><code>p</code>: paste file/folder</p>
</li>
<li><p><code>x</code>: Cut file/folder</p>
</li>
<li><p><code>d</code>: Delete file/folder</p>
</li>
<li><p><code>a</code>: add new file</p>
</li>
<li><p><code>shift+a</code>: Add new folder</p>
</li>
<li><p><code>s</code>: Open current file to horizontal split</p>
</li>
<li><p><code>shift+s</code>: Open current file to vertical split</p>
</li>
<li><p><code>enter</code>: Open selected file (In Mac OS, <code>enter</code> key opens rename action).</p>
</li>
</ul>
<p>Hope you find this useful.</p>
]]></content:encoded></item><item><title><![CDATA[Puma and PostgreSQL crash on M1 mac, Ruby on Rails 6.1]]></title><description><![CDATA[Have you encounter following bug when booting your M1 Mac machine when running PostgreSQL, Puma and Ruby on Rails application, this bug is frustrating but there is a really simple solution to this problem, which is to disable PGGSSENCMODE.
Solution:
...]]></description><link>https://blog.przbadu.dev/puma-and-postgresql-crash-on-m1-mac-ruby-on-rails-61</link><guid isPermaLink="true">https://blog.przbadu.dev/puma-and-postgresql-crash-on-m1-mac-ruby-on-rails-61</guid><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Puma]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Ruby]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Thu, 29 Aug 2024 08:00:22 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724918052921/82782042-17dd-4046-bfe2-6df9df2e1db7.png" alt class="image--center mx-auto" /></p>
<p>Have you encounter following bug when booting your M1 Mac machine when running PostgreSQL, Puma and Ruby on Rails application, this bug is frustrating but there is a really simple solution to this problem, which is to disable <code>PGGSSENCMODE</code>.</p>
<p><strong>Solution:</strong></p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> PGGSSENCMODE=<span class="hljs-string">"disable"</span>
</code></pre>
<p>put above code in your <code>~/.zshrc</code> or <code>~/.bashrc</code> file depending on which shell you are using.</p>
<p>I am using Zsh</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-variable">$SHELL</span>
/usr/bin/zsh
</code></pre>
<p>And finally, don't forget to reload your terminal, or restart your terminal.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> ~/.zshrc
rails s
</code></pre>
<p>It should fix that crash issue.</p>
]]></content:encoded></item><item><title><![CDATA[Setup VS Code to run rails test]]></title><description><![CDATA[Either you use RSpec or Mini test you want an easy way to run the test file you are working on, or you might like to run the test under cursor to quickly validate if the test are passing. Most of the time we run rails test command manually in the ter...]]></description><link>https://blog.przbadu.dev/setup-vs-code-to-run-rails-test</link><guid isPermaLink="true">https://blog.przbadu.dev/setup-vs-code-to-run-rails-test</guid><category><![CDATA[Ruby]]></category><category><![CDATA[vscode extensions]]></category><category><![CDATA[unit testing]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Wed, 28 Aug 2024 07:26:35 GMT</pubDate><content:encoded><![CDATA[<p>Either you use <a target="_blank" href="https://rspec.info/">RSpec</a> or <a target="_blank" href="https://guides.rubyonrails.org/testing.html">Mini test</a> you want an easy way to run the test file you are working on, or you might like to run the test under cursor to quickly validate if the test are passing. Most of the time we run <code>rails test</code> command manually in the terminal and its time consuming. For vim users there are so many great vim plugins like <a target="_blank" href="https://github.com/vim-test/vim-test">vim-test</a>.</p>
<p>In this article I am going to show you how you can easily setup a keybinding without using any additional VS code plugins to run tests directly from your editor.</p>
<ul>
<li><p>Open a <a target="_blank" href="https://code.visualstudio.com/">visual studio code</a>.</p>
</li>
<li><p>Click on setting -&gt; Keyboard shortcuts (you can also use <code>ctrl/cmd K, ctrl/cmd S</code></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724829656226/6e32f14b-a966-4793-8d98-fa37b3ebc51e.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>Open the setting file by clicking on the file icon at top right corner</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724829719837/ff9912d8-db2c-449c-9d3c-2df77d691a89.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>And add following 2 shortcuts, you can change the shortcut keys as you like, or add more if you want to run all the test cases</p>
</li>
<li><pre><code class="lang-json">  {
      <span class="hljs-comment">// run all tests in current test file</span>
      <span class="hljs-attr">"key"</span>: <span class="hljs-string">"shift+cmd+r"</span>,
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"workbench.action.terminal.sendSequence"</span>,
      <span class="hljs-attr">"args"</span>: { <span class="hljs-attr">"text"</span>: <span class="hljs-string">"bin/rails test ${relativeFile}\u000D"</span> }
    },
    {
      <span class="hljs-comment">// Run test at current line</span>
      <span class="hljs-attr">"key"</span>: <span class="hljs-string">"shift+cmd+i"</span>,
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"workbench.action.terminal.sendSequence"</span>,
      <span class="hljs-attr">"args"</span>: { <span class="hljs-attr">"text"</span>: <span class="hljs-string">"bin/rails test ${relativeFile}:${lineNumber}\u000D"</span> }
    }
</code></pre>
</li>
</ul>
</li>
<li><p>Open your terminal <code>ctrl/cmd ~</code> and also open your test file</p>
</li>
<li><p>You can use these shortcuts <code>shift + cmd/ctrl + r</code> to run all tests under current file</p>
</li>
<li><p>Or use <code>shift + cmd/ctrl + i</code> to run specific test under cursor.</p>
</li>
</ul>
<p>Enjoy!!</p>
]]></content:encoded></item><item><title><![CDATA[Migration from Heroku to Fly.io]]></title><description><![CDATA[We recently migrated our app from heroku to fly.io server with minimum downtime (almost 5 seconds of downtime) to be more precise. Here is how we did that.
First step obviously is to create a new branch and setup fly configuration. For that fly has a...]]></description><link>https://blog.przbadu.dev/migration-from-heroku-to-flyio</link><guid isPermaLink="true">https://blog.przbadu.dev/migration-from-heroku-to-flyio</guid><category><![CDATA[Devops]]></category><category><![CDATA[Docker]]></category><category><![CDATA[deployment]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Wed, 28 Aug 2024 07:14:09 GMT</pubDate><content:encoded><![CDATA[<p>We recently migrated our app from heroku to fly.io server with minimum downtime (almost 5 seconds of downtime) to be more precise. Here is how we did that.</p>
<p>First step obviously is to create a new branch and setup fly configuration. For that fly has a really nice CLI command which generates all the necessary configurations for you by inspecting your project files. For that first thing you need to do is Install fly CTL tool</p>
<ul>
<li><p>Install <a target="_blank" href="https://fly.io/docs/hands-on/install-flyctl/">https://fly.io/docs/hands-on/install-flyctl/</a></p>
</li>
<li><p>login to your fly app with</p>
</li>
</ul>
<pre><code class="lang-bash">fly auth login
</code></pre>
<p>Now you are ready to move forward.</p>
<p>Now follow <a target="_blank" href="https://fly.io/docs/rails/getting-started/existing/">https://fly.io/docs/rails/getting-started/existing/</a> to configure your existing ruby on rails application.</p>
<pre><code class="lang-bash">fly launch
</code></pre>
<p>and provide all necessary information if needed. This will generate <code>Dockerfile</code> if missing and <code>fly.toml</code> file and bunch of configuration files for your project.</p>
<p>Once your setup are done, it will also create a fly app in the fly dashboard, you can check it and confirm everything is fine.</p>
<p>Now next step is to Deploy our application with:</p>
<pre><code class="lang-bash">fly deploy
</code></pre>
<p>This command will build docker, push it to registry, run assets pre-compilation and so on, you can configure your Dockerfile as needed and finally it will deploy your app to fly server.</p>
<p>You will also see a fly domain where you can confirm all of the app features are working fine.</p>
<pre><code class="lang-bash">fly apps open
</code></pre>
<h3 id="heading-migration-from-heroku">Migration from Heroku</h3>
<p>Now that we our app is working as expected we need to migrate from Heroku for that follow: <a target="_blank" href="https://fly.io/docs/rails/getting-started/migrate-from-heroku/">https://fly.io/docs/rails/getting-started/migrate-from-heroku/</a></p>
<p>You can see, you can easily move all of your production Heroku Config vars with one single command:</p>
<pre><code class="lang-bash">heroku config -s | grep -v -e <span class="hljs-string">"RAILS_MASTER_KEY"</span> -e <span class="hljs-string">"DATABASE_URL"</span> -e <span class="hljs-string">"REDIS_URL"</span> -e <span class="hljs-string">"REDIS_TLS_URL"</span> | fly secrets import
</code></pre>
<p>I will keep sharing the migration process in upcoming articles.</p>
]]></content:encoded></item><item><title><![CDATA[How to Perform Load Testing on Authenticated Routes in Rails with k6 and Devise]]></title><description><![CDATA[Load testing is crucial for ensuring that web applications perform well under pressure. This becomes slightly more complex when applications have authenticated routes, especially when using frameworks like Ruby on Rails with authentication handled by...]]></description><link>https://blog.przbadu.dev/how-to-perform-load-testing-on-authenticated-routes-in-rails-with-k6-and-devise</link><guid isPermaLink="true">https://blog.przbadu.dev/how-to-perform-load-testing-on-authenticated-routes-in-rails-with-k6-and-devise</guid><category><![CDATA[Load Testing]]></category><category><![CDATA[k6]]></category><category><![CDATA[Grafana]]></category><category><![CDATA[Ruby]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Tue, 07 May 2024 06:16:49 GMT</pubDate><content:encoded><![CDATA[<p>Load testing is crucial for ensuring that web applications perform well under pressure. This becomes slightly more complex when applications have authenticated routes, especially when using frameworks like Ruby on Rails with authentication handled by Devise. In this blog post, we will go through the process of setting up load tests for such scenarios using <a target="_blank" href="https://k6.io">K6</a>, a powerful open-source load testing tool.</p>
<h3 id="heading-setting-up-your-testing-environment">Setting Up Your Testing Environment</h3>
<p>Before diving into the script setup, ensure you have <a target="_blank" href="https://k6.io">K6</a> installed. If you're using Docker, you can pull the <a target="_blank" href="https://k6.io">K6</a> image from Docker Hub:</p>
<pre><code class="lang-sh">docker pull grafana/k6
</code></pre>
<p>This ensures that your testing environment is isolated and consistent with your CI/CD pipeline.</p>
<h3 id="heading-writing-the-k6-script">Writing the k6 Script</h3>
<p>The objective is to simulate a user login through Devise to test authenticated routes. Here’s how you can achieve this with a <a target="_blank" href="https://k6.io">K6</a> script:</p>
<h4 id="heading-1-simulate-the-login"><strong>1. Simulate the Login</strong></h4>
<p>Devise handles user sessions, which we need to replicate in our load tests. We start by creating a function in our script to log in a user and capture the session cookie:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">'k6/http'</span>;
<span class="hljs-keyword">import</span> { check } <span class="hljs-keyword">from</span> <span class="hljs-string">'k6'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">login</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> payload = <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">user</span>: {
            <span class="hljs-attr">email</span>: <span class="hljs-string">'user@example.com'</span>,
            <span class="hljs-attr">password</span>: <span class="hljs-string">'password'</span>,
        },
    });

    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">headers</span>: {
            <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },
    };

    <span class="hljs-keyword">let</span> res = http.post(<span class="hljs-string">'https://yourdomain.com/users/sign_in'</span>, payload, params);
    check(res, {
        <span class="hljs-string">'is status 200'</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    });

    <span class="hljs-keyword">return</span> res.headers[<span class="hljs-string">'Set-Cookie'</span>];
}
</code></pre>
<h4 id="heading-2-access-authenticated-routes"><strong>2. Access Authenticated Routes</strong></h4>
<p>Once logged in, use the session cookie to make authenticated requests:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> cookie = login();
    <span class="hljs-keyword">let</span> params = {
        <span class="hljs-attr">headers</span>: {
            <span class="hljs-attr">Cookie</span>: cookie,
        },
    };

    <span class="hljs-keyword">let</span> res = http.get(<span class="hljs-string">'https://yourdomain.com/protected-route'</span>, params);
    check(res, {
        <span class="hljs-string">'is status 200'</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    });
}
</code></pre>
<h3 id="heading-load-testing-strategy">Load Testing Strategy</h3>
<p>Configure your <a target="_blank" href="https://k6.io">K6</a> scenarios to reflect real user behavior as closely as possible. This includes ramp-up phases, peak load, and ramp-down phases. Here is a simple configuration:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">stages</span>: [
        { <span class="hljs-attr">duration</span>: <span class="hljs-string">'1m'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">10</span> }, <span class="hljs-comment">// ramp up to 10 users</span>
        { <span class="hljs-attr">duration</span>: <span class="hljs-string">'5m'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">10</span> }, <span class="hljs-comment">// hold at 10 users</span>
        { <span class="hljs-attr">duration</span>: <span class="hljs-string">'1m'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">0</span> },  <span class="hljs-comment">// ramp down</span>
    ],
};
</code></pre>
<p><strong>Full script:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">'k6/http'</span>;
<span class="hljs-keyword">import</span> { check, sleep } <span class="hljs-keyword">from</span> <span class="hljs-string">'k6'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">stages</span>: [
        { <span class="hljs-attr">duration</span>: <span class="hljs-string">'1m'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">10</span> }, <span class="hljs-comment">// Example: Ramp up to 10 users over 1 minute</span>
        { <span class="hljs-attr">duration</span>: <span class="hljs-string">'5m'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">10</span> }, <span class="hljs-comment">// Stay at 10 users for 5 minutes</span>
        { <span class="hljs-attr">duration</span>: <span class="hljs-string">'1m'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">0</span> },  <span class="hljs-comment">// Ramp down to 0 users over 1 minute</span>
    ],
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">login</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> payload = <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">user</span>: {
            <span class="hljs-attr">email</span>: <span class="hljs-string">'user@example.com'</span>,
            <span class="hljs-attr">password</span>: <span class="hljs-string">'password'</span>,
        },
    });

    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">headers</span>: {
            <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },
    };

    <span class="hljs-keyword">let</span> res = http.post(<span class="hljs-string">'https://yourdomain.com/users/sign_in'</span>, payload, params);
    <span class="hljs-comment">// Check response to make sure login was successful</span>
    check(res, {
        <span class="hljs-string">'is status 200'</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    });

    <span class="hljs-comment">// Capture and return the cookie from the response header</span>
    <span class="hljs-keyword">return</span> res.headers[<span class="hljs-string">'Set-Cookie'</span>];
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> cookie = login();
    <span class="hljs-keyword">let</span> params = {
        <span class="hljs-attr">headers</span>: {
            <span class="hljs-attr">Cookie</span>: cookie,
        },
    };

    <span class="hljs-comment">// Perform a request to an authenticated route</span>
    <span class="hljs-keyword">let</span> res = http.get(<span class="hljs-string">'https://yourdomain.com/protected-route'</span>, params);
    check(res, {
        <span class="hljs-string">'is status 200'</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    });

    sleep(<span class="hljs-number">1</span>);
}
</code></pre>
<h3 id="heading-run-k6-container">Run K6 container</h3>
<p>To run <a target="_blank" href="https://k6.io">K6</a> from a Docker container, mount your test scripts into the container and direct <a target="_blank" href="https://k6.io">K6</a> to execute them. Here’s an example command:</p>
<pre><code class="lang-sh">docker run -v <span class="hljs-variable">${PWD}</span>/tests:/tests grafana/k6 run /tests/my-test-script.js
</code></pre>
<p><strong>Share a localhost network with docker container</strong></p>
<p>If your ruby on rails installation is done in Mac or Ubuntu machine directly without docker but you setup K6 using docker container and your script is using <code>http://localhost:3000</code> as an endpoint, running above command might give you <code>connection refused</code> error, to fix that issue we can simply share the host machine's network host by <code>--network="host"</code> flag in the <code>docker run</code> command</p>
<pre><code class="lang-bash">docker run --network=<span class="hljs-string">"host"</span> -v <span class="hljs-variable">${PWD}</span>/tests:/tests grafana/k6 run /tests/my-test-script.js
</code></pre>
<h3 id="heading-best-practices-and-considerations"><strong>Best Practices and Considerations</strong></h3>
<ul>
<li><p><strong>Rate Limiting and Lockouts:</strong> Ensure your test accounts are exempt from these features to avoid false negatives during testing.</p>
</li>
<li><p><strong>Security:</strong> Use environment variables to handle sensitive data like usernames and passwords securely.</p>
</li>
<li><p><strong>Session Management:</strong> If your application uses token-based authentication or has specific headers, adjust the script accordingly.</p>
</li>
<li><p><strong>Custom Headers or CSRF Tokens:</strong> If your Rails app uses CSRF tokens or other custom headers for security, make sure to capture and use them as required by your application.</p>
</li>
</ul>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>Load testing authenticated routes in a Rails application with Devise is straightforward with k6. By simulating real user behaviors, including logging in and accessing protected routes, you can uncover potential bottlenecks and ensure that your application can handle real-world usage. Remember to keep your scripts secure and handle user data responsibly.</p>
<p>With these guidelines, you're well-equipped to set up effective load testing for your Rails applications, helping to ensure robust performance and a great user experience.</p>
]]></content:encoded></item><item><title><![CDATA[How to add manually installed application to 'Show Applications' grid in Ubuntu]]></title><description><![CDATA[In this article we will cover how to add your manually installed application in the list of application list that Ubuntu shows natively. Often time we open such apps with terminal by running sh /path/to/application.sh or by running ./path/to/applicat...]]></description><link>https://blog.przbadu.dev/how-to-add-manually-installed-application-to-show-applications-grid-in-ubuntu</link><guid isPermaLink="true">https://blog.przbadu.dev/how-to-add-manually-installed-application-to-show-applications-grid-in-ubuntu</guid><category><![CDATA[Ubuntu]]></category><category><![CDATA[rubymine]]></category><category><![CDATA[Ruby]]></category><category><![CDATA[Ruby on Rails]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Mon, 06 May 2024 05:42:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714973996942/0c0351cf-6a56-4a9f-bf1c-13fd72358a26.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article we will cover how to add your manually installed application in the list of application list that Ubuntu shows natively. Often time we open such apps with terminal by running <code>sh /path/to/application.sh</code> or by running <code>./path/to/application.sh</code>.</p>
<p>So lets start with the template that is needed to add your application to the Ubuntu's desktop apps entry. All you need is:</p>
<ol>
<li><p>Create a launcher for your application with <code>.desktop</code> file extension</p>
</li>
<li><p>Place your <code>.desktop</code> file inside <code>~/.local/share/applications</code> directory or if you want it to be visible to all users then place it under <code>/usr/share/applications</code>.</p>
</li>
<li><p>Your <code>.desktop</code> file should have following contents:</p>
</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># &lt;application_name&gt;.desktop</span>

[Desktop Entry]
Name=Application Name
Comment=A short description of the application
Exec=/full/path/to/application/executable
Terminal=<span class="hljs-literal">false</span>
Type=Application
Icon=/full/path/to/icon-file
</code></pre>
<p>In this example we are going to use <code>RubyMine</code> as an example</p>
<ol>
<li><p>download the <code>rubymine-xx.tar.gz</code></p>
</li>
<li><p>Extract it and place it under your preferred place, I am placing it under <code>~/.local/.share</code> directory.</p>
</li>
<li><p>And now create a <code>rubymine.desktop</code> file inside <code>~/.local/share/applications</code></p>
</li>
</ol>
<pre><code class="lang-bash">touch ~/.<span class="hljs-built_in">local</span>/share/applications/rubymine.desktop
</code></pre>
<ol start="4">
<li>Now we edit this file with following content</li>
</ol>
<pre><code class="lang-bash">[Desktop Entry]
Name=RubyMine
Comment=RubyMine IDE
Exec=~/.<span class="hljs-built_in">local</span>/share/rubymine/bin/rubymine.sh
Terminal=<span class="hljs-literal">false</span>
Type=Application
Icon=~/.<span class="hljs-built_in">local</span>/share/rubymine/RMlogo.svg
</code></pre>
<p>That's it, you should now be able to see your RubyMine application listed in your "Show Applications" list. If it is not, try restarting your PC and it should be there.</p>
]]></content:encoded></item><item><title><![CDATA[Test-Driven Development (TDD), Building 'Hello World' in Go]]></title><description><![CDATA[What is Test-Driven Development?
Test-Driven Development (TDD) is a software development methodology that focuses on writing tests before writing the actual code. In TDD, you start by creating a test case that defines the expected behavior of a parti...]]></description><link>https://blog.przbadu.dev/test-driven-development-tdd-building-hello-world-in-go</link><guid isPermaLink="true">https://blog.przbadu.dev/test-driven-development-tdd-building-hello-world-in-go</guid><category><![CDATA[TDD (Test-driven development)]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Thu, 04 Jan 2024 05:59:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704347886368/c09406be-ecc8-40ab-b8eb-18f60b3f1064.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-test-driven-development">What is Test-Driven Development?</h2>
<p>Test-Driven Development (TDD) is a software development methodology that focuses on writing tests before writing the actual code. In TDD, you start by creating a test case that defines the expected behavior of a particular piece of code. Then, you write the code to make the test pass. TDD follows a cycle known as the "Red-Green-Refactor" cycle:</p>
<ol>
<li><p><strong>Red:</strong> You begin by writing a failing test case (the "Red" phase) that specifies the desired functionality.</p>
</li>
<li><p><strong>Green:</strong> You then write the minimal code required to make the test pass (the "Green" phase). This ensures that the code meets the specified requirements.</p>
</li>
<li><p><strong>Refactor:</strong> After the test passes, you can refactor the code to improve its quality while keeping the test passing. This maintains the code's correctness as you make changes.</p>
</li>
</ol>
<p>Before diving into our inaugural TDD Go application, it's crucial to emphasize another vital facet of TDD: setting up source control, such as Git. Git plays a pivotal role by allowing you to commit the latest functional iteration of your TDD process. This safeguards your progress, enabling you to revert to a previous version and embark on a fresh refactoring journey with confidence.</p>
<h2 id="heading-lets-start-tdd-with-our-hello-world-program-again">Lets start TDD with our hello world program again</h2>
<p><strong>Prerequisite:</strong></p>
<ul>
<li><p>Create new project folder e.g: <code>hello-world</code></p>
</li>
<li><p>Create 2 files inside it <code>hello_test.go</code> and <code>hello.go</code>, its Ok if the contents are empty</p>
</li>
<li><p>Generate go module with <code>go mod init hello</code>.</p>
</li>
</ul>
<p>And you should be ready to follow along.</p>
<h3 id="heading-red">RED</h3>
<p>Open your <code>hello_test.go</code> file in your editor/IDE and write a test case first:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestHello</span><span class="hljs-params">(t *testing.T)</span></span> {
    t.Run(<span class="hljs-string">"Say hello to John Doe"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">"John Doe"</span>)
        expected := <span class="hljs-string">"Hello, John Doe"</span>

        <span class="hljs-keyword">if</span> got != expected {
            t.Errorf(<span class="hljs-string">"expected %q but got %q"</span>, expected, got)
        }
    })
}
</code></pre>
<p>Run the <code>go test -v</code> and see it failing</p>
<pre><code class="lang-bash"> go <span class="hljs-built_in">test</span> -v                                                                              ─╯
<span class="hljs-comment"># hello [hello.test]</span>
./hello_test.go:7:10: undefined: Hello
FAIL    hello [build failed]
</code></pre>
<h3 id="heading-green">GREEN</h3>
<p>Open your <code>hello.go</code> file in editor and create a <code>Hello</code> function because our test case is complaining <code>Hello</code> is undefined.</p>
<pre><code class="lang-go"><span class="hljs-comment">// hello.go</span>

<span class="hljs-keyword">package</span> main

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
}
</code></pre>
<p>Run the test again and see another failure.</p>
<pre><code class="lang-go"><span class="hljs-keyword">go</span> test -v                                                                              ─╯
# hello [hello.test]
./hello.<span class="hljs-keyword">go</span>:<span class="hljs-number">4</span>:<span class="hljs-number">1</span>: missing <span class="hljs-keyword">return</span>
./hello_test.<span class="hljs-keyword">go</span>:<span class="hljs-number">7</span>:<span class="hljs-number">16</span>: too many arguments in call to Hello
        have (<span class="hljs-keyword">string</span>)
        want ()
FAIL    hello [build failed]
</code></pre>
<p>Running your tests at each step is essential to ensure that your test cases do not unexpectedly pass when they shouldn't.</p>
<p>Lets write enough code to make this test <strong>GREEN</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// hello.go</span>

<span class="hljs-keyword">package</span> main

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello, "</span> + name
}
</code></pre>
<p>Run your test case again and it should pass this time.</p>
<pre><code class="lang-basic"> go test -v                                                                              ─╯
=== <span class="hljs-keyword">RUN</span>   TestHello
=== <span class="hljs-keyword">RUN</span>   TestHello/Say_hello_to_John_Doe
--- PASS: TestHello (<span class="hljs-number">0.00</span>s)
    --- PASS: TestHello/Say_hello_to_John_Doe (<span class="hljs-number">0.00</span>s)
PASS
ok      hello   <span class="hljs-number">1.417</span>s
</code></pre>
<p><strong>Source Control</strong></p>
<blockquote>
<p>💡At this point because we have workable version of the code, lets commit our changes to git <code>git commit -am "Function to say hello to John Doe"</code></p>
</blockquote>
<h3 id="heading-refactor">REFACTOR</h3>
<p>Now that our test case is passing, let's refactor our code.</p>
<pre><code class="lang-go"><span class="hljs-comment">// hello.go</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">const</span> englishHelloPrefix = <span class="hljs-string">"Hello, "</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> englishHelloPrefix + name
}
</code></pre>
<p>Run the test again <code>go test</code> and everything should still pass. It might seem silly to make that change right now, trust me we need it.</p>
<p>In this step, we've extracted the <code>"Hello, "</code> message into a constant. It's worth considering the use of constants to convey the meaning of values and, in some cases, to optimize performance.</p>
<blockquote>
<p>💡As a final step, because now we have all steps satisfied RED, GREEN, REFACTOR, it is time to commit your changes to git and also push it to the git repository.</p>
</blockquote>
<h3 id="heading-continue-to-add-features-with-tdd-in-mind">Continue to add features with TDD in mind</h3>
<p><strong>Requirement:</strong> if <code>name</code> is <code>""</code> empty string, return <code>Hello, world</code> instead.</p>
<p><strong>RED</strong></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestHello</span><span class="hljs-params">(t *testing.T)</span></span> {
    t.Run(<span class="hljs-string">"Say hello to John Doe"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">"John Doe"</span>)
        expected := <span class="hljs-string">"Hello, John Doe"</span>

        <span class="hljs-keyword">if</span> got != expected {
            t.Errorf(<span class="hljs-string">"expected %q but got %q"</span>, expected, got)
        }
    })

    t.Run(<span class="hljs-string">"say 'Hello, world' when an empty string is given"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">""</span>)
        expected := <span class="hljs-string">"Hello, world"</span>

        <span class="hljs-keyword">if</span> got != expected {
            t.Errorf(<span class="hljs-string">"expected %q but got %q"</span>, expected, got)
        }
    })
}
</code></pre>
<p>Run the test <code>go test</code> it should fail with this message <code>hello_test.go:20: expected "Hello, world" but got "Hello, "</code> .</p>
<p><strong>GREEN</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> englishHelloPrefix = <span class="hljs-string">"Hello, "</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> {
        name = <span class="hljs-string">"world"</span>
    }

    <span class="hljs-keyword">return</span> englishHelloPrefix + name
}
</code></pre>
<p>Run the test <code>go test</code> again and it should pass.</p>
<blockquote>
<p>💡commit your code to git</p>
</blockquote>
<p><strong>REFACTOR</strong></p>
<p>There is nothing to refactor in the actual program, but we can extract duplicate code from our test case to keep it DRY (Don't Repeat Yourself)</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestHello</span><span class="hljs-params">(t *testing.T)</span></span> {
    t.Run(<span class="hljs-string">"Say hello to John Doe"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">"John Doe"</span>)
        expected := <span class="hljs-string">"Hello, John Doe"</span>

        assert(t, expected, got)
    })

    t.Run(<span class="hljs-string">"say 'Hello, world' when an empty string is given"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">""</span>)
        expected := <span class="hljs-string">"Hello, world"</span>

        assert(t, expected, got)
    })
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">assert</span><span class="hljs-params">(t testing.TB, expected, got <span class="hljs-keyword">string</span>)</span></span> {
    t.Helper()

    <span class="hljs-keyword">if</span> expected != got {
        t.Errorf(<span class="hljs-string">"Expected %q but got %q"</span>, expected, got)
    }
}
</code></pre>
<p>Run your test again and it should still pass.</p>
<p>We have created <code>assert</code> function that expects:</p>
<ul>
<li><p><code>testing.TB</code> object as first argument, this will allow us to pass either <code>*testing.T</code> testing or <code>*testing.B</code> benchmarking.</p>
</li>
<li><p>we also said this is a helper function by calling <code>t.Helper()</code>. We did this because if the test case fail, it will show the exact line number where it failed from the actual test function rather then showing line number of the <code>assert()</code> function. You can change one of your test case with wrong assertion and see it for yourself by keeping and removing <code>t.Helper()</code> from <code>assert()</code> function.</p>
</li>
</ul>
<p>\&gt;💡commit your changes to git, and push your code to git repo</p>
<p>Enjoying so far? 😍lets keep moving forward.</p>
<p><strong>Requirement:</strong> our <code>Hello</code> function should support additional parameter to specify the language of the greeting. Example <code>Hola,</code> in Spanish, <code>Bonjour,</code> in French, and you can choose your own language, example <code>नमस्ते,</code> (Nameste) in Nepali.</p>
<p><strong>RED</strong></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestHello</span><span class="hljs-params">(t *testing.T)</span></span> {
    <span class="hljs-comment">// old code</span>

    t.Run(<span class="hljs-string">"greeting in Spanish"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">"John"</span>, <span class="hljs-string">"es"</span>)
        expected := <span class="hljs-string">"Hola, John"</span>

        assert(t, expected, got)
    })
}
</code></pre>
<p>Run the test and it should fail. BTW because go is Statically typed language, if your Editor/IDE is correctly configured, you should see compiler yelling at you at this point.</p>
<pre><code class="lang-bash">./hello_test.go:21:24: too many arguments <span class="hljs-keyword">in</span> call to Hello
        have (string, string)
        want (string)
FAIL    hello [build failed]
</code></pre>
<p><strong>GREEN</strong></p>
<p>Add <code>lang string</code> parameter to your <code>Hello()</code> function</p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> englishHelloPrefix = <span class="hljs-string">"Hello, "</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">(name, lang <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> {
        name = <span class="hljs-string">"world"</span>
    }

    <span class="hljs-keyword">return</span> englishHelloPrefix + name
}
</code></pre>
<p>Run the test</p>
<pre><code class="lang-go"><span class="hljs-keyword">go</span> test                                                                                 ─╯
# hello [hello.test]
./hello_test.<span class="hljs-keyword">go</span>:<span class="hljs-number">7</span>:<span class="hljs-number">16</span>: not enough arguments in call to Hello
        have (<span class="hljs-keyword">string</span>)
        want (<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>)
./hello_test.<span class="hljs-keyword">go</span>:<span class="hljs-number">14</span>:<span class="hljs-number">16</span>: not enough arguments in call to Hello
        have (<span class="hljs-keyword">string</span>)
        want (<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>)
FAIL    hello [build failed]
</code></pre>
<p>As you can see, by writing TDD you know exactly when your code breaks, lets fix our old tests</p>
<pre><code class="lang-go">    t.Run(<span class="hljs-string">"Say hello to John Doe"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">"John Doe"</span>, <span class="hljs-string">""</span>) <span class="hljs-comment">// added "" as second argument</span>
        expected := <span class="hljs-string">"Hello, John Doe"</span>

        assert(t, expected, got)
    })

    t.Run(<span class="hljs-string">"say 'Hello, world' when an empty string is given"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">""</span>, <span class="hljs-string">""</span>) <span class="hljs-comment">// added "" as second argument</span>
        expected := <span class="hljs-string">"Hello, world"</span>

        assert(t, expected, got)
    })

    t.Run(<span class="hljs-string">"greeting in Spanish"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">"John"</span>, <span class="hljs-string">"es"</span>)
        expected := <span class="hljs-string">"Hola, John"</span>

        assert(t, expected, got)
    })
</code></pre>
<p>Run the test again</p>
<pre><code class="lang-go"><span class="hljs-keyword">go</span> test                                                                                 ─╯
--- FAIL: TestHello (<span class="hljs-number">0.00</span>s)
    --- FAIL: TestHello/greeting_in_Spanish (<span class="hljs-number">0.00</span>s)
        hello_test.<span class="hljs-keyword">go</span>:<span class="hljs-number">24</span>: Expected <span class="hljs-string">"Hola, John"</span> but got <span class="hljs-string">"Hello, John"</span>
FAIL
exit status <span class="hljs-number">1</span>
FAIL    hello   <span class="hljs-number">0.804</span>s
</code></pre>
<p>Now you should see, we are expecting <code>Hola, John</code> but our function is returning <code>Hello, John</code>. Lets fix it.</p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> englishHelloPrefix = <span class="hljs-string">"Hello, "</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">(name, lang <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> {
        name = <span class="hljs-string">"world"</span>
    }

    <span class="hljs-keyword">if</span> lang == <span class="hljs-string">"es"</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hola, "</span> + name
    }

    <span class="hljs-keyword">return</span> englishHelloPrefix + name
}
</code></pre>
<p>Run the test and you should see everything passing, 🎉</p>
<blockquote>
<p>💡commit your changes to git</p>
</blockquote>
<p><strong>REFACTOR</strong></p>
<p>Let's extract Spanish greeting to it's own constant and also extract <code>lang</code> value to its own constant.</p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> (
    englishHelloPrefix = <span class="hljs-string">"Hello, "</span>
    spanishHelloPrefix = <span class="hljs-string">"Hola, "</span>
)

<span class="hljs-keyword">const</span> spanish = <span class="hljs-string">"es"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">(name, lang <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> {
        name = <span class="hljs-string">"world"</span>
    }

    <span class="hljs-keyword">if</span> lang == spanish {
        <span class="hljs-keyword">return</span> spanishHelloPrefix + name
    }

    <span class="hljs-keyword">return</span> englishHelloPrefix + name
}
</code></pre>
<p>Run the test again and everything should pass.</p>
<blockquote>
<p>💡 Commit your changes to git and push to git repo</p>
</blockquote>
<p><strong>Exercise: Lets add French language support</strong></p>
<p><strong>Solution: RED</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">/// old code</span>


    t.Run(<span class="hljs-string">"greeting in French"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        got := Hello(<span class="hljs-string">"John"</span>, <span class="hljs-string">"fr"</span>)
        expected := <span class="hljs-string">"Bonjour, John"</span>

        assert(t, expected, got)
    })
</code></pre>
<p>Run the test you should see <code>hello_test.go:31: Expected "Bonjour, John" but got "Hello, John"</code></p>
<p><strong>GREEN</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> (
    englishHelloPrefix = <span class="hljs-string">"Hello, "</span>
    spanishHelloPrefix = <span class="hljs-string">"Hola, "</span>
    frenchHelloPrefix  = <span class="hljs-string">"Bonjour, "</span>
)

<span class="hljs-keyword">const</span> (
    spanish = <span class="hljs-string">"es"</span>
    french  = <span class="hljs-string">"fr"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">(name, lang <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> {
        name = <span class="hljs-string">"world"</span>
    }

    <span class="hljs-keyword">if</span> lang == spanish {
        <span class="hljs-keyword">return</span> spanishHelloPrefix + name
    }

    <span class="hljs-keyword">if</span> lang == french {
        <span class="hljs-keyword">return</span> frenchHelloPrefix + name
    }

    <span class="hljs-keyword">return</span> englishHelloPrefix + name
}
</code></pre>
<p>Now run the test, it should pass again.</p>
<p><strong>REFACTOR</strong></p>
<p>Now lets use <code>switch</code> statement to replace our <code>if</code> statements</p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> (
    englishHelloPrefix = <span class="hljs-string">"Hello, "</span>
    spanishHelloPrefix = <span class="hljs-string">"Hola, "</span>
    frenchHelloPrefix  = <span class="hljs-string">"Bonjour, "</span>
)

<span class="hljs-keyword">const</span> (
    spanish = <span class="hljs-string">"es"</span>
    french  = <span class="hljs-string">"fr"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">(name, lang <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> {
        name = <span class="hljs-string">"world"</span>
    }

    prefix := englishHelloPrefix

    <span class="hljs-keyword">switch</span> lang {
    <span class="hljs-keyword">case</span> spanish:
        prefix = spanishHelloPrefix
    <span class="hljs-keyword">case</span> french:
        prefix = frenchHelloPrefix
    }

    <span class="hljs-keyword">return</span> prefix + name
}
</code></pre>
<p>And the test should still pass.</p>
<h2 id="heading-what-we-practiced-here">What we practiced here?</h2>
<ul>
<li><p>Write a test first and see it fail (RED)</p>
</li>
<li><p>Make the code and make compiler happy</p>
</li>
<li><p>Run the test case and see it pass (GREEN)</p>
</li>
<li><p>Commit your code to git at this point, so that further Refactor becomes seemless</p>
</li>
<li><p>Refactor.</p>
</li>
<li><p>Commit your code to git and push your changes to git repository.</p>
</li>
</ul>
<p>Hope you enjoyed this TDD approach.</p>
]]></content:encoded></item><item><title><![CDATA[Hello world in Go]]></title><description><![CDATA[Every programming language career starts by writing Hello World program. Lets do it in Go.

Create a new folder called hello-world

Create a new file in it called hello.go


mkdir hello-world
cd hello-world
touch hello.go


Now write following code i...]]></description><link>https://blog.przbadu.dev/hello-world-in-go</link><guid isPermaLink="true">https://blog.przbadu.dev/hello-world-in-go</guid><category><![CDATA[Go Language]]></category><category><![CDATA[TDD (Test-driven development)]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Thu, 04 Jan 2024 03:05:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704337477286/bc27453c-f7a8-4dcb-9275-f25942408b5a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every programming language career starts by writing <code>Hello World</code> program. Lets do it in <a target="_blank" href="https://go.dev/">Go</a>.</p>
<ul>
<li><p>Create a new folder called <code>hello-world</code></p>
</li>
<li><p>Create a new file in it called <code>hello.go</code></p>
</li>
</ul>
<pre><code class="lang-bash">mkdir hello-world
<span class="hljs-built_in">cd</span> hello-world
touch hello.go
</code></pre>
<ul>
<li>Now write following code in it</li>
</ul>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"Hello, world!"</span>)
}
</code></pre>
<p>Run the program with following shell script <code>go run hello.go</code> .</p>
<h2 id="heading-writing-your-first-test">Writing your first test</h2>
<p>Go comes with built in test setup, this is a big deal if you are coming from other programming language where you need to setup your test, choose from different testing frameworks.</p>
<p>But how do we test the above code? Before writing a test case it is a good practice to separate your <code>domain</code> logic from the outside world (side effects). So lets do that first.</p>
<pre><code class="lang-go"><span class="hljs-comment">// main.go</span>

<span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Hello</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello, world!"</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    fmt.Println(Hello())
}
</code></pre>
<p><code>func</code> keyword is used to create a function in Go. we named our function <code>Hello()</code> and we said this function should return <code>string</code>. Because Go is strongly typed language, this will helps us catch errors in compile time and our editor will help us fix them before even executing the program.</p>
<p>Lets create another file called <code>hello_test.go</code> and start writing our first test for our <code>Hello</code> function.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestHello</span><span class="hljs-params">(t *testing.T)</span></span> {
    got := Hello()
    expected := <span class="hljs-string">"Hello, world!"</span>

    <span class="hljs-keyword">if</span> got != expected {
        t.Errorf(<span class="hljs-string">"expected %q got %q"</span>, expected, got)
    }
}
</code></pre>
<h3 id="heading-few-conventions-in-writing-tests">Few conventions in writing tests</h3>
<ul>
<li><p>Test file name should <code>xxx_test.go</code> in our case <code>hello_test.go</code></p>
</li>
<li><p>The test function must start with the word <code>Test</code> in our case <code>TestHello</code></p>
</li>
<li><p>The test function takes one argument only <code>t *testing.T</code></p>
</li>
<li><p>In order to use the <code>*testing.T</code> type, you need to import <code>import "testing"</code>.</p>
</li>
</ul>
<p>Please note that, In the <a target="_blank" href="https://blog.przbadu.dev/install-go">Go Installation and IDE Setup</a> section, you'll find detailed setup instructions for both Visual Studio Code and Neovim. Once your IDE is configured correctly, it can efficiently handle the automatic import of necessary modules, simplifying your development process.</p>
<h2 id="heading-go-modules">Go modules</h2>
<p>Now that our test is ready you can run your test by typing <code>go test</code> in your terminal</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
go: go.mod file not found <span class="hljs-keyword">in</span> current directory or any parent directory; see <span class="hljs-string">'go help modules'</span>
</code></pre>
<p>If you see <code>go.mod</code> related problem, that means you are running go 1.16 or later, Go has a new module changes, you can read more about it <a target="_blank" href="https://go.dev/blog/go116-module-changes">Go Modules</a>.</p>
<p>All you need to type is <code>go mod init hello</code> in your terminal followed by <code>go mod tidy</code> to fix this problem, and it will generate a <code>go.mod</code> file in your project.</p>
<pre><code class="lang-bash">$ go mod init hello      
go: creating new go.mod: module hello
go: to add module requirements and sums:
        go mod tidy
$ go mod tidy
</code></pre>
<p>If you are interested in what is generated in <code>go.mod</code> here is the content:</p>
<pre><code class="lang-go">module hello

<span class="hljs-keyword">go</span> <span class="hljs-number">1.21</span><span class="hljs-number">.5</span>
</code></pre>
<p>Now run <code>go test</code> command again and you will see your test is passing</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>    
PASS
ok      hello   0.001s
</code></pre>
<p>Next we will try to follow TDD approach to improve this hello world program.</p>
]]></content:encoded></item><item><title><![CDATA[Go Installation and IDE Setup]]></title><description><![CDATA[Using Go Playground
To learn the basics of go language you don't need to Install Go in your computer, you can start with Go Playground - https://go.dev/play/.
Install Go
If you prefer to install go programming language in our computer click the link ...]]></description><link>https://blog.przbadu.dev/install-go</link><guid isPermaLink="true">https://blog.przbadu.dev/install-go</guid><category><![CDATA[Go Language]]></category><category><![CDATA[vim]]></category><category><![CDATA[neovim]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Wed, 03 Jan 2024 02:50:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704249937812/d3d05346-61b0-4786-b7d8-5ea33b88fb44.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-using-go-playground">Using Go Playground</h2>
<p>To learn the basics of go language you don't need to Install Go in your computer, you can start with <a target="_blank" href="https://go.dev/play/">Go Playground</a> - <a target="_blank" href="https://go.dev/play/">https://go.dev/play/</a>.</p>
<h2 id="heading-install-go">Install Go</h2>
<p>If you prefer to install go programming language in our computer click the link <a target="_blank" href="https://go.dev/doc/install">https://go.dev/doc/install</a>, download and install go for your preferred OS.</p>
<h2 id="heading-editor-of-your-choice">Editor of your choice</h2>
<ul>
<li><p><a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> with <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=golang.Go">Go Extension</a></p>
</li>
<li><p><a target="_blank" href="https://neovim.io/">Neovim</a> with <a target="_blank" href="https://www.lazyvim.org/">lazyvim</a> setup, here is how you can enable Go extras from lazyvim. If you want to learn more about this setup visit <a target="_blank" href="https://www.lazyvim.org/extras/lang/go">https://www.lazyvim.org/extras/lang/go</a></p>
<ul>
<li><p>Open <code>nvim</code> and type <code>:LazyExtras</code></p>
<ul>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704249058687/7153adfc-6b67-46e8-9fb9-c3652a3f6346.png" alt class="image--center mx-auto" /></p>
<p>  Go to <strong>Disabled</strong> section and find <code>lang.go</code> language, e.g <code>/lang.go</code></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>        <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704249175435/76c64402-893e-49fe-bee7-bea134dbae55.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Press <code>x</code> from your keyboard to enable the go language plugin</li>
</ul>
<p>        <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704249241416/78063421-4b91-4d5c-b61a-66fbbd37fcfd.png" alt /></p>
<p>And you are all set, now you are ready to write your go programs.</p>
]]></content:encoded></item><item><title><![CDATA[Using Mocha to stub external APIs with minitest.]]></title><description><![CDATA[Recently at ProcurementExpress.com, I had to integrate Digital invoice-matching functionality, we decided to use Mindee, Which has an accurate and lightning-fast document parsing API.
Here is the implementation we have done with Ruby on Rails. One sm...]]></description><link>https://blog.przbadu.dev/using-mocha-to-stub-external-apis-with-minitest</link><guid isPermaLink="true">https://blog.przbadu.dev/using-mocha-to-stub-external-apis-with-minitest</guid><category><![CDATA[unit testing]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[APIs]]></category><category><![CDATA[mocha]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Fri, 07 Jul 2023 07:31:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1688715015973/9736d0a6-ee95-4321-8260-9c508ff351ad.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently at ProcurementExpress.com, I had to integrate Digital invoice-matching functionality, we decided to use <a target="_blank" href="https://mindee.com">Mindee</a>, Which has an accurate and lightning-fast document parsing API.</p>
<p>Here is the implementation we have done with Ruby on Rails. One small note, they already have <a target="_blank" href="https://github.com/mindee/mindee-api-ruby">https://github.com/mindee/mindee-api-ruby</a> SDK for ruby on rails, but because our ruby version was not supported by SDK, I had to write a wrapper for it.</p>
<p>Here is a Ruby class I created for Mindee Document-based prediction API.</p>
<p>InvoiceFields is a Concern that extracts different required fields from Mindee's response.</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># app/services/integrations/mindee/invoice_fields.rb</span>

<span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Integrations</span></span>
  <span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Mindee</span></span>
    <span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">InvoiceFields</span></span>
      extend ActiveSupport::Concern

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">date</span></span>
        value_of(<span class="hljs-symbol">:date</span>)
      <span class="hljs-keyword">end</span>

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">due_date</span></span>
        value_of(<span class="hljs-symbol">:due_date</span>)
      <span class="hljs-keyword">end</span>

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">invoice_number</span></span>
        value_of(<span class="hljs-symbol">:invoice_number</span>)
      <span class="hljs-keyword">end</span>

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">supplier_name</span></span>
        value_of(<span class="hljs-symbol">:supplier_name</span>)
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>ConfidenceScore is another concern that returns the Average confidence score from line items.</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># app/services/integrations/mindee/confidence_score.rb</span>

<span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Integrations</span></span>
  <span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Mindee</span></span>
    <span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">ConfidenceScore</span></span>
      extend ActiveSupport::Concern

      <span class="hljs-comment"># Confidence score of the average of the confidence scores of the line items</span>
      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">confidence_score</span></span>
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> line_items.count.zero?

        sum_confidence = line_items.map { <span class="hljs-params">|item|</span> item[<span class="hljs-symbol">:confidence</span>] }.sum
        sum_confidence / line_items.length
      <span class="hljs-keyword">rescue</span> StandardError =&gt; e
        puts <span class="hljs-string">"Error calculating confidence score: <span class="hljs-subst">#{e.message}</span>"</span>
        <span class="hljs-number">0</span>
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>We are using <a target="_blank" href="https://github.com/jnunemaker/httparty">HTTParty</a> to make API calls, and here is the Mindee client wrapper.</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># app/services/integrations/mindee/client.rb</span>

<span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Integrations</span></span>
  <span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Mindee</span></span>
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Client</span></span>
      <span class="hljs-keyword">include</span> HTTParty
      base_uri <span class="hljs-string">'https://api.mindee.net/v1'</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>And finally, here is the <code>predict_document.rb</code> service class that makes an actual call to the API service.</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># app/services/integrations/mindee/predict_document.rb</span>

<span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Integrations</span></span>
  <span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Mindee</span></span>
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PredictDocument</span></span>
      <span class="hljs-comment"># concerns</span>
      <span class="hljs-keyword">include</span> Integrations::Mindee::InvoiceFields
      <span class="hljs-keyword">include</span> Integrations::Mindee::ConfidenceScore

      <span class="hljs-comment"># attributes</span>
      <span class="hljs-keyword">attr_reader</span> <span class="hljs-symbol">:file</span>, <span class="hljs-symbol">:error</span>, <span class="hljs-symbol">:success</span>, <span class="hljs-symbol">:response</span>

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">initialize</span><span class="hljs-params">(<span class="hljs-symbol">file:</span>)</span></span>
        @file = file
        @error = <span class="hljs-literal">nil</span>
        @success = <span class="hljs-literal">nil</span>
        @token = ENV.fetch(<span class="hljs-string">'MINDEE_API_KEY'</span>)
      <span class="hljs-keyword">end</span>

      <span class="hljs-comment"># Extract the invoice data from the file using the Mindee API</span>
      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">call</span></span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> file.present?

        response = client.post(
          <span class="hljs-string">'/products/mindee/financial_document/v1/predict'</span>,
          <span class="hljs-symbol">body:</span> { <span class="hljs-symbol">document:</span> file },
          <span class="hljs-symbol">headers:</span> {
            <span class="hljs-symbol">Authorization:</span> <span class="hljs-string">"Token <span class="hljs-subst">#{@token}</span>"</span>
          }
        )
        body = JSON.parse(response.body).with_indifferent_access

        @error =  !response.success?
        @success = response.success?
        @response = body
      <span class="hljs-keyword">end</span>

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">line_items</span></span>
        prediction[<span class="hljs-symbol">:line_items</span>] <span class="hljs-params">||</span> []
      <span class="hljs-keyword">end</span>

      private

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">client</span></span>
        @client <span class="hljs-params">||</span>= Integrations::Mindee::Client
      <span class="hljs-keyword">end</span>

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">prediction</span></span>
        @response.dig(<span class="hljs-symbol">:document</span>, <span class="hljs-symbol">:inference</span>, <span class="hljs-symbol">:prediction</span>) <span class="hljs-params">||</span> {}
      <span class="hljs-keyword">end</span>

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">value_of</span><span class="hljs-params">(key)</span></span>
        pred = prediction[key]

        <span class="hljs-keyword">if</span> pred&amp;.is_a?(Array)
          pred.first[<span class="hljs-symbol">:value</span>]
        <span class="hljs-keyword">elsif</span> pred&amp;.is_a?(Hash)
          pred[<span class="hljs-symbol">:value</span>]
        <span class="hljs-keyword">end</span>
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h1 id="heading-how-to-test-the-given-service-class-using-mocha">How to Test the given service class using Mocha?</h1>
<pre><code class="lang-ruby">
<span class="hljs-comment"># Gemfile</span>

group <span class="hljs-symbol">:test</span> <span class="hljs-keyword">do</span>
  gem <span class="hljs-string">'mocha'</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Generally, I like to make one valid API call to the service and put the actual response in the JSON file inside the test fixture, so that we can re-use that JSON in different test cases.</p>
<blockquote>
<p>NOTE: Below JSON file does not contains valid JSON response</p>
</blockquote>
<pre><code class="lang-ruby"><span class="hljs-comment"># test/fixtures/mindee_response.json</span>

{
  <span class="hljs-string">"api_request"</span>: {
    ... 
  },
  <span class="hljs-string">"document"</span>: {
    <span class="hljs-string">"inference"</span>: {
      <span class="hljs-string">"extras"</span>: {},
      <span class="hljs-string">"finished_at"</span>: <span class="hljs-string">"2023-07-04T09:56:03.893829"</span>,
      <span class="hljs-string">"is_rotation_applied"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-string">"pages"</span>: [
        {
         ...   
        <span class="hljs-string">"line_items"</span>: [
          {
            <span class="hljs-string">"confidence"</span>: <span class="hljs-number">0</span>.<span class="hljs-number">99</span>,
            <span class="hljs-string">"description"</span>: <span class="hljs-string">"test"</span>,
            <span class="hljs-string">"page_id"</span>: <span class="hljs-number">0</span>,
            <span class="hljs-string">"polygon"</span>: [],
            <span class="hljs-string">"product_code"</span>: <span class="hljs-string">"1"</span>,
            <span class="hljs-string">"quantity"</span>: <span class="hljs-number">1.0</span>,
            <span class="hljs-string">"tax_amount"</span>: null,
            <span class="hljs-string">"tax_rate"</span>: <span class="hljs-number">0</span>.<span class="hljs-number">0</span>,
            <span class="hljs-string">"total_amount"</span>: <span class="hljs-number">12.0</span>,
            <span class="hljs-string">"unit_price"</span>: <span class="hljs-number">12.0</span>
          }
        ],
        <span class="hljs-string">"locale"</span>: {
          <span class="hljs-string">"confidence"</span>: <span class="hljs-number">0</span>.<span class="hljs-number">71</span>,
          <span class="hljs-string">"currency"</span>: <span class="hljs-string">"EUR"</span>,
          <span class="hljs-string">"language"</span>: <span class="hljs-string">"en"</span>
        },
        <span class="hljs-string">"reference_numbers"</span>: [
          {
            <span class="hljs-string">"confidence"</span>: <span class="hljs-number">1.0</span>,
            <span class="hljs-string">"page_id"</span>: <span class="hljs-number">0</span>,
            <span class="hljs-string">"polygon"</span>: [],
            <span class="hljs-string">"value"</span>: <span class="hljs-string">"#2534212-2023-07-03"</span>
          }
        ],
        ...
       <span class="hljs-string">"started_at"</span>: <span class="hljs-string">"2023-07-04T09:56:02.755287"</span>
    },
    <span class="hljs-string">"n_pages"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"test.pdf"</span>
  }
}
</code></pre>
<p>And finally, here is the mini test example:</p>
<pre><code class="lang-ruby"><span class="hljs-keyword">require</span> <span class="hljs-string">'test_helper'</span>

<span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Integrations</span></span>
  <span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">Mindee</span></span>
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PredictDocumentTest</span> &lt; ActiveSupport::TestCase</span>
      setup <span class="hljs-keyword">do</span>
        @response = File.read(<span class="hljs-string">'test/files/mindee_response.json'</span>)
        @subject = Integrations::Mindee::PredictDocument
      <span class="hljs-keyword">end</span>

      test <span class="hljs-string">'should return error if response is not success'</span> <span class="hljs-keyword">do</span>
        Integrations::Mindee::Client.stubs(<span class="hljs-symbol">:post</span>).returns(
          OpenStruct.new(<span class="hljs-symbol">success?:</span> <span class="hljs-literal">false</span>, <span class="hljs-symbol">body:</span> { <span class="hljs-symbol">response:</span> { <span class="hljs-symbol">api_request:</span> { <span class="hljs-symbol">error:</span> <span class="hljs-string">'something went wrong'</span> } } }.to_json)
        )
        service = @subject.new(<span class="hljs-symbol">file:</span> <span class="hljs-string">'file'</span>)
        service.call
        assert service.error
      <span class="hljs-keyword">end</span>

      test <span class="hljs-string">'should return success if response is success'</span> <span class="hljs-keyword">do</span>
        assert stub_and_call_service.success
      <span class="hljs-keyword">end</span>

      test <span class="hljs-string">'should return the confidence score'</span> <span class="hljs-keyword">do</span>
        assert_equal <span class="hljs-number">0</span>.<span class="hljs-number">99</span>, stub_and_call_service.confidence_score
      <span class="hljs-keyword">end</span>

      test <span class="hljs-string">'should return the invoice date'</span> <span class="hljs-keyword">do</span>
        assert_equal <span class="hljs-string">'2023-07-03'</span>, stub_and_call_service.date
      <span class="hljs-keyword">end</span>

      test <span class="hljs-string">'should return invoice items'</span> <span class="hljs-keyword">do</span>
        items = stub_and_call_service.line_items
        assert_equal <span class="hljs-number">1</span>, items.size
        assert_equal <span class="hljs-string">'test'</span>, items.first[<span class="hljs-symbol">:description</span>]
        assert_equal <span class="hljs-number">1</span>, items.first[<span class="hljs-symbol">:quantity</span>]
        assert_equal <span class="hljs-number">0</span>, items.first[<span class="hljs-symbol">:tax_rate</span>]
        assert_equal <span class="hljs-number">12</span>, items.first[<span class="hljs-symbol">:unit_price</span>]
        assert_equal <span class="hljs-number">12</span>, items.first[<span class="hljs-symbol">:total_amount</span>]
      <span class="hljs-keyword">end</span>

      ...
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stub_and_call_service</span></span>
  Integrations::Mindee::Client
    .stubs(<span class="hljs-symbol">:post</span>)
    .returns(OpenStruct.new(<span class="hljs-symbol">success?:</span> <span class="hljs-literal">true</span>, <span class="hljs-symbol">body:</span> @response))
  service = @subject.new(<span class="hljs-symbol">file:</span> <span class="hljs-string">'file'</span>)
  service.call
  service
<span class="hljs-keyword">end</span>
</code></pre>
<p>We can use <code>stubs</code> method to stub the <code>post</code> action from HTTParty and return our custom success or failed response. Also note that, we are directly stubbing <code>Integrations::Mindee::Client</code> service class and not the <code>Integrations::Mindee::PredictDocument</code>. That way when we call <code>.call</code> method it will stub the response and still allow us to test rest of the logic from the <code>.call</code> method, and that means, we can test both <code>success</code> and <code>failure</code> response logic.</p>
<p><strong>Return Error response.</strong></p>
<pre><code class="lang-ruby">Integrations::Mindee::Client.stubs(<span class="hljs-symbol">:post</span>).returns(
  OpenStruct.new(<span class="hljs-symbol">success?:</span> <span class="hljs-literal">false</span>, <span class="hljs-symbol">body:</span> { <span class="hljs-symbol">response:</span> { <span class="hljs-symbol">api_request:</span> { <span class="hljs-symbol">error:</span> <span class="hljs-string">'something went wrong'</span> } } }.to_json)
)
</code></pre>
<p><strong>Return Success response</strong></p>
<pre><code class="lang-ruby">Integrations::Mindee::Client.stubs(<span class="hljs-symbol">:post</span>).returns(OpenStruct.new(<span class="hljs-symbol">success?:</span> <span class="hljs-literal">true</span>, <span class="hljs-symbol">body:</span> @response))
service = @subject.new(<span class="hljs-symbol">file:</span> <span class="hljs-string">'file'</span>)
</code></pre>
<p>Through this blog post, we have explored the key features of Mocha and demonstrated how it can be integrated with minitest to create robust test suites. We discussed the process of mocking and stubbing external APIs, enabling developers to isolate their code and test it in isolation from external factors. This approach not only enhances the reliability of tests but also accelerates development cycles by reducing the reliance on external services during the testing phase.</p>
]]></content:encoded></item><item><title><![CDATA[Make your Tmux beautiful]]></title><description><![CDATA[Install Tmux
Follow https://github.com/tmux/tmux/wiki to install Tmux in your preferred OS.
Mac os users:
brew install tmux

Ubuntu users:
sudo apt update && sudo apt -y install tmux

Start using tmux
Create a new Tmux session and we will customize i...]]></description><link>https://blog.przbadu.dev/make-your-tmux-beautiful</link><guid isPermaLink="true">https://blog.przbadu.dev/make-your-tmux-beautiful</guid><category><![CDATA[tmux]]></category><category><![CDATA[shell]]></category><category><![CDATA[vim]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Mon, 03 Jul 2023 04:19:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1688358235097/e140d3ff-8b88-4adf-bb29-50a0eb8add5b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-install-tmux">Install Tmux</h2>
<p>Follow <a target="_blank" href="https://github.com/tmux/tmux/wiki">https://github.com/tmux/tmux/wiki</a> to install Tmux in your preferred OS.</p>
<p><strong>Mac os users:</strong></p>
<pre><code class="lang-bash">brew install tmux
</code></pre>
<p><strong>Ubuntu users:</strong></p>
<pre><code class="lang-bash">sudo apt update &amp;&amp; sudo apt -y install tmux
</code></pre>
<h2 id="heading-start-using-tmux">Start using tmux</h2>
<p>Create a new Tmux session and we will customize it.</p>
<pre><code class="lang-bash">tmux new -S &lt;your session name&gt;

<span class="hljs-comment"># example:</span>
tmux new -S tmux-conf
</code></pre>
<h2 id="heading-create-a-tmux-configuration-file">Create a Tmux configuration file</h2>
<p>Create a tmux config file in your root directory <code>~/.tmux.conf</code> and start adding the following scripts to customize the default Tmux UI and behaviors.</p>
<p><code>vim ~/.tmux.conf</code></p>
<pre><code class="lang-bash"><span class="hljs-comment"># ~/.tmux.conf</span>

unbind -r
<span class="hljs-built_in">bind</span> r source-file ~/.tmux.conf

<span class="hljs-built_in">set</span> -g prefix C-b
</code></pre>
<p>Here we are telling Tmux set <code>r</code> to reload the Tmux config and apply the changes to the current tmux session. And also we are setting <code>C-b</code> as a prefix key.</p>
<p>So if you do <code>C-b</code> + <code>r</code> your Tmux session should reload and new changes will get applied</p>
<p><strong>Next we will set Vim keybindings to switch between panes</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># act like vim</span>
setw -g mode-keys vi
bind-key h select-pane -L
bind-key j select-pane -D
bind-key k select-pane -U
bind-key l select-pane -R
</code></pre>
<h2 id="heading-install-tmux-plugin-manager">Install Tmux Plugin Manager</h2>
<p>Next, lets install Tmux plugin manager <a target="_blank" href="https://github.com/tmux-plugins/tpm">https://github.com/tmux-plugins/tpm</a>.</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
</code></pre>
<p>Put this at the bottom of <code>~/.tmux.conf</code></p>
<pre><code class="lang-bash"><span class="hljs-comment"># ...</span>

<span class="hljs-comment"># List of plugins</span>
<span class="hljs-built_in">set</span> -g @plugin <span class="hljs-string">'tmux-plugins/tpm'</span>
<span class="hljs-comment"># Navigate tmux panes with `C-hjkl` just like vim.</span>
<span class="hljs-built_in">set</span> -g @plugin <span class="hljs-string">'christoomey/vim-tmux-navigator'</span>
<span class="hljs-built_in">set</span> -g @plugin <span class="hljs-string">'dracula/tmux'</span>

<span class="hljs-built_in">set</span> -g @dracula-show-powerline <span class="hljs-literal">true</span>
<span class="hljs-built_in">set</span> -g @dracula-fixed-location <span class="hljs-string">"&lt;Your city&gt;"</span>
<span class="hljs-built_in">set</span> -g @dracula-show-fahrenheit <span class="hljs-literal">false</span>
<span class="hljs-built_in">set</span> -g @dracula-plugins <span class="hljs-string">"weather"</span>
<span class="hljs-built_in">set</span> -g @dracula-show-flags <span class="hljs-literal">true</span>
<span class="hljs-built_in">set</span> -g @dracula-show-left-icon session
<span class="hljs-built_in">set</span> -g @dracula-show-empty-plugins <span class="hljs-literal">false</span>
<span class="hljs-built_in">set</span> -g status-position top

<span class="hljs-comment"># Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)</span>
run <span class="hljs-string">'~/.tmux/plugins/tpm/tpm'</span>
</code></pre>
<p>Run <code>C-b + I</code> to install all the tmux plugins from your config file anytime you add new plugins</p>
<h2 id="heading-my-complete-tmuxconf">My complete <code>.tmux.conf</code></h2>
<pre><code class="lang-bash"><span class="hljs-comment">###########################</span>
<span class="hljs-comment">#  Configuration</span>
<span class="hljs-comment">###########################</span>

<span class="hljs-comment"># use 256 term for pretty colors</span>
<span class="hljs-comment"># set -g default-terminal "screen-256color" # making tmux slow</span>
<span class="hljs-built_in">set</span> -g default-terminal <span class="hljs-string">"xterm-256color"</span>

<span class="hljs-comment"># Enable mouse support</span>
<span class="hljs-built_in">set</span> -g mouse on

<span class="hljs-comment"># increase scroll-back history</span>
<span class="hljs-built_in">set</span> -g history-limit 5000

<span class="hljs-comment"># use vim key bindings</span>
setw -g mode-keys vi

<span class="hljs-comment"># decrease command delay (increases vim responsiveness)</span>
<span class="hljs-built_in">set</span> -sg escape-time 1

<span class="hljs-comment"># increase repeat time for repeatable commands</span>
<span class="hljs-built_in">set</span> -g repeat-time 1000

<span class="hljs-comment"># start window index at 1</span>
<span class="hljs-built_in">set</span> -g base-index 1

<span class="hljs-comment"># start pane index at 1</span>
setw -g pane-base-index 1

<span class="hljs-comment"># highlight window when it has new activity</span>
setw -g monitor-activity on
<span class="hljs-built_in">set</span> -g visual-activity on

<span class="hljs-comment"># re-number windows when one is closed</span>
<span class="hljs-built_in">set</span> -g renumber-windows on

<span class="hljs-comment"># Fix nightfly color scheme to display correctly inside tmux</span>
<span class="hljs-built_in">set</span> -ga terminal-overrides <span class="hljs-string">',xterm-256color:Tc'</span>

<span class="hljs-comment"># enable pbcopy and pbpaste</span>
<span class="hljs-comment"># https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard/blob/master/README.md</span>
<span class="hljs-comment"># set-option -g default-command "reattach-to-user-namespace -l zsh"</span>

<span class="hljs-comment">###########################</span>
<span class="hljs-comment">#  Key Bindings</span>
<span class="hljs-comment">###########################</span>

<span class="hljs-comment"># tmux prefix</span>
<span class="hljs-built_in">set</span> -g prefix C-b

<span class="hljs-comment"># paste</span>
unbind C-p
<span class="hljs-built_in">bind</span> C-p paste-buffer

<span class="hljs-comment"># window splitting</span>
unbind %
<span class="hljs-built_in">bind</span> | split-window -h
unbind <span class="hljs-string">'"'</span>
<span class="hljs-built_in">bind</span> - split-window -v -p 20

<span class="hljs-comment"># quickly switch panes</span>
unbind ^B
<span class="hljs-built_in">bind</span> ^B select-pane -t :.+

<span class="hljs-comment"># switch panes using Alt-arrow without prefix</span>
<span class="hljs-built_in">bind</span> -n M-h select-pane -L
<span class="hljs-built_in">bind</span> -n M<span class="hljs-_">-l</span> select-pane -R
<span class="hljs-built_in">bind</span> -n M-k select-pane -U
<span class="hljs-built_in">bind</span> -n M-j select-pane -D

<span class="hljs-comment"># swap windows</span>
bind-key -n C-S-Left swap-window -t -1
bind-key -n C-S-Right swap-window -t +1

<span class="hljs-comment"># Quickly view system &amp; process info in htop</span>
bind-key h split-window -h <span class="hljs-string">"htop"</span>
<span class="hljs-comment"># Quickly edit todo list</span>
bind-key w split-window -h <span class="hljs-string">"vim ~/vimwiki/index.md"</span>

<span class="hljs-comment"># rails specific</span>
bind-key -n M<span class="hljs-_">-s</span> split-window -h <span class="hljs-string">"bundle exec sidekiq"</span>
bind-key -n M-r split-window -v -p 20 <span class="hljs-string">"redis-server"</span>

<span class="hljs-comment"># force a reload of the config file</span>
unbind r
<span class="hljs-built_in">bind</span> r source-file ~/.tmux.conf \; display <span class="hljs-string">"Reloaded!"</span>

<span class="hljs-comment">###########################</span>
<span class="hljs-comment"># Status Bar</span>
<span class="hljs-comment">###########################</span>

<span class="hljs-comment"># set refresh interval for status bar</span>
<span class="hljs-built_in">set</span> -g status-interval 30

<span class="hljs-comment"># center the status bar</span>
<span class="hljs-built_in">set</span> -g status-justify left

<span class="hljs-comment"># show session, window, pane in left status bar</span>
<span class="hljs-built_in">set</span> -g status-left-length 40
<span class="hljs-built_in">set</span> -g status-left <span class="hljs-string">'#[fg=green]#S#[fg=blue] #I:#P #[default]'</span>

<span class="hljs-comment"># show hostname, date, time, and battery in right status bar</span>
set-option -g status-right <span class="hljs-string">'#[fg=green]#H#[default] %m/%d/%y %I:%M #[fg=red]#(battery discharging)#[default]#(battery charging)#{net_speed}'</span>

<span class="hljs-comment">###########################</span>
<span class="hljs-comment"># Colors</span>
<span class="hljs-comment">###########################</span>

<span class="hljs-comment"># color status bar</span>
<span class="hljs-built_in">set</span> -g status-style <span class="hljs-built_in">bg</span>=colour235
<span class="hljs-built_in">set</span> -g status-style <span class="hljs-built_in">fg</span>=white

<span class="hljs-comment"># highlight current window</span>
set-window-option -g window-status-current-style <span class="hljs-built_in">bg</span>=black
set-window-option -g window-status-current-style <span class="hljs-built_in">fg</span>=green

<span class="hljs-comment"># set color of active pane</span>
<span class="hljs-built_in">set</span> -g pane-border-style <span class="hljs-built_in">bg</span>=colour235
<span class="hljs-built_in">set</span> -g pane-border-style <span class="hljs-built_in">fg</span>=black
<span class="hljs-built_in">set</span> -g pane-active-border-style <span class="hljs-built_in">bg</span>=green
<span class="hljs-built_in">set</span> -g pane-active-border-style <span class="hljs-built_in">fg</span>=black

<span class="hljs-comment"># Resize pane</span>
bind-key J resize-pane -D 5
bind-key K resize-pane -U 5
bind-key H resize-pane -L 5
bind-key L resize-pane -R 5

<span class="hljs-comment">###########################</span>
<span class="hljs-comment"># Plugins</span>
<span class="hljs-comment">###########################</span>
<span class="hljs-built_in">set</span> -g @plugin <span class="hljs-string">'tmux-plugins/tpm'</span>
<span class="hljs-built_in">set</span> -g @plugin <span class="hljs-string">'christoomey/vim-tmux-navigator'</span>

<span class="hljs-comment"># Display internet download and upload speed</span>

<span class="hljs-comment"># Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)</span>
run -b <span class="hljs-string">'~/.tmux/plugins/tpm/tpm'</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[redirect_back vs redirect_to :back]]></title><description><![CDATA[Before Rails 4.x to easily navigate back once the action is complete we used to use redirect_to :back method that looks something like this:
class OrdersController < ApplicationController
  def approve
    order = Order.find params[:id]
    order.app...]]></description><link>https://blog.przbadu.dev/redirectback-vs-redirectto-back</link><guid isPermaLink="true">https://blog.przbadu.dev/redirectback-vs-redirectto-back</guid><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Ruby]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Tue, 27 Jun 2023 05:44:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1687844548346/c584cabf-9a8c-4988-9fab-8ba5cc4b3a69.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Before Rails <code>4.x</code> to easily navigate back once the action is complete we used to use <code>redirect_to :back</code> method that looks something like this:</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrdersController</span> &lt; ApplicationController</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">approve</span></span>
    order = Order.find params[<span class="hljs-symbol">:id</span>]
    order.approve!

    redirect_to <span class="hljs-symbol">:back</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>This action will approve the order and redirect the user to the previous location where he performed this action from. However, if this action was performed and the <code>HTTP_REFERER</code> is missing, which can lead to <code>ActionController::RedirectBackError</code> exception to be raised.</p>
<h3 id="heading-rescue-the-exception">Rescue the exception</h3>
<p>One way to solve this method is to <code>rescue</code> the Exception and redirect to the default location.</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrdersController</span> &lt; ApplicationController</span>
  rescue_from ActionController::RedirectBackError, <span class="hljs-symbol">with:</span> <span class="hljs-symbol">:redirect_to_default</span>


  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">approve</span></span>
    order = Order.find params[<span class="hljs-symbol">:id</span>]
    order.approve!

    redirect_to <span class="hljs-symbol">:back</span>
  <span class="hljs-keyword">end</span>

  private

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">redirect_to_default</span></span>
    redirect_to orders_path
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h3 id="heading-using-redirectback">Using <code>redirect_back</code></h3>
<p>After Rails 5.x, we can use <a target="_blank" href="https://apidock.com/rails/v5.0.0.1/ActionController/Redirecting/redirect_back">redirect_back</a> it accept <code>fallback_location</code> as an argument where we can provide the fallback location if the <code>HTTP_REFERER</code> is empty.</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrdersController</span> &lt; ApplicationController</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">approve</span></span>
    order = Order.find params[<span class="hljs-symbol">:id</span>]
    order.approve!

    redirect_back <span class="hljs-symbol">fallback_location:</span> orders_path
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[ActiveSupport#presence method]]></title><description><![CDATA[We want to set the default role to guest if both params[:role] is blank. By blank I mean either these values hold nil or '' .
Old way
def role(params: {})
    params[:role].present? ? params[:role] : 'guest'
end

Using ActiveSupport#presence
def role...]]></description><link>https://blog.przbadu.dev/activesupportpresence-method</link><guid isPermaLink="true">https://blog.przbadu.dev/activesupportpresence-method</guid><category><![CDATA[Ruby]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[presence]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Thu, 15 Jun 2023 02:15:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1686795423619/3ce827b6-2a3b-474f-9f16-7680699eaa4f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We want to set the default role to <code>guest</code> if both <code>params[:role]</code> is blank. By blank I mean either these values hold <code>nil</code> or <code>''</code> .</p>
<p><strong>Old way</strong></p>
<pre><code class="lang-ruby"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">role</span><span class="hljs-params">(<span class="hljs-symbol">params:</span> {})</span></span>
    params[<span class="hljs-symbol">:role</span>].present? ? params[<span class="hljs-symbol">:role</span>] : <span class="hljs-string">'guest'</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p><strong>Using</strong> <a target="_blank" href="https://www.rubydoc.info/gems/activesupport/2.3.17/Object:presence"><strong>ActiveSupport#presence</strong></a></p>
<pre><code class="lang-ruby"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">role</span><span class="hljs-params">(<span class="hljs-symbol">params:</span> {})</span></span>
    params[<span class="hljs-symbol">:role</span>].presence <span class="hljs-params">||</span> <span class="hljs-string">'guest'</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>It makes our code clean. <a target="_blank" href="https://www.rubydoc.info/gems/activesupport/2.3.17/Object:presence"><strong>ActiveSupport#presence</strong></a> returns an object if it’s <code>#present?</code> otherwise returns <code>nil</code>. <code>object.presence</code> is equivalent to <code>object.present? ? object : nil</code></p>
<p><strong>Another example:</strong></p>
<pre><code class="lang-ruby"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">address</span><span class="hljs-params">(<span class="hljs-symbol">params:</span> {})</span></span>
  params[<span class="hljs-symbol">:state</span>].presence <span class="hljs-params">||</span>
    params[<span class="hljs-symbol">:city</span>].presence <span class="hljs-params">||</span>
    params[<span class="hljs-symbol">:country</span>].presence <span class="hljs-params">||</span>
    <span class="hljs-string">'US'</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>Happy coding 🤘</p>
]]></content:encoded></item><item><title><![CDATA[Configure tmux to copy to system clipboard]]></title><description><![CDATA[Tmux is a terminal multiplexer. It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal.
I love Tmux because using Tmux + Neovim makes a great IDE...]]></description><link>https://blog.przbadu.dev/configure-tmux-to-copy-to-system-clipboard</link><guid isPermaLink="true">https://blog.przbadu.dev/configure-tmux-to-copy-to-system-clipboard</guid><category><![CDATA[tmux]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Bash]]></category><category><![CDATA[Developer]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Tue, 25 Apr 2023 08:03:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1686795920079/9de6a052-414f-4443-bcf7-a0e442259770.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://github.com/tmux/tmux/wiki">Tmux</a> is a terminal multiplexer. It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal.</p>
<p>I love Tmux because using <a target="_blank" href="https://github.com/tmux/tmux/wiki">Tmux</a> + <a target="_blank" href="https://neovim.io">Neovim</a> makes a great IDE.</p>
<p>Depending on which Terminal app you are using, some things in Linux if you copy content from within the <a target="_blank" href="https://github.com/tmux/tmux/wiki">Tmux</a> session, you can't paste them outside of <a target="_blank" href="https://github.com/tmux/tmux/wiki">Tmux</a>, which can be a big pain most of the time.</p>
<p>To solve that problem we are going to install <a target="_blank" href="https://github.com/astrand/xclip">Xclip</a>, it is a command line utility that is designed to run on any system with an X11 implementation and it allows you to use clipboard from your command line.</p>
<h3 id="heading-install-xclip">Install Xclip</h3>
<pre><code class="lang-bash">sudo apt install xclip
</code></pre>
<h3 id="heading-configure-tmux-to-use-xclip">Configure Tmux to use Xclip</h3>
<p>Put following script in your tmux configuration file <code>vim ~/.tmux.conf</code></p>
<pre><code class="lang-bash"><span class="hljs-comment">#for copying to sys clipboard</span>
<span class="hljs-built_in">bind</span> -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel <span class="hljs-string">"xclip -i -f -selection primary | xclip -i -selection clipboard"</span>
<span class="hljs-built_in">bind</span> -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel <span class="hljs-string">"xclip -i -f -selection primary | xclip -i -selection clipboard"</span>
<span class="hljs-built_in">bind</span> -T copy-mode-vi C-j send-keys -X copy-pipe-and-cancel <span class="hljs-string">"xclip -i -f -selection primary | xclip -i -selection clipboard"</span>
</code></pre>
<p>Reload your tmux session usually <code>prefix + r</code> . And your clipboard from tmux session should work again.</p>
]]></content:encoded></item><item><title><![CDATA[Implementing Feature Flags in Ruby on Rails with Flipper]]></title><description><![CDATA[Please visit Feature Flags if you want to know what a feature flag is and what are the benefits of using a feature flag.
This article will explain how to implement Feature Flags in Ruby on Rails using Flipper gem. It will cover different use cases fo...]]></description><link>https://blog.przbadu.dev/implementing-feature-flags-in-ruby-on-rails-with-flipper</link><guid isPermaLink="true">https://blog.przbadu.dev/implementing-feature-flags-in-ruby-on-rails-with-flipper</guid><category><![CDATA[Devops]]></category><category><![CDATA[software development]]></category><category><![CDATA[  feature flags]]></category><category><![CDATA[Developer]]></category><dc:creator><![CDATA[Pushpa Raj Badu]]></dc:creator><pubDate>Thu, 06 Apr 2023 15:56:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/w7ZyuGYNpRQ/upload/284432ba7ccc3eae707097b6ea7a44bd.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Please visit <a target="_blank" href="https://hashnode.com/post/clg58afbm000009k26v190ncm">Feature Flags</a> if you want to know what a feature flag is and what are the benefits of using a feature flag.</p>
<p>This article will explain how to implement <a target="_blank" href="https://hashnode.com/post/clg58afbm000009k26v190ncm">Feature Flags</a> in Ruby on Rails using <a target="_blank" href="https://github.com/jnunemaker/flipper">Flipper</a> gem. It will cover different use cases for feature flags, as well as best practices for managing and testing them.</p>
<h2 id="heading-setting-up-flipper">Setting up flipper</h2>
<p>If you don't have a Rails app, you can generate one quickly using <code>rails new my_app</code>. Now open the <code>Gemfile</code> and add the following line to the setup:</p>
<pre><code class="lang-ruby">gem <span class="hljs-string">"flipper-cloud"</span>
gem <span class="hljs-string">"flipper-active_record"</span>
</code></pre>
<p>Run <code>bundle</code> to install them</p>
<pre><code class="lang-ruby">bundle install
</code></pre>
<p>If you're using the ActiveRecord adapter, you'll also need to generate the migrations and run them.</p>
<pre><code class="lang-ruby">bin/rails g <span class="hljs-symbol">flipper:</span>active_record
bin/rails <span class="hljs-symbol">db:</span>migrate
</code></pre>
<p>This will generate a migration file that includes the required database table so that Flipper can work.</p>
<h2 id="heading-how-to-use-flipper-in-your-project">How to use Flipper in your project</h2>
<p>After the setup is completed you can now use Flipper as a feature flag for your features. Some of the examples of features we have used Flipper with are:</p>
<ul>
<li><p><strong>Sync Invoices To QuickBooks:</strong> We used Flipper to enable our Invoice sync with <a target="_blank" href="https://quickbooks.intuit.com/global/">QuickBooks</a> invoices as a beta feature for our certain beta customers. This way other customers who don't want to sync invoices will still use the app.</p>
</li>
<li><p><strong>Multiple Version of Dashboards for A/B testing:</strong> Flipper also has a great way to enable certain feature flags to a certain percentage of users.</p>
</li>
<li><p><strong>Feature Flags for Custom Reporting:</strong> Some customer wants their reports in a specific format and those reports should be only accessible to them.</p>
</li>
<li><p><strong>Continuous Deployment of new big feature:</strong> If your company follows a continuous deployment and <a target="_blank" href="https://hashnode.com/post/clfuk1bs5000109l38d4sejlp">DORA</a> metrics, feature flags are very important to do risk-free continuous delivery of your app without breaking them in production. And you can allow your QA team or stakeholders to test them before even your full feature is completed.</p>
</li>
</ul>
<p>These are mostly our use cases but they can differ based on how your team delivers the product to the end users.</p>
<p>Flipper makes it dead simple to enable/disable features for your app, for example:</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InvoiceSyncFeature</span></span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span></span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> Flipper.enabled?(<span class="hljs-symbol">:qb_sync</span>, current_user)

        sync_with_quickbooks
    <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h2 id="heading-how-to-enable-the-flipper-feature-flags">How to enable the Flipper feature flags?</h2>
<p>You can enable Flipper feature flags for different actors, most of the rails applications will have <code>user</code> as an actor.</p>
<p>Here is how you can enable it:</p>
<pre><code class="lang-ruby"><span class="hljs-comment"># Enable a feature for everyone</span>
Flipper.enable <span class="hljs-symbol">:qb_sync</span>

<span class="hljs-comment"># Enable a feature for a specific actor</span>
Flipper.enable_actor <span class="hljs-symbol">:qb_sync</span>, current_user

<span class="hljs-comment"># Enable a feature for a group of actors</span>
Flipper.enable_group <span class="hljs-symbol">:qb_sync</span>, <span class="hljs-symbol">:admin</span>

<span class="hljs-comment"># Enable a feature for a percentage of actors</span>
Flipper.enable_percentage_of_actors <span class="hljs-symbol">:qb_sync</span>, <span class="hljs-number">2</span>
</code></pre>
<h2 id="heading-flipper-ui">Flipper UI</h2>
<p>If you want to manage your feature flags from UI you can use:</p>
<pre><code class="lang-ruby">gem <span class="hljs-string">'flipper-ui'</span>
</code></pre>
<p>Install it with <code>bundle install</code></p>
<p>And mount it into your <code>config/routes.rb</code> file</p>
<pre><code class="lang-ruby">YourRailsApp::Application.routes.draw <span class="hljs-keyword">do</span>
  mount Flipper::UI.app(Flipper) =&gt; <span class="hljs-string">'/flipper'</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h2 id="heading-securing-your-flipper-routes">Securing your flipper routes</h2>
<p>Obviously, we don't want to leave the flipper open for everyone and mess around with it. For that, we are using the <a target="_blank" href="https://github.com/heartcombo/devise">Devise</a> gem and we only allow admins to access it. In our case, admins are our site owners.</p>
<pre><code class="lang-ruby">authenticated <span class="hljs-keyword">do</span>
    mount Flipper::UI.app(Flipper) =&gt; <span class="hljs-string">'/flipper'</span>, <span class="hljs-symbol">constraints:</span> lambda { <span class="hljs-params">|req|</span>
      current_user = req.env[<span class="hljs-string">'warden'</span>].user
      current_user.present? &amp;&amp; current_user.admin?
    }
<span class="hljs-keyword">end</span>
</code></pre>
<p><strong>Or you can use basic HTTP authentication via Rack</strong></p>
<pre><code class="lang-ruby"><span class="hljs-comment"># config/routes.rb</span>

flipper_app = Flipper::UI.app <span class="hljs-keyword">do</span> <span class="hljs-params">|builder|</span>
  builder.use Rack::Auth::Basic <span class="hljs-keyword">do</span> <span class="hljs-params">|username, password|</span>
    <span class="hljs-comment"># Verify credentials</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
mount flipper_app, <span class="hljs-symbol">at:</span> <span class="hljs-string">'/flipper'</span>
</code></pre>
<p>Happy Hacking ;)</p>
]]></content:encoded></item></channel></rss>