<?xml version="1.0" encoding="UTF-8" ?>
  <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title><![CDATA[Yatish Mehta]]></title>
        <link>https://yatishmehta.com</link>
        <atom:link href="https://yatishmehta.com/rss" rel="self" type="application/rss+xml" />
        <description><![CDATA[Thoughts, stories and ideas]]></description>
        <item>
          <title><![CDATA[September SF Ruby Meetup at Binti]]></title>
          <link>https://yatishmehta.com/notes/2025-10-04-september-sf-ruby-meetup-at-binti</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-10-04-september-sf-ruby-meetup-at-binti</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>The September SF Ruby meetup was hosted at <a href="https://binti.com">Binti</a>, 
and it was great to see a new company join the roster of Ruby meetup hosts.</p>
<p>The evening kicked off with Chris Fung introducing <a href="https://binti.com">Binti</a> 
and their mission. Binti builds a platform for child care agencies with the goal of making sure every child gets a home. 
Recently, they added AI features to help social workers work more efficiently—so much so that they were even mentioned by Anthropic&#39;s
CEO in Forbes. </p>
<p>Next, Irina spoke about the upcoming <a href="https://sfruby.com/">SF Ruby Conference</a>. I&#39;m excited to attend
The Ruby Passport will be available there. Tickets currently have a $50 discount, </p>
<p>While talking about the recent Ruby Central changes, I especially liked her quote: &quot;We
can choose to believe that every person is actually acting from the best possible intentions.&quot; 
It was a refreshing reminder of the positive spirit in the Ruby community.</p>
<p>The talks then continued:</p>
<ul>
<li>André Arko introduced <a href="https://github.com/spinel-coop/rv">rv.dev</a>, a new Ruby version manager inspired by uv
and built in Rust.</li>
<li>Cade Friedenbach gave a talk on how Binti built their webhooks and data export platform. They kept things
simple with Service Objects, <a href="https://github.com/krisleech/wisper">Wisper</a>, and background jobs, and used JSON
schema validation before saving to the database via
<a href="https://github.com/mirego/activerecord_json_validator">activerecord_json_validator</a>.</li>
<li>Kody Kendall spoke about <a href="https://github.com/llamapressai/LlamaPress">llamapress</a>, an open-source platform for
building Ruby on Rails apps with LLMs. He explained how Rails runner can be used as a tool for agentic coding,
and why Rails conventions and concise syntax make it especially suited for AI-driven development.</li>
<li>Arjun shared how Ruby is being used in medical software to build 3D models from 2D scans. It was exciting to
see Ruby applied outside of web development. He also talked about the importance of compliance and testing in
the medical field—no &quot;move fast and break things&quot; there.</li>
<li>Finally, Pranav from <a href="https://chatwoot.com">Chatwoot</a> presented their open-source gem for building <a href="https://ai-agents.chatwoot.dev/">AI agents</a>
based on <a href="https://rubygems.org/gems/ruby_llm">Ruby LLM</a>. This is the foundation behind Captain, their agentic
customer support assistant. It enables customers to create their own assistants by providing custom
instructions to triage support tickets. I liked how simple their API was. After the talk, I got to chat with
him about Chatwoot&#39;s journey and how they&#39;ve kept their architecture and deployment simple.</li>
</ul>
<p>Overall, it was a fantastic meetup. It was great to see the
Ruby community experimenting with AI in practical ways, beyond the hype.</p>
]]></content:encoded>
          <pubDate>Sat, 04 Oct 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[SF Ruby August Meetup at Github]]></title>
          <link>https://yatishmehta.com/notes/2025-08-27-sf-ruby-august-meetup-at-github</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-08-27-sf-ruby-august-meetup-at-github</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>I had the chance to attend the August SF Ruby Meetup today at GitHub. As always, it was a mix of exciting talks, practical tips, and new projects to check out.</p>
<p>Drew Hoskins introduced Temporal, which now has an official Ruby SDK. He showed how we can build Durable Execution with distributed systems. He demoed a simple workflow using Temporal that pulls a user&#39;s location using two external services. I&#39;ve built workflow engines in the past using background jobs and Redis, but as workflows grow in complexity, they quickly become a pain to manage. Temporal makes it easier to focus on your business logic than spend time managing the infrastructure.</p>
<p>Enrique Mogollan presented his MCP server inspector written in Ruby. He demoed the CLI version and shared his plans for a future web UI.He also pointed us to mcpui.dev, an interesting project that lets you build dynamic UIs for MCP servers.</p>
<p>Miles Georgi introduced <a href="https://github.com/foobara/foobara">Foobara</a>, a new Ruby framework centered around the command pattern. It provides structure to the classic Service Object approach and offers a way to organize apps around clearly defined commands.</p>
<p>Irina Nazarova, CEO of Evil Martians, shared how her team helped Whop reduce CI time by 50%.</p>
<p>Some practical takeaways:</p>
<ul>
<li>Skip logging in tests</li>
<li>Avoid external API calls (use mocks instead)</li>
<li>Be mindful of what your factories create — reuse associated instances when possible</li>
</ul>
<p>I also discovered a couple of tools worth trying:</p>
<ul>
<li><a href="https://github.com/tmm1/test-queue">test-queue</a>, an alternative to parallel_tests</li>
<li><a href="https://github.com/test-prof/test-prof">test-prof</a>, a Ruby test profiling toolkit</li>
</ul>
<p>Sergey Karayev closed out the talks with a session on programming with AI agents. He shared do&#39;s and don&#39;ts when working with LLM coding agents and explained how to draft an effective agent.md.His demo of Superconductor was a fun look at what&#39;s possible with modern AI tooling.</p>
<p>Between talks, we had an open mic session where I officially launched my new newsletter: <a href="https://tokenruby.com/">TokenRuby</a>.</p>
<p><img src="/images/notes/sf-ruby-tokenruby.jpeg" alt="Token ruby at SF Ruby"></p>
]]></content:encoded>
          <pubDate>Wed, 27 Aug 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[We Need Humans.txt]]></title>
          <link>https://yatishmehta.com/notes/2025-08-07-we-need-humans-txt</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-08-07-we-need-humans-txt</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>I finally fixed my blog&#39;s RSS feed so it shows the full content instead of just the title and a link.</p>
