<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://orimarash.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://orimarash.com/" rel="alternate" type="text/html" /><updated>2026-06-02T12:25:37+00:00</updated><id>https://orimarash.com/feed.xml</id><title type="html">Ori Marash</title><subtitle>Ori Marash is a programmer. Write-ups on Rails, productivity systems, AI, and side projects.</subtitle><author><name>Ori Marash</name></author><entry><title type="html">Turbo Lazy-Loading Scroll — How 37Signals do it</title><link href="https://orimarash.com/turbo-lazy-loading-scroll-how-37signals-do-it/" rel="alternate" type="text/html" title="Turbo Lazy-Loading Scroll — How 37Signals do it" /><published>2025-10-03T09:23:22+00:00</published><updated>2025-10-03T09:23:22+00:00</updated><id>https://orimarash.com/turbo-lazy-loading-scroll-how-37signals-do-it</id><content type="html" xml:base="https://orimarash.com/turbo-lazy-loading-scroll-how-37signals-do-it/"><![CDATA[<p>Inside Campfire, I prompted cursor “How do we do infinite loading in the account users display?” Here’s the result:</p>

<p>The infinite loading for account users is implemented using Turbo Streams and lazy-loading Turbo Frames with the <code class="language-plaintext highlighter-rouge">geared_pagination</code> gem. Here’s the complete flow:</p>

<h3 id="1-controller-setup-appcontrollersaccountsusers_controllerrb">1. <strong>Controller Setup</strong> (<code class="language-plaintext highlighter-rouge">app/controllers/accounts/users_controller.rb</code>)</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">index</span>
  <span class="n">set_page_and_extract_portion_from</span> <span class="no">User</span><span class="p">.</span><span class="nf">active</span><span class="p">.</span><span class="nf">ordered</span><span class="p">,</span> <span class="ss">per_page: </span><span class="mi">500</span>
<span class="k">end</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">set_page_and_extract_portion_from</code> method comes from the <code class="language-plaintext highlighter-rouge">geared_pagination</code> gem (version 1.2.0) and creates a <code class="language-plaintext highlighter-rouge">@page</code> object with:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">@page.records</code> - the current batch of users</li>
  <li><code class="language-plaintext highlighter-rouge">@page.last?</code> - whether this is the last page</li>
  <li><code class="language-plaintext highlighter-rouge">@page.next_param</code> - the parameter for the next page</li>
</ul>

<h3 id="2-initial-display-appviewsaccountsedithtmlerb">2. <strong>Initial Display</strong> (<code class="language-plaintext highlighter-rouge">app/views/accounts/edit.html.erb</code>)</h3>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;turbo-frame</span> <span class="na">id=</span><span class="s">"account_users"</span><span class="nt">&gt;</span>
  <span class="cp">&lt;%=</span> <span class="n">render</span> <span class="ss">partial: </span><span class="s2">"accounts/users/user"</span><span class="p">,</span> <span class="ss">collection: </span><span class="vi">@page</span><span class="p">.</span><span class="nf">records</span><span class="p">,</span> <span class="ss">as: :user</span> <span class="cp">%&gt;</span>
  <span class="cp">&lt;%=</span> <span class="n">render</span> <span class="s2">"accounts/users/next_page_container"</span><span class="p">,</span> <span class="ss">page: </span><span class="vi">@page</span><span class="p">.</span><span class="nf">next_param</span> <span class="k">unless</span> <span class="vi">@page</span><span class="p">.</span><span class="nf">last?</span> <span class="cp">%&gt;</span>
<span class="nt">&lt;/turbo-frame&gt;</span>
</code></pre></div></div>

<p>This renders:</p>

<ul>
  <li>The first batch of users (up to 500 per page)</li>
  <li>A “next page container” if there are more users to load</li>
</ul>

<h3 id="3-lazy-loading-container-appviewsaccountsusers_next_page_containerhtmlerb">3. <strong>Lazy Loading Container</strong> (<code class="language-plaintext highlighter-rouge">app/views/accounts/users/_next_page_container.html.erb</code>)</h3>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">turbo_frame_tag</span> <span class="ss">:next_page_container</span><span class="p">,</span> <span class="ss">loading: :lazy</span><span class="p">,</span>
      <span class="ss">src: </span><span class="n">account_users_path</span><span class="p">(</span><span class="ss">page: </span><span class="n">page</span><span class="p">,</span> <span class="ss">format: :turbo_stream</span><span class="p">),</span> <span class="ss">class: </span><span class="s2">"flex center"</span> <span class="k">do</span> <span class="cp">%&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"spinner center"</span><span class="nt">&gt;&lt;/div&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>This creates a <strong>lazy-loading Turbo Frame</strong> that:</p>