<p>That sent me down a rabbit hole reading about the history of feed formats—RSS, Atom, and how we got here. At their core, these formats were designed to make content easy to read and parse for machines.</p>
<p>Wait, it sounds familiar? Yep LLMs.</p>
<p>As AI continues to grow, we&#39;ll probably need something similar: a clean, predictable format that LLMs can easily understand. There&#39;s already an effort around making llms.txt a standard. </p>
<p>But hey, the modern web isn&#39;t just hard for LLMs. Between SPAs, trackers, cookie banners, autoplay videos, permission requests and that one weird floating chat icon that blocks the text—maybe we humans need help too. </p>
<p>Can we have humans.txt please? A sane simple clean readable format for us humans.</p>
<p>I am happy LLMs are pushing us to make web back to old days of simple HTML.
Because just like baggy jeans, everything good eventually comes back in style.</p>
]]></content:encoded>
          <pubDate>Thu, 07 Aug 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Oh Right, Ruby Refinements Exist]]></title>
          <link>https://yatishmehta.com/notes/2025-07-24-refinements</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-24-refinements</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>While browsing through the source code of a gem recently, I came across something I hadn&#39;t seen in a while: <strong>refinements</strong>.</p>
<p>Refinements were introduced in Ruby 2.4 as a better alternative to monkey-patching. It allows to override a core class method in a limited scope. 
On the other hand, monkey patching changes the method across the whole program.</p>
<p>Here&#39;s the example I saw:</p>
<pre><code class="language-ruby"># frozen_string_literal: true

module Isolator
  module ThreadFetch 
    refine Thread do
      def fetch(key, fallback = :__undef__)
        raise KeyError, &quot;key not found: #{key}&quot; if !key?(key) &amp;&amp; fallback == :__undef__

        self[key] || fallback
      end
    end
  end
end
</code></pre>
<p>This overrides Thread&#39;s fetch method.</p>
<p>Later in the same gem, it&#39;s used like this</p>
<pre><code class="language-ruby">module Isolator
  using Isolator::ThreadFetch

  class ThreadStateProxy
    # Only within this scope, Thread#fetch behaves as defined above
    ...
  end
end
</code></pre>
<p>What&#39;s nice here is that <code>Thread#fetch</code> isn&#39;t changed globally. The override only applies inside the <code>Isolator::ThreadStateProxy</code> module. Other parts of the app that use Thread won&#39;t be affected.</p>
]]></content:encoded>
          <pubDate>Thu, 24 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Meetup at Figma]]></title>
          <link>https://yatishmehta.com/notes/2025-07-22-sf-ruby-meetup-at-figma</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-22-sf-ruby-meetup-at-figma</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>I attended the July edition of the SF Ruby meetup at Figma&#39;s beautiful office on Market Street.</p>
<p>The first talk was from the Figma team discussing their tech stack. They use Sinatra, ActiveRecord, and a custom background job scheduler. They are heavy users of Sorbet. Currently, they are in the process of modularizing their monolith using Packwerk.</p>
<p>They also gave a demo of their internal admin app. It has a unique structure where they define the panel in Ruby, which then compiles to YAML/JSON and is rendered using React.</p>
<p>Marco Roth gave a deep dive into his new project, Herb, which is a language server for .erb files. Excited to see better tooling for .erb files.</p>
<p>👉🏽 <a href="https://speakerdeck.com/marcoroth/the-modern-view-layer-rails-deserves-a-vision-for-2025-and-beyond-at-sf-bay-area-ruby-meetup-july-2025">Slides</a></p>
<p>Finally, the talk was from Jeremy Evans. He discussed how he fixed unnecessary object allocations. It definitely piqued my interest in Ruby VM memory management.</p>
<p>👉🏽 <a href="https://code.jeremyevans.net/presentations/sf-ruby-meetup-2025-07/index.html#1">Slides</a></p>
<p>Along with the great talks, there was another highlight for me.
I met a friend after almost a decade. We first connected at PuneRB and stayed in touch since then through Twitter
and now, here we were, catching up at a Ruby meetup on the other side of the world.</p>
]]></content:encoded>
          <pubDate>Tue, 22 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[SF Ruby's first AI hackathon]]></title>
          <link>https://yatishmehta.com/notes/2025-07-19-sf-rubys-first-ai-hackathon</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-19-sf-rubys-first-ai-hackathon</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>Today was the SF Ruby AI Hackathon, an event I&#39;ve been planning for the last few months.</p>
<p>And what a day it was.</p>
<p>We had a strong turnout, with over 55 developers. We kicked off with a quick orientation, outlining the schedule and setting the tone for the day. People started forming teams, throwing around ideas, and diving into code. 
People brought some great vibes. One participant showed up with a full-fledged multi-monitor setup — yes, you read that right, multiple monitors at a hackathon. Meanwhile, someone else was casually vibe-coding on an iPad.</p>
<VideoPlayer src="/images/notes/sf-ruby-hackathon-video-compressed.mp4" title="SF Ruby AI Hackathon demo presentations" aspectRatio="portrait" />

<p>After the lunch break (thanks Sentry for the food), the building continued.
Along with the judges, I went around the room to get a quick vibe check. </p>
<p>Demos began at 4 PM — each team had just two minutes to showcase what they&#39;d created.</p>
<p>And honestly? I was blown away.</p>
<p>I was surprised to see what people could build in less than eight hours.
I was relieved that I was not the judge because selecting the top three would be a challenge.
Many thanks to Kamil Nicieja, Justin Bowen, Sarah Mei, and Victoria Melnikova for doing a fantastic job.</p>
<p>The winners were:</p>
<ul>
<li>Vibeseq - collaborative music production by Josh Leichtung</li>
<li>LLM Workbench - build LLM-enabled pipelines by Jonte Craighead</li>
<li>Wheel Town - making cities safer for cyclists by Andrew Ford and Nathan Tate</li>
</ul>
<p><img src="/images/notes/sf-ruby-hackathon-image.webp" alt="SF Ruby AI Hackathon"></p>
<p>Alongside these, there were plenty of other interesting projects — from AI-assisted couple counseling to tools for programming in foreign languages.</p>
<p>What stood out was the enthusiasm within the Ruby community for AI. It has really inspired people to be builders, similar to what Rails did a decade back.</p>
<p>So what next? Will there be another hackathon?
I was thinking of bringing back Rails Rumble, a 48-hour hackathon but now with an AI twist. DM me if you&#39;re interested.</p>
]]></content:encoded>
          <pubDate>Sat, 19 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[I Tried to Troll a Scammer, But Ended Up Debugging Zoho Assist]]></title>
          <link>https://yatishmehta.com/notes/2025-07-18-scam-call-that-led-me-to-fixing-zohos-frontend-code</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-18-scam-call-that-led-me-to-fixing-zohos-frontend-code</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>Today, I received a message from an unknown number claiming there was a $168.12 charge on my Apple Pay account. It said I should call the given number if I didn&#39;t authorize the transaction.</p>
<p>I knew it was a scam, but for my own entertainment, I decided to call them.</p>
<p>After some back and forth, they asked me to join a remote control session using Zoho Assist.
I clicked the link they sent and pretended to follow along. </p>
<p>I kept pretending the page was loading. While keeping them engaged, I decided to report to Zoho about scammers abusing their product.</p>
<p>Zoho has a large “Report Abuse” CTA on the page, so I guess they are aware of this issue.</p>
<p>I filled out the form with the given session ID.
It turns out there&#39;s a bug in their frontend code that prevents submitting the form.</p>
<p>Frustrated, I opened up inspect element and found the following event handlers:</p>
<pre><code class="language-js">window.addEventListener(&quot;DOMContentLoaded&quot;, () =&gt; {
    reportOnload();
    document.getElementById(&quot;name&quot;)?.addEventListener(&quot;keyup&quot;, () =&gt; {
        submitCont(this.form, false, &#39;name&#39;)
    });

    document.getElementById(&quot;phone&quot;)?.addEventListener(&quot;keyup&quot;, () =&gt; {
        submitCont(this.form, false, &#39;phone&#39;)
    });

    document.getElementById(&quot;Email&quot;)?.addEventListener(&quot;keyup&quot;, () =&gt; {
        submitCont(this.form, false, &#39;email&#39;)
    });

    document.getElementById(&quot;sessionCode&quot;)?.addEventListener(&quot;keyup&quot;, () =&gt; {
        submitCont(this.form, false, &#39;&#39;)
    });

    document.getElementById(&quot;abuse_desc&quot;)?.addEventListener(&quot;keyup&quot;, () =&gt; {
        submitCont(this.form, false, &#39;abuse&#39;)
    });

    document.getElementById(&quot;report_btn&quot;)?.addEventListener(&quot;click&quot;, () =&gt; {
        submitCont(this.form, true, &#39;&#39;)
    });
});
</code></pre>
<p>They were using arrow functions with an incorrect reference to <code>this</code>.
Since arrow functions do not bind their own this, <code>this.form</code> is undefined in these callbacks.</p>
<p>I found the expected payload for the form and submitted the request via curl.</p>
<p>They didn&#39;t have any captcha or CSRF token. I emailed their team about the issue.</p>
<p>Who knew a playful call with scammers would lead to a debugging session.</p>
]]></content:encoded>
          <pubDate>Fri, 18 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Behind the Scenes: Final Touches for the SF Ruby AI Hackathon]]></title>
          <link>https://yatishmehta.com/notes/2025-07-17-behind-the-scenes-final-touches-for-sf-ruby-ai-hackathon</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-17-behind-the-scenes-final-touches-for-sf-ruby-ai-hackathon</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>We wrapped up the final organizers meeting for the upcoming <a href="https://lu.ma/znhcct7v">SF Ruby AI Hackathon</a>, where we also briefed the judges on the criteria. </p>
<p>Later, I worked on the event presentation
I told myself I wouldn&#39;t use AI for it—but I caved and used it to pick the right emojis.</p>
<p>It was satisfying to see months of planning come together. But of course, no matter how early you start, there&#39;s always something left to do until the last minute. Damn Parkinson&#39;s Law</p>
]]></content:encoded>
          <pubDate>Thu, 17 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[New React UI Libraries & More]]></title>
          <link>https://yatishmehta.com/notes/2025-07-16-new-react-ui-libraries</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-16-new-react-ui-libraries</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<ul>