<ul>
  <li>Shows a spinner initially</li>
  <li>Automatically loads when it comes into the viewport (<code class="language-plaintext highlighter-rouge">loading: :lazy</code>)</li>
  <li>Makes a request to <code class="language-plaintext highlighter-rouge">account_users_path</code> with the next page parameter</li>
  <li>Requests the response in <code class="language-plaintext highlighter-rouge">turbo_stream</code> format</li>
</ul>

<h3 id="4-turbo-stream-response-appviewsaccountsusersindexturbo_streamerb">4. <strong>Turbo Stream Response</strong> (<code class="language-plaintext highlighter-rouge">app/views/accounts/users/index.turbo_stream.erb</code>)</h3>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">turbo_stream</span><span class="p">.</span><span class="nf">replace</span> <span class="ss">:next_page_container</span><span class="p">,</span> <span class="ss">partial: </span><span class="s2">"accounts/users/user"</span><span class="p">,</span> <span class="ss">collection: </span><span class="vi">@page</span><span class="p">.</span><span class="nf">records</span><span class="p">,</span> <span class="ss">as: :user</span> <span class="cp">%&gt;</span>

<span class="cp">&lt;%</span> <span class="k">unless</span> <span class="vi">@page</span><span class="p">.</span><span class="nf">last?</span> <span class="cp">%&gt;</span>
  <span class="cp">&lt;%=</span> <span class="n">turbo_stream</span><span class="p">.</span><span class="nf">append</span> <span class="ss">:account_users</span><span class="p">,</span> <span class="ss">partial: </span><span class="s2">"accounts/users/next_page_container"</span><span class="p">,</span> <span class="ss">locals: </span><span class="p">{</span> <span class="ss">page: </span><span class="vi">@page</span><span class="p">.</span><span class="nf">next_param</span> <span class="p">}</span> <span class="cp">%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>When the lazy frame loads, this:</p>

<ol>
  <li><strong>Replaces</strong> the spinner with the new batch of users</li>
  <li><strong>Appends</strong> a new “next page container” to the bottom (if there are more pages)</li>
</ol>

<h3 id="5-how-the-infinite-loading-works">5. <strong>How the Infinite Loading Works</strong></h3>

<ol>
  <li><strong>Initial Load</strong>: First 500 users are displayed with a lazy-loading frame at the bottom</li>
  <li><strong>Scroll Trigger</strong>: When user scrolls near the bottom, the lazy frame enters the viewport</li>
  <li><strong>Automatic Request</strong>: Turbo automatically makes a request for the next page</li>
  <li><strong>Stream Update</strong>: The response replaces the spinner with new users and adds another lazy frame</li>
  <li><strong>Repeat</strong>: Process continues until all users are loaded (<code class="language-plaintext highlighter-rouge">@page.last?</code> is true)</li>
</ol>

<h3 id="key-benefits-of-this-approach">Key Benefits of This Approach:</h3>

<ul>
  <li><strong>No [Extra] JavaScript Required</strong>: Uses native browser intersection observer through Turbo’s lazy loading [This isn’t quite right. Turbo streams uses Javascript. But there definitely is the benefit that you don’t have to write any <em>custom</em> javascript]</li>
  <li><strong>Seamless UX</strong>: Users see a spinner briefly, then new content appears automatically</li>
  <li><strong>Performance</strong>: Large page size (500 users) reduces the number of requests</li>
  <li><strong>Progressive Enhancement</strong>: Works without JavaScript (would fall back to regular pagination)</li>
  <li><strong>Accessibility</strong>: Maintains proper focus management and screen reader compatibility</li>
</ul>