<li><p>I discovered some new React UI libraries:</p>
<ul>
<li><a href="https://www.untitledui.com/react">Untitled UI</a> - I&#39;ve used their design system before, so it&#39;s exciting to have their React UI library.</li>
<li><a href="https://www.9ui.dev/">9ui</a> - Built on BaseUI, created by the RadixUI creators. It&#39;s similar to shadcn.</li>
</ul>
</li>
<li><p>I came across <a href="https://x.com/tdinh_me/status/1945031856627372295">tweet about hacker residencies</a>. 
I would like the idea of spending a month in a hacker house. It would provide the dedicated time and environment to build.</p>
</li>
</ul>
]]></content:encoded>
          <pubDate>Wed, 16 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Ubicloud: Open Source AWS Alternative]]></title>
          <link>https://yatishmehta.com/notes/2025-07-15-ubicloud-open-source-aws</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-15-ubicloud-open-source-aws</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>I went through the codebase of Ubicloud. It&#39;s an open source AWS alternative built using Roda.</p>
<p>One thing I liked is their simple but powerful implementation of relationship-based access control (ReRBAC). </p>
<p>It stores tuples of subject, action, and object. If the tuple exists, then the action is allowed. 
A light version of Google&#39;s Zanzibar project.</p>
<ul>
<li><a href="https://github.com/ubicloud/ubicloud/blob/main/lib/authorization.rb">authorization.rb</a></li>
<li><a href="https://www.ubicloud.com/docs/security/authorization">Authorization</a></li>
</ul>
]]></content:encoded>
          <pubDate>Tue, 15 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Weekend Log: Work From Cafe ☕️]]></title>
          <link>https://yatishmehta.com/notes/2025-07-14-weekend-work-from-cafe</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-14-weekend-work-from-cafe</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>We worked from <a href="https://g.co/kgs/yc4dEQA">Sama Coffee Shop</a> today.
Unlike Starbucks or Peet&#39;s, it had a very different vibe.</p>
<p>Even though I&#39;m not a coffee drinker, I still ordered the Rose Cardamom Honey ☕️. Highly recommend!!</p>
<p>There were lots of fellow hackers around. </p>
<p>There&#39;s something about working from a café—despite the people, the music, and the chatter, I was still able to do deep work (without headphones).</p>
<p>I also submitted my blog to the <a href="https://www.reddit.com/r/nextjs/comments/1lz53wz/finally_happy_with_my_personal_website/">r/nextjs subreddit</a> today. 
I was surprised by the responses. I got a lot of positive comments. That&#39;s not usually been my experience with Reddit 😅.</p>
<p>One thing I realized while reviewing the post: I&#39;m not optimizing images in my blogs or notes.
<a href="/notes/2025-07-13-beach-time-6-hour-podcast-and-some-updates">That</a> note ended up loading 13MB just for a single image 😬.</p>
<p>Lesson learned: don&#39;t ship 13MB images to production.
Time to switch to <code>next/image</code> before Reddit calls me out.</p>
]]></content:encoded>
          <pubDate>Mon, 14 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Beach, Blog Updates, and a 6-Hour Podcast]]></title>
          <link>https://yatishmehta.com/notes/2025-07-13-beach-time-6-hour-podcast-and-some-updates</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-13-beach-time-6-hour-podcast-and-some-updates</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>It was the weekend, so we decided to go to the beach.
<img src="/images/notes/half_moon_bay_beach.png" alt="Half Moon Bay Beach"></p>
<p>I managed to listen to a <a href="https://www.youtube.com/watch?v=vagyIcmIGOQ">6-hour podcast</a>:Lex Fridman with DHH.</p>
<p>I made some updates to the blog</p>
<ul>
<li>Added a new favicon</li>
<li>Upgraded NextJS to v15</li>
</ul>
]]></content:encoded>
          <pubDate>Sun, 13 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Exploring AdonisJS]]></title>
          <link>https://yatishmehta.com/notes/2025-07-12-exploring-adonisjs-and-other-blog-updates</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-12-exploring-adonisjs-and-other-blog-updates</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>Today, I came across <a href="https://adonisjs.com/">AdonisJS</a>. I was surprised to learn it&#39;s been around since 2015, but I had never heard of it.</p>
<p>Even though it&#39;s a JS framework, the focus is more on the backend. You can use any frontend library.
It has official support for InertiaJS.</p>
<p>I like that it&#39;s strongly opinionated and puts focus on developer experience.</p>
<p>I went through the entire documentation. It&#39;s hard to explain, but it has taste—something about it just feels well-thought-out and put together with care.</p>
<p>Funny how reading good docs can trick you into redesigning your blog font at midnight.
Anyway, Instrument Sans it is.</p>
]]></content:encoded>
          <pubDate>Sat, 12 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Some Minor Updates to My Blog]]></title>
          <link>https://yatishmehta.com/notes/2025-07-11-some-minor-updates-to-my-blog</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-11-some-minor-updates-to-my-blog</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>I made some minor updates to my blog.</p>
<ul>
<li>Added the favicon image. I kept it simple. It is a letter Y with black background.</li>
<li>Added other icons for apple-touch and android-chrome</li>
<li>Added a redirect for www to root. Now <a href="https://www.yatishmehta.com">https://www.yatishmehta.com</a> redirects to <a href="https://yatishmehta.com">https://yatishmehta.com</a></li>
</ul>
]]></content:encoded>
          <pubDate>Fri, 11 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Understanding Async Gem and Its OS Primitives]]></title>
          <link>https://yatishmehta.com/notes/2025-07-10-understanding-rubys-async-gem-and-its-os-primitives</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-10-understanding-rubys-async-gem-and-its-os-primitives</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>Today, I read the article <a href="https://paolino.me/async-ruby-is-the-future/">Async Ruby is the Future</a>, which gave a great overview of how Ruby&#39;s async gem uses Fibers and an event loop to handle I/O without blocking.</p>
<p>After reading it, I started wondering how the OS knows when a Fiber can resume. I dug deeper and learned about <code>epoll_ctl</code> / <code>epoll_wait</code> on Linux, and their macOS equivalent, <code>kqueue</code> and <code>kevent</code>.</p>
<p>With some help from ChatGPT, I wrote a C program that uses <code>kevent</code> to wait for STDIN input asynchronously. 
It was a good exercise to learn these low level OS primitives</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/event.h&gt;
#include &lt;sys/time.h&gt;
#include &lt;fcntl.h&gt;

int main() {
    int kq = kqueue();
    if (kq == -1) {
        perror(&quot;kqueue&quot;);
        exit(1);
    }

    struct kevent change;
    EV_SET(&amp;change, STDIN_FILENO, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);

    printf(&quot;Waiting for input (kqueue)...\n&quot;);

    struct kevent event;
    int nev = kevent(kq, &amp;change, 1, &amp;event, 1, NULL);  // NULL = block forever

    if (nev == -1) {
        perror(&quot;kevent&quot;);
        exit(1);
    }

    if (event.filter == EVFILT_READ) {
        char buffer[1024];
        ssize_t n = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
        if (n &gt; 0) {
            buffer[n] = &#39;\0&#39;;
            printf(&quot;Read from STDIN: %s\n&quot;, buffer);
        }
    }

    close(kq);
    return 0;
}
</code></pre>
<p>This gave me a better understanding of OS Kernel abstractions.</p>
]]></content:encoded>
          <pubDate>Thu, 10 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Added Support for Deployment Using Kamal with GitHub Actions]]></title>
          <link>https://yatishmehta.com/notes/2025-07-09-added-support-for-deployment-using-kamal-with-github-actions</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-09-added-support-for-deployment-using-kamal-with-github-actions</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>I recently set up Kamal deployment for my Rails dev kit, Shore, using GitHub Actions.</p>