<p>This implementation is elegant because it leverages Turbo’s built-in lazy loading capabilities rather than requiring custom JavaScript for scroll detection and AJAX requests.</p>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[Inside Campfire, I prompted cursor "How do we do infinite loading in the account users display?" Here's the result: The infinite loading for account users is implemented using Turbo Streams and…]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/turbo-lazy-loading-scroll-how-37signals-do-it/feature.jpg" /><media:content medium="image" url="https://orimarash.com/assets/posts/turbo-lazy-loading-scroll-how-37signals-do-it/feature.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">A modern Amazon Mechanical Turk Requester UI</title><link href="https://orimarash.com/a-modern-amazon-mechanical-turk-requester-ui/" rel="alternate" type="text/html" title="A modern Amazon Mechanical Turk Requester UI" /><published>2025-01-27T13:03:04+00:00</published><updated>2025-01-27T13:03:04+00:00</updated><id>https://orimarash.com/a-modern-amazon-mechanical-turk-requester-ui</id><content type="html" xml:base="https://orimarash.com/a-modern-amazon-mechanical-turk-requester-ui/"><![CDATA[<p>At FrameSports we needed some data entry done quickly, so we turned to Amazon’s Mechanical Turk. I wanted to check that our hundreds of tasks were going through the system properly and that the system at large worked as I expected. But I couldn’t find a user interface that let me do that, so I build my own.</p>

<p>Send me an email if you want access to this:</p>

<ul>
  <li>First, you add your MTurk AWS credentials to the system. You can manage multiple MTurk accounts and sandbox accounts.</li>
  <li>You can then view all your HITs, their assignments, statuses, etc.</li>
</ul>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[At FrameSports we needed some data entry done quickly, so we turned to Amazon's Mechanical Turk. I wanted to check that our hundreds of tasks were going through the system properly and that the…]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/a-modern-amazon-mechanical-turk-requester-ui/feature.jpg" /><media:content medium="image" url="https://orimarash.com/assets/posts/a-modern-amazon-mechanical-turk-requester-ui/feature.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Advent of Code 2024 - Day 2: Red-Nosed Reports</title><link href="https://orimarash.com/advent-of-code-2024-day-2-2/" rel="alternate" type="text/html" title="Advent of Code 2024 - Day 2: Red-Nosed Reports" /><published>2024-12-02T23:26:16+00:00</published><updated>2024-12-02T23:26:16+00:00</updated><id>https://orimarash.com/advent-of-code-2024-day-2-2</id><content type="html" xml:base="https://orimarash.com/advent-of-code-2024-day-2-2/"><![CDATA[<p>Day 2 of the Advent of Code coding challenge.</p>

<p>Restated problems:</p>

<ol>
  <li>You are given a list of “records”. Each record is a list of numbers (called “levels”). How many of the records follow both these conditions?
    <ol>
      <li>Monotonic (always increasing or always decreasing)</li>
      <li>Levels change within a range 1-3</li>
    </ol>
  </li>
  <li>Same as 1. but you can remove one number from the record before checking the conditions.</li>
</ol>

<p>Solution:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">each_way_to_remove_one_level</span><span class="p">(</span><span class="n">original_arr</span><span class="p">)</span>
  <span class="n">original_arr</span><span class="p">.</span><span class="nf">map</span><span class="p">.</span><span class="nf">with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">_</span><span class="p">,</span> <span class="n">index</span><span class="o">|</span>
    <span class="n">original_arr</span><span class="p">.</span><span class="nf">dup</span><span class="p">.</span><span class="nf">tap</span> <span class="p">{</span> <span class="n">_1</span><span class="p">.</span><span class="nf">delete_at</span><span class="p">(</span><span class="n">index</span><span class="p">)</span> <span class="p">}</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">monotonic?</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
  <span class="n">record</span><span class="p">.</span><span class="nf">each_cons</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">|</span> <span class="n">a</span> <span class="o">&lt;=&gt;</span> <span class="n">b</span> <span class="p">}.</span><span class="nf">uniq</span><span class="p">.</span><span class="nf">count</span> <span class="o">==</span> <span class="mi">1</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">small_distances?</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
  <span class="n">record</span><span class="p">.</span><span class="nf">each_cons</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="nf">all?</span> <span class="k">do</span> <span class="o">|</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">|</span>
    <span class="p">(</span><span class="n">a</span> <span class="o">-</span> <span class="n">b</span><span class="p">).</span><span class="nf">abs</span><span class="p">.</span><span class="nf">between?</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">safe?</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
  <span class="n">monotonic?</span><span class="p">(</span><span class="n">record</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">small_distances?</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="k">end</span>

<span class="k">def</span> <span class="nf">read_records_from_stdin</span>
  <span class="no">ARGF</span><span class="p">.</span><span class="nf">read</span><span class="p">.</span><span class="nf">lines</span><span class="p">.</span><span class="nf">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
    <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:to_i</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="c1"># Level 1</span>
<span class="nb">puts</span> <span class="n">read_records_from_stdin</span><span class="p">.</span><span class="nf">count</span> <span class="p">{</span> <span class="o">|</span><span class="n">record</span><span class="o">|</span> <span class="n">safe?</span><span class="p">(</span><span class="n">record</span><span class="p">)</span> <span class="p">}</span>

<span class="c1"># Level 2</span>
<span class="nb">puts</span> <span class="n">read_records_from_stdin</span><span class="p">.</span><span class="nf">count</span> <span class="p">{</span> <span class="o">|</span><span class="n">record</span><span class="o">|</span>
  <span class="n">each_way_to_remove_one_level</span><span class="p">(</span><span class="n">record</span><span class="p">).</span><span class="nf">any?</span> <span class="p">{</span> <span class="o">|</span><span class="n">record</span><span class="o">|</span> <span class="n">safe?</span><span class="p">(</span><span class="n">record</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Ori Marash</name></author><category term="Advent of Code 2024" /><summary type="html"><![CDATA[Day 2 of the Advent of Code coding challenge. Restated problems: 1. You are given a list of "records". Each record is a list of numbers (called "levels"). How many of the records follow both these…]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/advent-of-code-2024-day-2-2/feature.jpg" /><media:content medium="image" url="https://orimarash.com/assets/posts/advent-of-code-2024-day-2-2/feature.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Academorphic – automated literature reviews</title><link href="https://orimarash.com/academorphic-automated-literature-reviews/" rel="alternate" type="text/html" title="Academorphic – automated literature reviews" /><published>2024-07-26T11:11:18+00:00</published><updated>2024-07-26T11:11:18+00:00</updated><id>https://orimarash.com/academorphic-automated-literature-reviews</id><content type="html" xml:base="https://orimarash.com/academorphic-automated-literature-reviews/"><![CDATA[<p>Academorphic, a tool for university students working on their dissertation, is designed to simplify the process of writing literature reviews. It’s a fork of Morphic (<a href="https://github.com/miurla/morphic">GitHub</a>). Academorphic gathers, summarises, and answers questions about academic papers, then generates a PDF literature review for you.</p>

<p>I built it as part of the Bath Hack 2024 with my two teammates: Arnau Ayerbe and Varniethan Ketheeswaran.</p>

<h2 id="features"><strong>Features</strong></h2>

<p><strong>Title-Based Search on ArXiv</strong>: Academorphic starts by asking for the title of your dissertation or research paper. Using this title, it searches the ArXiv database, a repository of pre-print papers, to find relevant literature.</p>

<p><img src="/assets/posts/academorphic-automated-literature-reviews/img-0.png" alt="" /></p>

<p><strong>Summarization and Analysis</strong>: Once the relevant papers are found, Academorphic summarizes the key points of each paper. It then uses Retrieval-Augmented Generation (RAG) to answer specific questions about the literature.</p>

<p><img src="/assets/posts/academorphic-automated-literature-reviews/img-1.png" alt="" /></p>

<p><strong>Interactive Follow-Up Queries</strong>: If the title you provide is too vague or broad, Academorphic prompts you with follow-up questions to narrow down the search.</p>

<p><img src="/assets/posts/academorphic-automated-literature-reviews/img-2.png" alt="" /></p>

<p><strong>PDF Generation</strong>: After compiling and analyzing the relevant literature, Academorphic allows you to generate a PDF of your literature review with a click.</p>

<p><img src="/assets/posts/academorphic-automated-literature-reviews/img-3.png" alt="" /></p>

<p>We used a new React library made by Vercel to build the UI. It lets you directly connect streamed LLM responses to React components, so you get snappy responses. Here’s a link:</p>

<p><a href="https://vercel.com/blog/ai-sdk-3-generative-ui">Introducing AI SDK 3.0 with Generative UI support – Vercel</a></p>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[Academorphic, a tool for university students working on their dissertation, is designed to simplify the process of writing literature reviews. It's a fork of Morphic (GitHub). Academorphic…]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/academorphic-automated-literature-reviews/feature.png" /><media:content medium="image" url="https://orimarash.com/assets/posts/academorphic-automated-literature-reviews/feature.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Partitions of a set in Ruby</title><link href="https://orimarash.com/partitions-of-a-set-in-ruby/" rel="alternate" type="text/html" title="Partitions of a set in Ruby" /><published>2024-07-11T16:44:39+00:00</published><updated>2024-07-11T16:44:39+00:00</updated><id>https://orimarash.com/partitions-of-a-set-in-ruby</id><content type="html" xml:base="https://orimarash.com/partitions-of-a-set-in-ruby/"><![CDATA[<p>I needed an implementation for generating partitions of a set for <a href="https://framesports.ai/">FrameSports.ai</a> (long story) and couldn’t find a working implementation online. I wrote an implementation based on the <a href="https://www.geeksforgeeks.org/generate-all-partition-of-a-set/">Geeks for Geeks article on partitions of a set</a>.</p>

<script src="https://gist.github.com/penguoir/0cdd24c8d4a19f45c346f82ec02563f8.js"></script>

<p>And a direct link to Gist in case the embed doesn’t work:</p>

<p><a href="https://gist.github.com/penguoir/0cdd24c8d4a19f45c346f82ec02563f8">partition.rb</a></p>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[I needed an implementation for generating partitions of a set for FrameSports.ai (long story) and couldn't find a working implementation online]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/partitions-of-a-set-in-ruby/feature.jpg" /><media:content medium="image" url="https://orimarash.com/assets/posts/partitions-of-a-set-in-ruby/feature.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Trays - A faithful GTD implementation</title><link href="https://orimarash.com/trays-a-faithful-gtd-implementation/" rel="alternate" type="text/html" title="Trays - A faithful GTD implementation" /><published>2024-03-17T22:26:59+00:00</published><updated>2024-03-17T22:26:59+00:00</updated><id>https://orimarash.com/trays-a-faithful-gtd-implementation</id><content type="html" xml:base="https://orimarash.com/trays-a-faithful-gtd-implementation/"><![CDATA[<iframe width="200" height="113" src="https://www.youtube.com/embed/VNHILyBC4Dc?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Trays"></iframe>

<p>I recorded this demo video about six months ago showing my ideal todo system. Some key features:</p>

<ul>
  <li><strong>Capture:</strong> When you type new things into the system, you don’t see anything else. This lets you “brain-dump” without seeing anything in the system: useful!</li>
  <li><strong>Process</strong>: You only see one inbox item at a time. This forces you to process every item in your mind so things don’t slip between the cracks.</li>
  <li><strong>Project page:</strong> A little notes section helps you keep track of the project resources without being a full-fledged notebook. Link to Notion or another note-taking tool here. Or, for smaller projects, don’t type anything at all!</li>
  <li><strong>Incubating + Waiting For</strong>: Delay tasks until a date, and you don’t see the project until that date arrives. Not quite a “start date” because the project might have already started but you just need to think about it for a few days. It’s also not a “due” date because nothing needs to happen on that day. Some use cases:
    <ul>
      <li>I need to sleep on this decision</li>
      <li>I’ve got too much on my plate and don’t want to think about this for the next few weeks</li>
      <li>I’m burned out on this project so I’ll rest for a few days</li>
    </ul>
  </li>
  <li><strong>Don’t forget a next action:</strong> Projects without a defined next step are highlighted as such, nudging you to add a task. And tasks can belong to multiple projects. When projects have clear next actions, it’s super easy to push them forward.</li>
  <li><strong>Next actions page:</strong> When you sit down to work, you just look at the next actions page. Not a massive project/action/context window, just a list of things to do.</li>
</ul>

<p>If you’d like to use this sort of tool, let me know and I’ll set it up properly!</p>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[I recorded this demo video about six months ago showing my ideal todo system. Some key features: * Capture: When you type new things into the system, you don't see anything else. This lets you…]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/trays-a-faithful-gtd-implementation/feature.jpg" /><media:content medium="image" url="https://orimarash.com/assets/posts/trays-a-faithful-gtd-implementation/feature.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">ActiveCortex</title><link href="https://orimarash.com/activecortex/" rel="alternate" type="text/html" title="ActiveCortex" /><published>2024-02-16T14:47:36+00:00</published><updated>2024-02-16T14:47:36+00:00</updated><id>https://orimarash.com/activecortex</id><content type="html" xml:base="https://orimarash.com/activecortex/"><![CDATA[<p><strong>ActiveCortex is a Ruby gem that makes it faster to integrate OpenAI with Ruby on Rails applications.</strong></p>

<p>Ruby on Rails is a framework for building web apps. It’s been around for 20 years and has big names like Shopify and Github using it. It’s also my favourite way to build products.</p>

<p>One of my clients that uses Rails also wanted to integrate with OpenAI’s ChatGPT. They make many calls to ChatGPT across the application for various use cases. Typically, standard programming techniques mean I don’t have to re-write the integration each time; I can reuse the same code for each integration.</p>

<p>But, specifically for ChatGPT, I noticed that I couldn’t really make this work. The reused code was either too specific or to general. When it was too specific, I couldn’t reuse the code across the integrations. When it was too general, the resulting code was ugly. I think this happened because the client has varying use cases for ChatGPT, so it’s hard to generalise appropriately.</p>

<p>I noticed that some of my other projects–such as my dissertation–would also benefit from using this generalised code. I talked with the client about extracting my work and publishing it as an open-source project. That way, I could spend more time coding without overcharging them; they agreed. The result is the ActiveCortex package.</p>

<p>From anecdotal experience, using ActiveCortex cuts down on about 150 lines of code. But I think the best benefit is how neat the new code is!</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Document
  ai_generated :summary
end
</code></pre></div></div>

<p>That’s all the code you need to automatically generate a summary for a document using OpenAI.</p>

<p>I’m hoping to add more features to this package, such as:</p>

<ul>
  <li>Function calls</li>
  <li>Generating images</li>
  <li>Using images in a prompt</li>
</ul>

<p>Check out the code and more examples here: <a href="https://github.com/penguoir/active_cortex">https://github.com/penguoir/active_cortex</a></p>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[ActiveCortex is a Ruby gem that makes it faster to integrate OpenAI with Ruby on Rails applications. Ruby on Rails is a framework for building web apps. It's been around for 20 years and has big…]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/activecortex/feature.webp" /><media:content medium="image" url="https://orimarash.com/assets/posts/activecortex/feature.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Just look at their code</title><link href="https://orimarash.com/just-look-at-their-code/" rel="alternate" type="text/html" title="Just look at their code" /><published>2024-01-15T18:35:14+00:00</published><updated>2024-01-15T18:35:14+00:00</updated><id>https://orimarash.com/just-look-at-their-code</id><content type="html" xml:base="https://orimarash.com/just-look-at-their-code/"><![CDATA[<p>One of my clients was searching to hire another developer. I was responsible for one of the steps in this hiring process: I verified the candidate could write good code. Through this process, I developed my own preferred way to test candidates.</p>

<p>I was only responsible for verifying that the candidate could write good code. The client was responsible for everything else, like making sure the candidate wasn’t an arse, could speak English, could legally work for the company, etc. Another essential skill I was not testing is whether the developer could independently transform a loose design document into an implementation plan. This also was handled earlier in the hiring pipeline.</p>

<p>At first, I checked whether candidates could write good code by conducting a technical interview. All I did in this interview was ask them technical questions. I’d make sure they know about model-view-controller architecture, make sure they could use Git (ask me why!), and test-driven development. After an interview finished, I’d usually have a low-confidence rating for the candidate. My rating was low confidence because I could only base my confidence on what they <em>said</em>.</p>

<p>I eventually asked one candidate, “Can you <em>show</em> me some code you’ve worked on and talk me through it?” They shared their code, explained its context, and walked me through the constraints they were working under while writing it. We then had a fascinating discussion that clearly revealed the candidates’ opinions on what good and bad code looked like.</p>

<p>From then on, I conducted all my technical interviews using the golden question: “Can you show me some of your code?”</p>

<p>I didn’t mind if the code they showed me looked ugly. I only cared whether their internal classifier for good/bad code was calibrated.</p>

<p>Some candidates didn’t have much to say about their code, so I found a good follow-up question, “If you had time to re-write this function, how would you do it?”</p>

<p>I asked them to show me their code rather than show them some of my own because, as a developer, it isn’t necessary to look at code and quickly understand what it’s doing. It’s okay to take time to get acquainted with the system. I was worried that some developers wouldn’t have any side projects, but all of them did. On second thought, I don’t think a developer should apply for any role until they’ve written <em>some</em> code, so this makes sense.</p>

<p>At some point, the client wanted me to spend less time on interviews so I could divert more time to other priorities. Instead of setting up interviews, the client directly asked candidates to share some code they’d written and forwarded it to me. Although this was a faster method for assessing candidates, I lacked some of the context that often influences the code’s elegance, which lowered my confidence in my ratings.</p>

<p>One thing this approach doesn’t measure is the overall competency of a developer, which includes proficiency in business, product, and soft skills. For example, if the code they’re writing is entirely unnecessary (but clean), they’ll pass the “show me your code” test, even though they aren’t a competent developer. Competent developers don’t write unnecessary code. So, this test is great for measuring the developer’s coding style rather than their overall software engineering.</p>

<p>I want to mention that, of course, I’m influenced by many biases when using this approach to assess candidates. For example, I will likely give a higher score to a developer who writes code similar to mine. My code is neat, but I’m not perfect, so this assessment might return false positives for programming geniuses whose style differs from mine.</p>

<p>I actually stole this question from <a href="https://www.linkedin.com/in/andrejusk/">Andrejus K</a>, who interviewed me a few years ago using this approach. Other than that one interview, I haven’t ever been asked this question. I don’t see why it’s not used more often in the industry. Please email me if you know!</p>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[One of my clients was searching to hire another developer. I was responsible for one of the steps in this hiring process: I verified the candidate could write good code. Through this process, I…]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/just-look-at-their-code/feature.png" /><media:content medium="image" url="https://orimarash.com/assets/posts/just-look-at-their-code/feature.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">RailsGPT</title><link href="https://orimarash.com/railsgpt/" rel="alternate" type="text/html" title="RailsGPT" /><published>2024-01-07T17:13:35+00:00</published><updated>2024-01-07T17:13:35+00:00</updated><id>https://orimarash.com/railsgpt</id><content type="html" xml:base="https://orimarash.com/railsgpt/"><![CDATA[<p>I’ve been playing around with using ChatGPT to answer my Rails-related questions. But I find that it often runs into problems. I decided to create a custom GPT to avoid them.</p>

<p>The problems are:</p>

<ul>
  <li>ChatGPT often responds in a long tutorial-style format when really all I want is a short response.</li>
  <li>ChatGPT relies on using tutorials, guides, blog posts. These are second-hand information so they are sometimes outdated, misguided or just incorrect.</li>
  <li>ChatGPT doesn’t have a great grasp of Rails internals. Especially bits that nobody else has written about. This makes it harder to build on lesser-known Rails functionality.</li>
</ul>

<p>To combat these problems, I’ve taken the following steps:</p>

<ul>
  <li>Prompted GPT to answer questions in the style I like.</li>
  <li>Upload all the Rails codebase into its “Knowledge”, so it can always base its answers off the latest Rails code.</li>
</ul>

<p>I’ve pasted the full prompt below if you’d like to have a read.</p>

<p>OpenAI put a limit on the number of files that users can upload to custom GPTs, so I wrote a script that compresses the Rails codebase into a few files. I’ve pasted the script below. Although, I’m not entirely sure whether I managed to get the GPT to actually reference these files or whether it’s just using its existing knowledge of Rails.</p>

<p>Please email me if you have ideas for improving the GPT. And I hope you find this useful!</p>

<p>Here’s a link to <a href="https://chat.openai.com/gpts/editor/g-qrdir5FMK">RailsGPT, the custom GPT for your Rails questions.</a></p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Copy every file from ../rails, recursively, to the current directory.</span>
<span class="c1"># Rename the file to just the library it's in.</span>
<span class="c1">#</span>
<span class="c1">#   /actionpack/lib/action_controller.rb</span>
<span class="c1">#</span>
<span class="c1">#   to</span>
<span class="c1">#</span>
<span class="c1">#   actionpack.rb</span>

<span class="nb">require</span> <span class="s1">'fileutils'</span>

<span class="no">Dir</span><span class="p">.</span><span class="nf">glob</span><span class="p">(</span><span class="s1">'../rails/**/*'</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span>
  <span class="k">next</span> <span class="k">if</span> <span class="no">File</span><span class="p">.</span><span class="nf">directory?</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>

  <span class="c1"># Skip test files</span>
  <span class="k">next</span> <span class="k">if</span> <span class="n">file</span> <span class="o">=~</span> <span class="sr">/_test\.rb$/</span>

  <span class="c1"># Skip files that are not ruby</span>
  <span class="k">next</span> <span class="k">unless</span> <span class="n">file</span> <span class="o">=~</span> <span class="sr">/\.rb$/</span>

  <span class="c1"># Skip files that are not in the lib directory</span>
  <span class="k">next</span> <span class="k">unless</span> <span class="n">file</span> <span class="o">=~</span> <span class="sr">/\/lib\//</span>

  <span class="c1"># Skip fi the file is less than N lines</span>
  <span class="k">next</span> <span class="k">if</span> <span class="no">File</span><span class="p">.</span><span class="nf">readlines</span><span class="p">(</span><span class="n">file</span><span class="p">).</span><span class="nf">size</span> <span class="o">&lt;</span> <span class="mi">200</span>

  <span class="c1"># Append the file to the library name</span>
  <span class="c1"># e.g.</span>
  <span class="c1"># /rails/actionpack/lib/action_controller.rb</span>
  <span class="c1"># goes to</span>
  <span class="c1"># actionpack.rb</span>
  <span class="n">library_name</span> <span class="o">=</span> <span class="n">file</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)[</span><span class="mi">2</span><span class="p">]</span>

  <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">library_name</span> <span class="o">+</span> <span class="s1">'.rb'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"</span><span class="se">\n\n</span><span class="s2">"</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"# </span><span class="si">#{</span><span class="n">file</span><span class="si">}</span><span class="s2">"</span>

    <span class="n">f</span><span class="p">.</span><span class="nf">puts</span> <span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h4 id="full-prompt">Full Prompt</h4>

<p>“RailsGPT” is an expert GPT in Ruby on Rails internals, designed for professionals. Your role extends beyond just answering questions. You should also identify and address potential underlying problems in the user’s approach. In complex situations, rather than providing a direct fix, suggest alternative ways to structure programs to prevent the problem from occurring in the first place. Provide relevant Rails internal source code where relevant. Assume high user expertise. When faced with ambiguous queries, seek clarification to offer precise, context-relevant advice. Your expertise should guide users towards solutions that embody the principles and best practices of Ruby on Rails.</p>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[I've been playing around with using ChatGPT to answer my Rails-related questions. But I find that it often runs into problems. I decided to write a custom GPT to avoid them.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/railsgpt/feature.jpg" /><media:content medium="image" url="https://orimarash.com/assets/posts/railsgpt/feature.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">I emailed notes to the future 253 times over the past two years, here’s what I learned</title><link href="https://orimarash.com/follow-up-then/" rel="alternate" type="text/html" title="I emailed notes to the future 253 times over the past two years, here’s what I learned" /><published>2023-10-09T20:36:52+00:00</published><updated>2023-10-09T20:36:52+00:00</updated><id>https://orimarash.com/follow-up-then</id><content type="html" xml:base="https://orimarash.com/follow-up-then/"><![CDATA[<p>I’ve just looked back at every email I’ve received from followupthen.com. I was surprised to see 253 results, with the first coming back over two years ago! I hadn’t realised how a free tool that sends emails back to you has been a crucial part of my productivity system.</p>

<p>As I process my inbox, I sometimes encounter items that don’t have any immediate actions necessary or possible. Like when I need to wait a couple of days before filling in an application or I need some time to sit on an idea before writing it up. Recently, I’ve been sending these items to Follow Up Then, a service that sends me an email after some time has passed. Anything you send to 3days@followupthen.com gets sent back to you in three days.</p>

<p>Here are some of the emails I’ve received from Follow Up Then (I’m a uni student with a part-time software development job):</p>

<ul>
  <li>“Don’t forget to apply for your university accommodation in one week’s time.”</li>
  <li>“Unsubscribe from this trial before it ends!”</li>
  <li>“Please email back over term time.” No problem, cc: <a href="mailto:october@fut.io">october@fut.io</a></li>
  <li>“Renew your Railcard when it expires in a few months.”</li>
  <li>My professor promised he’d release answers for a problem sheet in a week, and I wanted to remember to check my answers against the correct ones to learn from my mistakes (I made many)</li>
  <li>I had coursework due in a few days but wanted to sleep on my answers before submitting it. “Submit coursework now that you’ve slept on it”</li>
  <li>I set myself a yearly theme and didn’t want to forget to do so next year.</li>
  <li>I rented an Arduino from my school department and had to return it in a few months.</li>
  <li>I wanted to sign up for a society once exam pressure died down (mountaineering!)</li>
  <li>I got an email that included information on what to do when I depart my accommodation, which was happening in one week: <a href="mailto:oneweek@fut.io">oneweek@fut.io</a></li>
  <li>I reached out to an old colleague on Linkedin and wanted to ensure I’d follow up in a few days if he didn’t respond, bcc: <a href="mailto:3days@fut.io">3days@fut.io</a></li>
  <li>I got an out-of-office email and wanted to send a follow-up when they returned.</li>
  <li>“Set up revision schedule.” a few weeks before exams start.</li>
  <li>“Renew passport” sent to <a href="mailto:10years@fut.io">10years@fut.io</a></li>
  <li>“I’ll do that when my exams are over” <a href="mailto:7days@fut.io">7days@fut.io</a></li>
  <li>“Call when X is open after holidays” <a href="mailto:jan@fut.io">jan@fut.io</a></li>
</ul>

<p>These projects are only actionable on a specified date in the future. They’re different from projects with a due date but also different from projects that have no date at all (“someday” projects). The project “finish implementation of X by wednesday” would not fit into FollowUpThen, nor would “learn Russian”, which has no specified start date.</p>

<p>These projects are so crucial that not addressing them would leave me feeling like there’s an ‘open loop’. Follow Up Then closes the loop for me.</p>

<p>The gods of productivity deal with “incubating” projects by keeping them out of sight, out of mind, until their start date arrives. For example, David Allen (last I heard) uses 43 folders to manage these incubating projects. He can send messages to himself in the future, reminding him to start the projects without bulking up his day-to-day system. However, even Allen acknowledges that this approach has limitations, such as being confined to the physical realm.</p>

<p>Follow Up Then achieves that same goal:</p>

<ul>
  <li>I can send notes for the future to start up a project when its time comes.</li>
  <li>I only have to view or think about these notes for the future once their time comes.</li>
</ul>

<p>I have yet to find another system that is as seamless as Follow Up Then for these goals. Please email me if you have found one!</p>

<p>These incubating projects come up all the time. Next time you notice one, try out FollowUpThen and let me know how it went!</p>]]></content><author><name>Ori Marash</name></author><summary type="html"><![CDATA[I've just looked back at every email I've received from followupthen.com. I was surprised to see 253 results, with the first coming back over two years ago! I hadn't realised how a free tool that…]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://orimarash.com/assets/posts/follow-up-then/feature.png" /><media:content medium="image" url="https://orimarash.com/assets/posts/follow-up-then/feature.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>