<p>➡️ <a href="https://github.com/yatish27/shore/blob/main/.github/workflows/deploy.yml">View the deploy.yml</a></p>
<p>While setting this up, I forgot to set one of the environment variables from GitHub secrets in the workflow. I forgot secrets are not available as environment variables automatically</p>
<p>Next up:</p>
<ul>
<li>Move the container registry from Docker Hub to GitHub Container Registry.</li>
<li>Optimize the deploy step (currently takes around 4 minutes).</li>
</ul>
]]></content:encoded>
          <pubDate>Wed, 09 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Hello, World!]]></title>
          <link>https://yatishmehta.com/notes/2025-07-08-hello-world</link>
          <guid isPermaLink="true">https://yatishmehta.com/notes/2025-07-08-hello-world</guid>
          <description><![CDATA[]]></description>
          <content:encoded><![CDATA[<p>This is my first note. I plan to write a daily log here.</p>
<p>The design of this notes system is inspired by <a href="https://healeycodes.com/notes">Andrew Healey&#39;s notes</a>.</p>
]]></content:encoded>
          <pubDate>Tue, 08 Jul 2025 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[How I Use YARD for Documenting Ruby Code]]></title>
          <link>https://yatishmehta.com/blog/how-i-use-yard-for-documenting-ruby-code</link>
          <guid isPermaLink="true">https://yatishmehta.com/blog/how-i-use-yard-for-documenting-ruby-code</guid>
          <description><![CDATA[A comprehensive guide to using YARD (Yet Another Ruby Documentation) for creating clear, maintainable documentation in Ruby projects.]]></description>
          <content:encoded><![CDATA[<p>Ruby offers several code documentation specifications like <a href="https://rdoc.github.io/rdoc/">RDoc</a>, <a href="https://yardoc.org/">YARD</a>, and <a href="http://tomdoc.org/">TomDoc</a>. I prefer <a href="https://yardoc.org/">YARD</a> for its flexibility and rich feature set.</p>
<h2>Getting Started with YARD</h2>
<p>You can document your Ruby code using YARD by following a specific syntax. For example:</p>
<pre><code class="language-ruby">class Person
  # Greets a person
  #
  # @param [String] name the name of the person
  #
  # @return [String] the greeting
  def greet(name)
    &quot;Hello #{name}&quot;
  end
end
</code></pre>
<h2>Installation and Usage</h2>
<p>Install the YARD gem using:</p>
<pre><code class="language-bash">gem install yard
</code></pre>
<p>To generate documentation, run:</p>
<pre><code class="language-bash">yard doc person.rb
</code></pre>
<p>This generates a documentation site in the doc directory. You can preview it locally using:</p>
<pre><code class="language-bash">yard server
</code></pre>
<p>Since the documentation consists of static HTML files, you can host it on any web server.</p>
<h2>YARD Cheatsheet</h2>
<pre><code class="language-ruby"># Represents a generic document in a document management system.
# @abstract
# @author John Doe
# @since 1.0.0
# @deprecated Use NewDocument instead.
class Document
  # @!attribute [r] title
  #   @return [String]
  attr_reader :title

  # @!attribute [w] description
  #   @return [String]
  attr_writer :description

  # @!attribute [rw] sections
  #   @api private
  #   @return [Array&lt;Section&gt;]
  attr_accessor :sections

  # Initializes a new Document instance.
  # @note This method should be called with care.
  #
  # @param [String] title the title of the document
  # @param [String] description the description of the document
  # @param [Hash] options additional configuration options
  # @option options [Boolean] :editable whether the document can be edited
  # @yieldparam [String] content The content of the document.
  # @yieldreturn [String] Returns a modified content.
  #
  # @raise [ArgumentError] if the title is nil
  #
  # @return [Document] a new Document instance
  def initialize(title, description, options = {})
    raise ArgumentError, &quot;Title cannot be nil&quot; unless title

    @title = title
    @description = description
    @editable = options.fetch(:editable, true)

    @content = yield(content) if block_given?
  end

  # Edits the document content.
  #
  # @overload edit(new_content)
  #   @param [String] new_content the new content for the document
  #   @return [Boolean] true if editing was successful, false otherwise
  #
  # @overload edit
  #   @yield Gives a block to process the current content.
  #   @yieldreturn [String] Returns the new content after processing.
  #   @return [Boolean] true if editing was successful, false otherwise
  #
  # @deprecated Use `modify` method instead.
  def edit(new_content = nil)
    if new_content
      @content = new_content
      true
    elsif block_given?
      @content = yield(@content)
      true
    else
      false
    end
  end

  # @todo Implement a proper save mechanism
  def save
    # Implementation pending
  end

  # Views the document
  #
  # @example Viewing the document title
  #   document.view_title #=&gt; &quot;Sample Document&quot;
  #
  # @see #edit
  # @return [String] the title of the document
  def view_title
    @title
  end
end
</code></pre>
<p>I created a cheatsheet of all YARD tags:
It is available as a <a href="https://gist.github.com/yatish27/bc2c630f190eedbbae5277a91005adf2">gist</a>.</p>
<h2>Custom Tags in YARD</h2>
<p>YARD supports <code>.yardopts</code> for custom configuration, including custom tags. I use this to document Rails controller actions:</p>
<pre><code>--tag url
--tag action
</code></pre>
<p>Example usage in a Rails controller:</p>
<pre><code class="language-ruby">class DocumentsController &lt; ApplicationController

  # @url /documents
  # @action GET
  #
  # List all documents.
  #
  def index
    @documents = Document.all
  end

  # @url /documents/:id
  # @action GET
  #
  # Show a single document.
  #
  def show
    @document = Document.find(params[:id])
  end
end
</code></pre>
<p>Since <code>params</code> is not explicitly passed as an argument, you cannot use <code>@param</code> and <code>@option</code> to document them. One of the methods that the YARD author suggests to document these methods is to use the <code>@overload</code> tag.</p>
<pre><code class="language-ruby"># @overload show(params)
#   @param params [Hash]
#   @option params [String] :id the id of the document
def show
  @document = Document.find(params[:id])
end
</code></pre>
<h2>YARD in VSCode</h2>
<p>VSCode has a YARD extension that provides auto-completion for YARD tags. You can generate documentation for a method by placing the cursor at the end of its definition and pressing <code>Ctrl+Alt+Enter</code> (or <code>Option+Command+Enter</code> on macOS).</p>
<p>For example, with the cursor anywhere on this line:</p>
<pre><code class="language-ruby">def foo(name, baz = false) # &lt;- put cursor at any place of this line
end
</code></pre>
<p>The extension generates:</p>
<pre><code class="language-ruby">#
# &lt;Description&gt;
#
# @param [&lt;Type&gt;] name &lt;description&gt;
# @param [&lt;Type&gt;] baz &lt;description&gt;
#
# @return [&lt;Type&gt;] &lt;description&gt;
#
def foo(name, baz = false)
end
</code></pre>
<h2>Conclusion</h2>
<p>YARD is a powerful tool for documenting Ruby code. It improves code readability if used correctly.</p>
<p>Happy documenting!</p>
]]></content:encoded>
          <pubDate>Thu, 19 Dec 2024 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Include, Extend and Prepend in Ruby]]></title>
          <link>https://yatishmehta.com/blog/include-extend-and-prepend-in-ruby</link>
          <guid isPermaLink="true">https://yatishmehta.com/blog/include-extend-and-prepend-in-ruby</guid>
          <description><![CDATA[A comprehensive guide to understanding the three ways to add methods from modules to classes in Ruby: include, extend, and prepend.]]></description>
          <content:encoded><![CDATA[<p>There are three ways to add methods from a module to a class in Ruby.</p>
<h2>Include</h2>
<p>It adds methods as instance methods</p>
<pre><code class="language-ruby">module Swimmable
  def swim
    &quot;I can swim!&quot;
  end
end

class Fish
  include Swimmable
end

fish = Fish.new
puts fish.swim # =&gt; &quot;I can swim!&quot;
puts Fish.ancestors # =&gt; [Fish, Swimmable, Object, Kernel, BasicObject]
</code></pre>
<h2>Extend</h2>
<p>It adds methods as class methods</p>
<pre><code class="language-ruby">module Flyable
  def fly
    &quot;I can fly!&quot;
  end
end

class Bird
  extend Flyable
end

puts Bird.fly # =&gt; &quot;I can fly!&quot;
puts Bird.ancestors # =&gt; [Bird, Object, Kernel, BasicObject]
</code></pre>
<h2>Prepend</h2>
<p>It adds methods as instance methods but with higher precedence.</p>
<pre><code class="language-ruby">module Logger
  def log_action
    &quot;Logged: #{super}&quot;
  end
end

class Action
  prepend Logger

  def log_action
    &quot;Some action&quot;
  end
end

action = Action.new
puts action.log_action # =&gt; &quot;Logged: Some action&quot;
puts Action.ancestors # =&gt; [Logger, Action, Object, Kernel, BasicObject]
</code></pre>
<p>Normally, prepend is defined at the end of the class definition.</p>
<p>You can also prepend a module outside the class definition</p>
<pre><code class="language-ruby">Action.prepend(Logger)
</code></pre>
<h2>Consecutive prepends</h2>
<pre><code class="language-ruby">module Formatter
  def display
    &quot;Formatted: #{super}&quot;
  end
end

module Validator
  def display
    &quot;Validated: #{super}&quot;
  end
end

class Report
  prepend Formatter
  prepend Validator

  def display
    &quot;Report content&quot;
  end
end

report = Report.new
puts report.display # =&gt; &quot;Validated: Formatted: Report content&quot;
puts Report.ancestors # =&gt; [Validator, Formatter, Report, Object, Kernel, BasicObject]
</code></pre>
<p>When there are multiple prepends, the first lookup is from the last prepend.</p>
<p>Hence the lookup flow would be as follows.
<code>prepend(n) -&gt; prepend(n-1) -&gt; class -&gt; include(n) -&gt; include(n-1)</code></p>
<p>Prepend is used to insert a module&#39;s methods at the beginning of a class&#39;s method lookup chain, before the class&#39;s own methods. This means that when a method is called, Ruby will first look for it in the prepended module before looking in the class itself.</p>
]]></content:encoded>
          <pubDate>Thu, 19 Dec 2024 00:00:00 GMT</pubDate>
        </item>
<item>
          <title><![CDATA[Code Syntax Highlighting Showcase]]></title>
          <link>https://yatishmehta.com/blog/code-syntax-showcase</link>
          <guid isPermaLink="true">https://yatishmehta.com/blog/code-syntax-showcase</guid>
          <description><![CDATA[A showcase of syntax highlighting for various programming languages including Go, Python, Ruby, JavaScript, TypeScript, CSS, and SQL.]]></description>
          <content:encoded><![CDATA[<p>I wanted to test the syntax highlighting on my blog, so I decided to share some code snippets from different programming languages.</p>
<p>I&#39;ve used variations of these snippets in real projects.</p>
<h2>Ruby</h2>
<p>It is a single-file Rails application used for testing ActiveRecord methods. It&#39;s based on the official Rails issue template, but I&#39;ve modified it to use PostgreSQL as the database. There are also <a href="https://guides.rubyonrails.org/contributing_to_ruby_on_rails.html#create-an-executable-test-case">templates</a> available to test other parts of a Rails application.</p>
<pre><code class="language-ruby"># frozen_string_literal: true

require &quot;bundler/inline&quot;

gemfile(true) do
  source &quot;https://rubygems.org&quot;

  gem &quot;rails&quot;
  gem &quot;pg&quot;
end

require &quot;active_record&quot;
require &quot;logger&quot;

ActiveRecord::Base.establish_connection(
  adapter: &quot;postgresql&quot;,
  database: &quot;postgres&quot;,
  host: &quot;localhost&quot;,
  username: &quot;postgres&quot;,
  password: &quot;&quot;,
  port: 5432
)

ActiveRecord::Base.connection.drop_database(&quot;rails_blog_demo&quot;) rescue nil
ActiveRecord::Base.connection.create_database(&quot;rails_blog_demo&quot;)
ActiveRecord::Base.establish_connection(
  adapter: &quot;postgresql&quot;,
  database: &quot;rails_blog_demo&quot;,
  host: &quot;localhost&quot;,
  username: &quot;postgres&quot;,
  password: &quot;&quot;,
  port: 5432
)

ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
    t.string :title
    t.text :content
    t.timestamps
  end

  create_table :comments, force: true do |t|
    t.belongs_to :post, null: false
    t.text :body
    t.timestamps
  end
end

class Post &lt; ActiveRecord::Base
  has_many :comments, dependent: :destroy
end

class Comment &lt; ActiveRecord::Base
  belongs_to :post
end

post = Post.create!(title: &quot;Sample Post&quot;, content: &quot;This is a sample blog post.&quot;)
comment = post.comments.create!(body: &quot;Great post!&quot;)

puts &quot;Post: #{post.title}&quot;
puts &quot;Comment: #{comment.body}&quot;
puts &quot;Post has #{post.comments.count} comments&quot;
</code></pre>
<h2>CSS</h2>
<p>CSS that makes a circle with a colorful gradient that spins, changes color, and animates.</p>
<pre><code class="language-css">.circle {
  width: 150px;
  height: 150px;
  border-radius: 50%;
  background: linear-gradient(45deg, #60a5fa, #a855f7);
  margin: 20px auto;
  animation: 
      rotate 4s linear infinite,
      colorShift 6s ease-in-out infinite,
      bounce 3s ease-in-out infinite;
}

@keyframes rotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes colorShift {
  0% { filter: hue-rotate(0deg) brightness(1); }
  25% { filter: hue-rotate(90deg) brightness(1.2); }
  50% { filter: hue-rotate(180deg) brightness(0.9); }
  75% { filter: hue-rotate(270deg) brightness(1.1); }
  100% { filter: hue-rotate(360deg) brightness(1); }
}

@keyframes bounce {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}
</code></pre>
<style>
{`
.circle {
  width: 150px;
  height: 150px;
  border-radius: 50%;
  background: linear-gradient(45deg, #60a5fa, #a855f7);
  margin: 20px auto;
  animation: 
      rotate 4s linear infinite,
      colorShift 6s ease-in-out infinite,
      bounce 3s ease-in-out infinite;
}

@keyframes rotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes colorShift {
  0% { filter: hue-rotate(0deg) brightness(1); }
  25% { filter: hue-rotate(90deg) brightness(1.2); }
  50% { filter: hue-rotate(180deg) brightness(0.9); }
  75% { filter: hue-rotate(270deg) brightness(1.1); }
  100% { filter: hue-rotate(360deg) brightness(1); }
}

@keyframes bounce {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}
`}
</style>

<div className="circle" />


<h2>Go</h2>
<p>A Go program that parses IPv4 and IPv6 addresses.</p>
<pre><code class="language-go">package main

import (
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;net&quot;
)

func main() {
	ipv4Addr, ipv4Net, err := net.ParseCIDR(&quot;192.0.2.1/24&quot;)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(ipv4Addr)
	fmt.Println(ipv4Net)

	ipv6Addr, ipv6Net, err := net.ParseCIDR(&quot;2001:db8:a0b:12f0::1/32&quot;)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(ipv6Addr)
	fmt.Println(ipv6Net)
}
</code></pre>
<h2>JavaScript/TypeScript</h2>
<p>A simple Rock Paper Scissors game built with React where you can play against the computer:</p>
<RockPaperScissors />

<pre><code class="language-typescript">// RockPaperScissors.tsx
import React, { useState } from &#39;react&#39;;

const choices = [&#39;Rock&#39;, &#39;Paper&#39;, &#39;Scissors&#39;] as const;
type Choice = typeof choices[number];

const getWinner = (player: Choice, cpu: Choice) =&gt; {
  if (player === cpu) return &quot;It&#39;s a tie!&quot;;
  if (
    (player === &#39;Rock&#39; &amp;&amp; cpu === &#39;Scissors&#39;) ||
    (player === &#39;Paper&#39; &amp;&amp; cpu === &#39;Rock&#39;) ||
    (player === &#39;Scissors&#39; &amp;&amp; cpu === &#39;Paper&#39;)
  ) return &#39;You win!&#39;;
  return &#39;You lose!&#39;;
};

export default function RockPaperScissors() {
  const [player, setPlayer] = useState&lt;Choice | null&gt;(null);
  const [cpu, setCpu] = useState&lt;Choice | null&gt;(null);
  const [result, setResult] = useState(&#39;&#39;);

  const play = (choice: Choice) =&gt; {
    const cpuChoice = choices[Math.floor(Math.random() * 3)];
    setPlayer(choice);
    setCpu(cpuChoice);
    setResult(getWinner(choice, cpuChoice));
  };

  return (
    &lt;div style={{ textAlign: &#39;center&#39;, fontFamily: &#39;sans-serif&#39; }}&gt;
      &lt;h1&gt;Rock Paper Scissors&lt;/h1&gt;
      {choices.map(c =&gt; (
        &lt;button key={c} onClick={() =&gt; play(c)} style={{ margin: &#39;0 10px&#39; }}&gt;
          {c}
        &lt;/button&gt;
      ))}
      {player &amp;&amp; cpu &amp;&amp; (
        &lt;div&gt;
          &lt;p&gt;You chose: {player}&lt;/p&gt;
          &lt;p&gt;Computer chose: {cpu}&lt;/p&gt;
          &lt;h2&gt;{result}&lt;/h2&gt;
        &lt;/div&gt;
      )}
    &lt;/div&gt;
  );
}
</code></pre>
<h2>SQL</h2>
<p>SQL query to find the management chain of a user:</p>
<pre><code class="language-sql">CREATE TABLE users (id SERIAL PRIMARY KEY,
                              name TEXT, manager_id INT REFERENCES users(id));

WITH RECURSIVE management_chain AS
  (SELECT id,
          name,
          manager_id,
          1 AS LEVEL
   FROM users
   WHERE id = 7
   UNION ALL SELECT u.id,
                    u.name,
                    u.manager_id,
                    mc.level + 1
   FROM users u
   JOIN management_chain mc ON u.id = mc.manager_id)
SELECT *
FROM management_chain
ORDER BY LEVEL;
</code></pre>
]]></content:encoded>
          <pubDate>Fri, 12 Apr 2024 00:00:00 GMT</pubDate>
        </item>
    </channel>
  </rss>