<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://evencuriouser.net">
	<id>https://evencuriouser.net/feed.xml</id>
	<title>Aron Lebani&apos;s blog</title>
	<link href="https://evencuriouser.net/feed.xml" rel="self" type="application/atom+xml" />
	<link href="https://evencuriouser.net/" rel="alternate" type="text/html" />
	<updated>2026-09-19T09:20:15+10:00</updated>
	<author>
		<name>Aron Lebani</name>
		<uri>https://evencuriouser.net</uri>
	</author>
	
	<entry>
		<id>https://evencuriouser.net/2026/07/thoughts-on-hotwire-native</id>
		<title type="html">Thoughts on Hotwire Native</title>
		<link href="https://evencuriouser.net/2026/07/thoughts-on-hotwire-native.html" rel="alternate" type="text/html" title="" />
		<published>2026-07-31T00:00:00+10:00</published>
		<updated>2026-07-31T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I recently had the opportunity to try out &lt;a href=&quot;https://native.hotwired.dev/&quot;&gt;Hotwire Native&lt;/a&gt; for a
Rails app I’ve been building for one of my clients. It started as a plain old
Rails web app, but eventually the need arose to be able to use it as an iOS
app. As I am the sole developer on this project, we didn’t have the resources to
build and maintain a separate frontend using Swift or React Native or similar.
This made Hotwire Native a natural choice.  I was slightly apprehensive about
using it in a production product given it is still under heavy development, and
has been known to have breaking changes with minor version releases&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. However
given our constraints, it seemed like the best option. While there are many blog
posts describing the merits of Hotwire Native, most of them seemed somewhat
promotional. So I thought I’d describe my own experiences of using it as a
non-iOS developer. Hopefully this is helpful to others who may be considering it
to make an informed decision. It’s worth noting that I haven’t played around with
the Android side yet, so my experiences are based on working with iOS.&lt;/p&gt;

&lt;p&gt;The first thing I noticed when working with Hotwire Native is that the
documentation is not particularly great. The main documentation site gives you a
“getting started” &lt;a href=&quot;https://native.hotwired.dev/ios/getting-started&quot;&gt;overview&lt;/a&gt;, and a &lt;a href=&quot;https://native.hotwired.dev/ios/reference&quot;&gt;reference&lt;/a&gt;
which covers only a subset of what is actually available in the library. There
is also a &lt;a href=&quot;https://hotwire-native-demo.dev/&quot;&gt;demo&lt;/a&gt; for which the source code has been made publicly
available which you can use as a reference. The provided docs are enough to get
a “hello world” project up and running, but you’ll inevitably need more than
that, and for that you’re kind of on your own. I ended up spending a lot of time
digging through forums and Github issues to figure out how to implement what I
would consider to be pretty common usage patterns and features.&lt;/p&gt;

&lt;p&gt;The other big thing is that practically speaking, you need to write a lot more
Swift code than what the pitch sells you. One of the key selling points of
Hotwire Native is that you don’t need much native code, which is true if you
only need a very basic app with minimal native integration. But most real apps
will end up needing a lot more than what Hotwire Native provides out of the box.
By default, the library provides native navigation, a mechanism for the native
code to communicate with the web code, and a mechanism to “progressively
enhance” with fully native components and screens. It also includes a native tab
bar component you can wire up. There are quite a few things which I had to
implement myself including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;saving photos to the device&lt;/li&gt;
  &lt;li&gt;requesting notification permissions&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt;ing notification tokens to the server&lt;/li&gt;
  &lt;li&gt;routing notifications to the correct tab&lt;/li&gt;
  &lt;li&gt;updating the notification badge count&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The work around notifications in particular is poignant because having native
notifications is probably the main reason you would want to build a native app
over say using a PWA.&lt;/p&gt;

&lt;p&gt;Another thing I noticed while working with Hotwire Native is that many things
don’t “just work” as you would expect them to. Even with the tab bar component
provided by Hotwire, you still need to write a bunch of code to get it to work
as you would expect. A good example of this is that by default, all tabs are
pre-loaded when you open the app. This makes changing tabs much quicker. But it
also means that when signing in for the first time, all tabs require you to sign
in to access them as they are loaded prior to authentication. There is an
&lt;a href=&quot;https://github.com/hotwired/hotwire-native-ios/discussions/133&quot;&gt;active discussion&lt;/a&gt; about this.&lt;/p&gt;

&lt;p&gt;This is one of many examples where I had to dig around through forums and write
custom code to get things to work the way I would expect. Some other examples
include hiding the tab bar on the authentication pages and showing it again
after signing in, and changing tabs when following a link to a new tab (by
default it just renders the new view in the same tab). I also had to subclass
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HotwireWebViewController&lt;/code&gt; in order to tweak some settings to prevent
glitches when opening and closing the software keyboard (though that’s more of
an iOS/WKWebView issue than a Hotwire issue).&lt;/p&gt;

&lt;p&gt;All in all it took nearly 400 lines of Swift code and many days of work to get
something that I would consider to be usable. While this isn’t a lot in the
grand scheme of things, it is significant given that the library is targeted at
non-iOS developers. Given the “plug and play” pitch, I do feel like many of
these things should be built into the library. Having said that, I think a lot
of these are hard problems to solve in a generic way, and I understand that the
library can’t provide for every use case. It would be great if at the very least
there were some canonical examples of how to work around these issues in the
documentation. One of the core contributors has a paid Substack and a paid
e-book where they do give many examples of how to implement common usage
patterns. While I appreciate the need to make a living, given the lack of
official documentation it does make it feel like you’re having to pay for what
is essentially documentation that should be freely available.&lt;/p&gt;

&lt;p&gt;Many of the issues I mentioned above are likely due to the project being
relatively young, and I’m sure it will get better over time. It seems that the
developers are actively working on ironing out these kinks, and I’m sure more
and more features will provided over time. But for now it is still quite far
from the promise of being plug and play. Having said that, once you’ve got
it all working you don’t really have to touch it much, and you can go back to
the comfortable and familiar land of Rails. In this regard it does deliver on
it’s promise, it’s just a &lt;em&gt;lot&lt;/em&gt; of work to get there. And whilst it did take a
lot more work than I expected to get it working, it’s still much less work than
building and maintaining a separate frontend.&lt;/p&gt;

&lt;p&gt;My overall experience is that though the project doesn’t quite deliver on it’s
promise, it is certainly a useful tool. And if, like me, you are not an iOS
developer and you need to ship an iOS app to your customers, it’s a pretty good
option. It’s just a matter of adjusting your expectations. Prepare to learn a
lot of Swift and native app development, prepare to spend a lot of time digging
around on forums trying to figure out how to do things, and prepare to spend a
lot of time reading the Hotwire Native source code. Even if it was frustrating
at times, it is very much preferable over maintaining an entire second codebase.
For a sole developer with little experience building native apps, I’d still say
Howtire Native is a good choice. I look forward to seeing where this project
goes. If they can iron out the kinks and make it super easy to implement the
major features and patterns developers need for an mobile app, it has the
potential to become a really great project.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;One of the core developers even recommends pinning it to a minor version. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2026/04/morning-routine</id>
		<title type="html">Morning routine</title>
		<link href="https://evencuriouser.net/2026/04/morning-routine.html" rel="alternate" type="text/html" title="" />
		<published>2026-04-24T00:00:00+10:00</published>
		<updated>2026-04-24T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I’ve recently started seeing a counsellor to help deal with my increasing
feelings of dysphoria about the current state of the world. One of the really
helpful things that has come out of this process is setting up a positive
morning routine for myself. The idea is that having a positive start to the day
would help improve my overall mood throughout the day. Since I love walking,
and live right by the beautiful &lt;a href=&quot;https://en.wikipedia.org/wiki/Darebin_Creek&quot;&gt;Darebin Creek&lt;/a&gt;, I decided to
start going for a walk down the creek each morning before work.&lt;/p&gt;

&lt;p&gt;I aim to leave the house by 7 and go for a roughly 40 minute walk. Then I come
home, boot up my computer, and spend a blissful hour doing whatever I feel like
doing before starting work for the day. Whether it be reading my RSS feed,
tinkering with a personal project, or doing some writing (which is the case
today). During my walks, sometimes I like to practice mindfulness, and
sometimes I like to just let my mind wander&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;I’ve been doing this for about a month now, I can say that it genuinely has
made a big difference. Regular exercise obviously has a big impact on mental
wellbeing. I’ve also had so many amazing experiences on my morning walks. On
warm summery mornings I see the sun beaming through the trees, bathing the
creek and the trees in a golden light. On frosty wintery mornings sometimes
there is a layer of mist over the grass, giving the creek an almost mystical
feeling. Sometimes if I get there early enough, I get to see the sun rising
over the creek, casting beautiful shades of red, pink, and orange in the sky.
This morning I even got to see some hot air balloons taking off from a nearby
park. That’s something I certainly don’t get to see every day.&lt;/p&gt;

&lt;p&gt;I also get to experience a really positive side of humanity on my walks.
Interactions with the people who I pass are so much more positive than the
usual day-to-day interactions with strangers. It’s like being in a small
country town — people smile and say “good morning”, and everyone seems so
happy. There are a few regulars who I see and we always smile and greet each
other as we pass. That always makes me feel nice. I also love seeing all the
dogs, and sometimes they will come up to me and give me a sniff and say hello.&lt;/p&gt;

&lt;p&gt;By the time I sit down at my computer for the day, it feels like I’ve had a
whole little adventure already. It’s nice knowing that no matter what happens
in a day, I can go to bed at night knowing that I’ve experienced something
positive for the day. And these experiences are small reminders that there is
still kindness and humanity in this world.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;I like to practice mindfulness in meditation, but I think that
intentionally giving your mind space to wonder is equally important.
Especially when you’re in a creative field. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2026/02/my-eventually-file</id>
		<title type="html">My eventually file</title>
		<link href="https://evencuriouser.net/2026/02/my-eventually-file.html" rel="alternate" type="text/html" title="" />
		<published>2026-02-17T00:00:00+11:00</published>
		<updated>2026-02-17T00:00:00+11:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;Let me paint a picture… You’re browsing your RSS feed and you come across an
interesting article. The article inspires a brilliant project idea. You’re
really excited about it, and you start working on it as soon as you’ve got some
free time. Eventually you hit a wall. Maybe it’s taking more time than you
initially anticipated, maybe it’s more challenging, or maybe it’s just not as
interesting as you initially thought it would be. You do this a few times and
before you know it, you’ve got a huge backlog of half-finished projects, and
you still haven’t finished the thing you were actually meant to be doing.&lt;/p&gt;

&lt;p&gt;I think many people can relate to this in some way or another. It doesn’t have
to be a project either - it could be a book you want to read, a TV series you
want to watch, a blog post you want to write, a feature or optimisation you
want to implement in an existing project. Maybe you just came across a really
elegant way to implement something, and want to apply it to one of your
projects. Whatever it is, the general pattern is this: thing X pops into your
head, you get really excited about it and you feel like you &lt;em&gt;need&lt;/em&gt; to do it
&lt;em&gt;right&lt;/em&gt; now. Eventually you lose steam, and it gets relegated to the graveyard
of things you might get around to finishing some day. This happens to me a lot.&lt;/p&gt;

&lt;p&gt;My solution to this problem is quite simple. I keep a text file on my computer
called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eventually.adoc&lt;/code&gt;&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, and every time I come up with one of these ideas,
instead of dropping everything and jumping onto it on a whim, I just add a line
to my eventually file describing the idea. I have a simple, greppable tagging
system with tags such as “reading”, “project”, “blog”, “quick”, etc, but other
than that it’s not particularly structured. Sometimes I put links in there to
resources or to other projects, sometimes it’s just a few words.&lt;/p&gt;

&lt;p&gt;The key to this working is to understand that it’s not a todo list. There is
absolutely no pressure to actually do anything on the list. In fact, most of
the things on the list probably won’t ever get done, and that’s okay. Whenever
I am looking for something to do, I can browse my eventually file and pick
something to work on based on the amount of time and energy I have and the
amount of effort I want to put in. In fact, that’s exactly how this very post
came to be. I’ve had this line in my eventually file for some time now:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Write about this eventually doc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every now and again I’ll go through and delete things from the file that no
longer interest me. And every line deleted is potentially a half-finished
project that I never had to start in the first place&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Time is precious, and there are so many things in the world that we could spend
it on. Especially if you’re someone like me who is fascinated by just about
everything under the sun. This simple strategy helps me to be more deliberate
and mindful about the things I spend my time on, and it helps reduce the
feelings of overwhelm due to having too many things on the go. I’ve been doing
it for a few years now, and it has helped a lot!&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;I really like Asciidoc, and use it for all my notes these days. But
use whatever format you like. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.md&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.txt&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.html&lt;/code&gt;, pick your poison. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;Granted sometimes you have to start a project before you realise that
it’s not actually something you want to continue with, which is totally
fine. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/10/querying-server-logs-with-ruby</id>
		<title type="html">Querying server logs with Ruby</title>
		<link href="https://evencuriouser.net/2025/10/querying-server-logs-with-ruby.html" rel="alternate" type="text/html" title="" />
		<published>2025-10-10T00:00:00+11:00</published>
		<updated>2025-10-10T00:00:00+11:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;During one of my internet blog rabbit-holes, I stumbled onto this &lt;a href=&quot;https://github.com/woodruffw/snippets/blob/master/vbnla/vbnla&quot;&gt;nifty
script&lt;/a&gt; for parsing Nginx server logs using Ruby. The script basically
parses the logs, dumps them into a hash table in a structured way, then spits
out some basic statistics. Check out the accompanying &lt;a href=&quot;https://blog.yossarian.net/2023/12/24/You-dont-need-analytics-on-your-blog&quot;&gt;blog post&lt;/a&gt;
for more details.&lt;/p&gt;

&lt;p&gt;I had to adjust it slightly because I have multiple virtual hosts running under
Nginx, and my log format includes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$host&lt;/code&gt; so I can see which requests are
for which website:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;log_format&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;custom&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$remote_addr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$remote_user&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$time_local&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$status&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$body_bytes_sent&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$http_referer&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$http_user_agent&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I love this script because it’s a great example of how far you can get with
simple tools. You don’t need huge complex software like Google Analytics to get
useful data about your website traffic. All you need is a scripting language
and a bit of creativity! The value to complexity ratio is much higher with a
simple script like this.&lt;/p&gt;

&lt;p&gt;The script is pretty limited (by design) in what it spits out: the top 10 hits
for each field in the logs. You can customise the threshold using environment
variables, and you can specify an ignore list to filter out logs you don’t want
to see, but that’s about it.&lt;/p&gt;

&lt;p&gt;I wanted just a teeny bit more out of it. Specifically, I wanted to be able to
see different sorts of metrics. For example, how many hits has
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://example.com/foo/bar&lt;/code&gt; had in the last month? I thought about adding
some CLI flags to customize the output. Then over time I could accumulate a
collection of useful metrics which are just a CLI flag away. But it starts
getting very complicated very quickly. If I went down that path, I would
already be halfway towards &lt;a href=&quot;https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule&quot;&gt;implementing my own query language&lt;/a&gt; for
Nginx logs. Instead, I decided to add a just few simple lines to the script,
just after it finishes parsing the logs into a data structure, and before
printing the statistics:&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-i&quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;STDIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/dev/tty&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;irb&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now I can run the script in interactive mode by supplying a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i&lt;/code&gt; flag. It
will drop me into a REPL with the logs already parsed into a convenient data
structure and made available to me in the current scope:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat /var/log/nginx/access.log | vbnla -i

irb&amp;gt; hgram # access the histogram
=&amp;gt; {&quot;ip&quot; =&amp;gt; ...}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And I can use Ruby as my query language! Why implement, learn, and maintain a
new query language when I can use a language I already know&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;? Need to
remember a complex one-liner you came up with last month? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.irb_history&lt;/code&gt; to the
rescue. You could even put your queries in a script and source them from within
the REPL. And with Ruby’s numerous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable&lt;/code&gt; methods such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt; and friends&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, it makes for quite an elegant query
language.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;If you’re wondering about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STDIN.reopen&lt;/code&gt; line, it’s because the
script reads input from STDIN. This is really elegant because it means you
can use existing unix tools to control what the script sees. For instance,
for large log files, you can do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail -n100 /var/log/nginx/access.log |
vbnla&lt;/code&gt; to only parse the 100 most recent lines. But IRB reads from STDIN,
which when piping data into the script, is the file. IRB will see that the
input stream is finished and quit, so it needs to be reopened. Thanks to
&lt;a href=&quot;https://stackoverflow.com/a/4585387&quot;&gt;Theo&lt;/a&gt; over on Stack Overflow for this
solution. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;I’m generally quite pro-DSL. But they’re only worthwhile for things you
use regularly. For things that you don’t use as often, the effort of
constantly looking up syntax that you’ve forgotten doesn’t outweigh the
benefits of using a DSL. In those cases I think it’s better to use a more
generic language which you’re already familiar with. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;Or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reject&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inject&lt;/code&gt; if you prefer Smalltalk’s more whimsical
nomenclature. Take your pick. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/09/progressive-enhancement-of-my-tech-stack</id>
		<title type="html">Progressive enhancement of my tech stack</title>
		<link href="https://evencuriouser.net/2025/09/progressive-enhancement-of-my-tech-stack.html" rel="alternate" type="text/html" title="" />
		<published>2025-09-20T00:00:00+10:00</published>
		<updated>2025-09-20T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;In a previous &lt;a href=&quot;/2025/07/why-i-use-jekyll.html&quot;&gt;post&lt;/a&gt;, I touched on a concept I called
“progressive enhancement of my tech stack”. The name for this idea comes from
the well-known concept of &lt;a href=&quot;https://en.wikipedia.org/wiki/Progressive_enhancement&quot;&gt;progressive enhancement&lt;/a&gt; of
the UI in a web application. Here I’m applying it not just to the UI, but to
the entire stack. The idea is that when I’m building a project, I use the
simplest possible technology for the requirements as they currently stand. If
and when the requirements of the project necessitate more technical complexity,
I’ll slowly bump up the complexity of my stack. But here is the crucial part:
my stack is carefully chosen so that as the requirements arise, I can easily
bolt on more layers to add more complexity to the stack, without affecting the
layers beneath. In other words, without having to re-factor or re-write my
entire app.&lt;/p&gt;

&lt;p&gt;This is what it looks like:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Start with hand-written HTML and CSS. Almost all my projects start out this
way.&lt;/li&gt;
  &lt;li&gt;If you need layouts for consistency across multiple pages or to render
content from static data, bring in a static site generator.  For this I use
Jekyll.&lt;/li&gt;
  &lt;li&gt;If you need client-side logic, bring in vanilla JS.&lt;/li&gt;
  &lt;li&gt;If you need reactivity or complex client side logic, bring in a light-weight
javascript library. One that can be imported via a simple script tag. For
this I use Alpine.js.&lt;/li&gt;
  &lt;li&gt;If you need server-side logic, bring in a micro-framework. I use Sinatra.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using a framework like Svelte, Astro, or Nextjs is like assuming you’ll need
everything right off the bat. It’s all there &lt;em&gt;in case you need it&lt;/em&gt;.  This
certainly has an advantage - no need to refactor if you need an extra feature
in your framework. But as I said above, I’ve carefully chosen my stack so that
I don’t need to do any major re-factoring or re-writing to add the next layer.
And it has the huge advantage of always being the simplest possible technology
for the requirements of the project.&lt;/p&gt;

&lt;p&gt;Another advantage is that you start to learn just how far you can get with the
simplest tools. You start to learn just how often you don’t actually &lt;em&gt;need&lt;/em&gt; the
features offered by those complex frameworks. Admittedly the jump from
hand-written HTML to using an SSG doesn’t take all that much to justify. I have
very few projects where I haven’t used an SSG. The jump to step 4 though is not
one I have to make very often. You’d be surprised &lt;a href=&quot;https://codeberg.org/evencuriouser/robin_cms&quot;&gt;how&lt;/a&gt;
&lt;a href=&quot;https://git.sr.ht/~evencuriouser/teatree&quot;&gt;far&lt;/a&gt; you can get with native HTML elements. The jump to step 5 is
pretty clear-cut. Whenever you need to run code on the server, you’ll need some
server-side scripting.  Examples of this are projects which need a contact
form, or database-backed web apps. As I’ll explain later, it is quite possible
to mix static and dynamic websites without complex frameworks.&lt;/p&gt;

&lt;p&gt;So how does it work? How can I add layers to my stack without fundamentally
altering it? I’ll walk you through each layer of enhancement, and for each
layer, explain how it fits onto the stack.  Lets assume that your starting from
step 1, with some hand-written HTML and CSS files in a repository that looks
something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./
├── index.html
├── robots.txt
├── favicon.ico
└── stylesheet.css
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;incorporating-a-build-step&quot;&gt;Incorporating a build step&lt;/h2&gt;

&lt;p&gt;Say you want to move from step 1 to step 2 and add a build step. If you use
Jekyll, all you need to do is run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle init&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle add
jekyll&lt;/code&gt;&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. You can now build your website with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec jekyll build&lt;/code&gt;.
That’s it! We didn’t generate any boilerplate, or project skeletons, we didn’t
have to move any files around, or even edit any the existing files.&lt;/p&gt;

&lt;p&gt;You can now enhance away - add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;, add some layouts, some data
files, whatever you need.  The key to making this all work is that Jekyll is
practically boilerplate-free, and it stores content in the root directory&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.
Your repository now looks something like the following. Note that the structure
hasn’t changed at all.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./
├── Gemfile
├── Gemfile.lock
├── index.html
├── robots.txt
├── favicon.ico
├── stylesheet.css
├── _config.yml
└── _layouts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;incorporating-client-side-logic&quot;&gt;Incorporating client-side logic&lt;/h2&gt;

&lt;p&gt;Now say you want to move from step 2 to step 3. This is probably the easiest
and most obvious jump, but I’ll include it for completeness. All you need to do
is create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script.js&lt;/code&gt; file in the root directory of your repository. Then
just add the script tag to your HTML and your done. Your repository doesn’t
look very different from before:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./
├── Gemfile
├── Gemfile.lock
├── index.html
├── script.js
├── robots.txt
├── favicon.ico
├── stylesheet.css
├── _config.yml
└── _layouts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;incorporating-reactivity-or-complex-client-side-logic&quot;&gt;Incorporating reactivity or complex client-side logic&lt;/h2&gt;

&lt;p&gt;If the client-side logic is getting too out of hand, or if you need some kind
of reactivity, &lt;a href=&quot;https://alpinejs.dev/&quot;&gt;Alpine.js&lt;/a&gt; is a great choice. It progressively
enhances your HTML (this is the traditional meaning of the term). Including it
is as simple as adding the following script tag to the head of your HTML
documents:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you can start adding Alpine.js code to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script.js&lt;/code&gt;. Again, you haven’t
changed anything, you haven’t altered your project structure or your toolchain.
You’ve just enhanced it, and your repository looks exactly the same as it did
before.&lt;/p&gt;

&lt;h2 id=&quot;incorporating-server-side-logic&quot;&gt;Incorporating server-side logic&lt;/h2&gt;

&lt;p&gt;Moving from step 4 to step 5 is a little more complicated, but using
&lt;a href=&quot;https://sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; keeps it relatively simple. The process is to create a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.ru&lt;/code&gt; script in the root directory of your repository, and create some
Ruby files which handle your routes. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.ru&lt;/code&gt; looks like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;sinatra&quot;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;**/*.rb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_plugins&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vendor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sinatra&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Application&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dir.glob&lt;/code&gt; loop includes every Ruby file in the current directory
(excluding anything you might have in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_plugins&lt;/code&gt; and your gem source files).
That way you can just dump Ruby files in your directory and they’ll
automatically get loaded. Combined with Sinatra’s &lt;a href=&quot;https://sinatrarb.com/intro.html#inline-templates&quot;&gt;inline
templates&lt;/a&gt;, this gives you a simple file-based routing
system. Static and dynamic pages treated on equal footing, the only real
difference being whether they are rendered at compile time or at run time.&lt;/p&gt;

&lt;p&gt;For an example of a dynamic route file, say you want to create a dynamic route
at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/hello&lt;/code&gt;. You can create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello.rb&lt;/code&gt; file that looks like the following,
which will automatically be loaded by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.ru&lt;/code&gt; file above.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:inline_templates&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/hello&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;liquid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:layout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:default&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;__END__

@@hello
&amp;lt;h1&amp;gt;Hello, world!&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;I am a dynamic route!&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Being able to use your Jekyll layouts for your dynamically rendered pages
creates a slight complication. To do this, we need to set Sinatra’s views
directory to Jekyll’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_layouts&lt;/code&gt; directory like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Register the .html file extension for liquid template engine because Jekyll&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# expects it to be .html, while Sinatra expects it to be .liquid&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Tilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Tilt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:liquid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:views&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__dir__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;_layouts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that Sinatra expects both views and layouts to reside in the same
directory, but because we’re using inline templates, we don’t have to pollute
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_layouts&lt;/code&gt; directory with server-side views.&lt;/p&gt;

&lt;p&gt;Now we’re ready to tackle the final part: web server config. I use Nginx as my
web server but you could achieve the same thing with any other server. To allow
serving dynamic routes as well as static pages, and add the following block to
your nginx config (assuming your Sinatra app is running on port 3001).&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;^/hello(/.*)?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://localhost:3001&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-For&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Real-IP&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$http_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you start having a lot of dynamic routes, maintaining them all in the nginx
config could start to become tedious. My recommendation would be have a common
regexable pattern for all your dynamic routes, so you can match them all with a
single block in your nginx config.&lt;/p&gt;

&lt;p&gt;Something to note is that even if I need some dynamic routes, I still tend to
keep as many of my pages as possible static. This way if the app process
running my dynamic routes fails for whatever reason, I know the static pages
will still be served reliably.&lt;/p&gt;

&lt;p&gt;Migrating to dynamically-rendered pages is probably the biggest step in the
progressive enhancement journey, but note that we still haven’t done any
restructuring or re-factoring. You’re repository still looks the same as it
did before, with the addition of some Ruby files and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.ru&lt;/code&gt; file.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./
├── Gemfile
├── Gemfile.lock
├── index.html
├── script.js
├── robots.txt
├── favicon.ico
├── stylesheet.css
├── config.ru
├── hello.rb
├── _config.yml
└── _layouts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This stack gets you most of what complex web frameworks get you - you
can have parts of your website built at compile-time (SSG), parts built at
run-time on the server (dynamic routes), and parts execute at run-time on the
client. All the while keeping the number of moving parts as small as possible.
This makes for reliable and robust software.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Technically you don’t even need to do that. You can build your site from a global Jekyll install (in this way it can be used in a similar way to Hugo or Zola). But I prefer to use a Gemfile in case I need to bring in more dependencies later. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;This doesn’t seem to be a very popular feature among static site generators. Most of them stuff the content away in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; directory. But the website &lt;em&gt;is&lt;/em&gt; content. Everything else is auxiliary. Jekyll gets this right in my opinion by storing content in the root directory, and everything else in auxiliary “underscore” directories. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/08/rendering-my-posts-as-man-pages</id>
		<title type="html">Rendering my posts as man pages</title>
		<link href="https://evencuriouser.net/2025/08/rendering-my-posts-as-man-pages.html" rel="alternate" type="text/html" title="" />
		<published>2025-08-22T00:00:00+10:00</published>
		<updated>2025-08-22T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;Some time back I remember reading on one of the &lt;a href=&quot;https://aartaka.me/&quot;&gt;blogs&lt;/a&gt; that I follow
that you could read their blog posts as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man&lt;/code&gt; pages by changing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.html&lt;/code&gt;
suffix on the URL to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.7&lt;/code&gt;. I thought this was a really fun idea. It’s a great
example of how programming doesn’t always have to be super practical.
Sometimes it can be about making things just because they’re fun. This is
something I’d wanted to implement on my own blog ever since, and so one sunny
Sunday afternoon, I set out to do just that.&lt;/p&gt;

&lt;p&gt;I figured it should be relatively straightforward as my website is built with
Jekyll which is &lt;em&gt;very&lt;/em&gt; extensible. It turns out that, combined with Ruby’s
inherent hackability, it was.&lt;/p&gt;

&lt;p&gt;Jekyll lets you build &lt;a href=&quot;https://jekyllrb.com/docs/plugins/generators/&quot;&gt;generator plugins&lt;/a&gt; which can
programmatically create additional content for you. So all I needed to make
this work is a generator that iterates through my blog posts and:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;creates a new page for each post;&lt;/li&gt;
  &lt;li&gt;changes the layout from the prescribed HTML layout to a new man page layout; and&lt;/li&gt;
  &lt;li&gt;somehow converts the content of my blog posts to a format supported by man(1).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is what I ended up with.&lt;/p&gt;

&lt;div class=&quot;language-rb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Man&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Generator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jekyll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Generator&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@site&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;docs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;vi&quot;&gt;@site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ManPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ManPage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jekyll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Page&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;section&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@site&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@base&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;source&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;vi&quot;&gt;@basename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;slug&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@basename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@ext&lt;/span&gt;

      &lt;span class=&quot;vi&quot;&gt;@data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;layout&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;replace_ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;layout&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;command&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;section&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;section&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# TODO: convert post content to a format supported by man(1)&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# and set post content...&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# @content =&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;replace_ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Most of this is just boilerplate taken straight from the Jekyll documentation.
There are a few interesting bits here. The first is setting the basename to the
slug of the post and the extension to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.7&lt;/code&gt;. This ensures that the man page
representation has the same URL as the HTML representation but with the man
extension. The other interesting bit is the fields in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@data&lt;/code&gt; hash. They
correspond the page metadata that you would normally set in the frontmatter.
The layout is set to a new layout with the same name as the one specified in
the post, but with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.7&lt;/code&gt; file extension. The rest of the fields are
variables for rendering in the layout - the man page “command”, and the man
section. I’m also copying over all the metadata from the post’s frontmatter
with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;**post.data&lt;/code&gt;. The layout looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-roff&quot;&gt;.TH {{ page.command | upcase }} {{ page.section }} {{ page.date | date: &quot;%Y-%m-%d&quot; }}
.SH NAME
{{ page.title }}
.SH SYNOPSIS
{{ site.description }}
.SH TEXT
{{ page.content }}
.SH AUTHOR
{{ site.author.name }}
.SH COPYRIGHT
Copyright {{ page.date | date: &quot;%Y&quot; }} {{ site.author.name }}
.MT {{ site.author.email }}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the only thing left is to figure out how to convert the post content to a
format supported by man(1). It turns out that Kramdown—the Markdown parser
which is built into Jekyll—already has a &lt;a href=&quot;https://kramdown.gettalong.org/converter/man.html&quot;&gt;built-in method&lt;/a&gt; that can
convert Markdown to groff format. All you need to do is call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_man&lt;/code&gt; instead
of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_html&lt;/code&gt; on the parsed document.&lt;/p&gt;

&lt;div class=&quot;language-rb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Kramdown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;vi&quot;&gt;@content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_man&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I put it all together, and it was all going rather smoothly until…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Build Warning: Layout &apos;article.7&apos; requested in ... does not exist.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we’re coding.&lt;/p&gt;

&lt;p&gt;I did some digging through the Jekyll source code and found the culprit in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/jekyll/readers/layout_reader.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;layout_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I had two layouts for each post - an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;article.html&lt;/code&gt; layout which renders the
default HTML representation of the post, and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;article.7&lt;/code&gt; layout which
renders the man page representation. The method above is called by a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;
method which builds a hash table of the layouts as an instance variable in the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutReader&lt;/code&gt; class. It strips the file extension from the layout before
adding it to the hash table, so when I have two layouts with the same name but
different file extensions, the second layout it reads overrides the first.&lt;/p&gt;

&lt;p&gt;A simple (and safe) solution to this would be to give the layout a different
name. But they are different representations of the same layout, so they
&lt;em&gt;should&lt;/em&gt; have the same name. This is a personal project, and I wanted it
done right, not safe. And Ruby is a very hackable language, so let’s hack it!&lt;/p&gt;

&lt;p&gt;One of the really cool (and dangerous) things about Ruby is that you can monkey
patch any class. It’s basically like having a patch which only exists in your
codebase, and doesn’t need to be accepted upstream. The dangerous part is that
if the Jekyll maintainers ever restructure the code, the patch might break. But
I’m not too worried as worst case scenario I update my Jekyll dependency, my
site doesn’t build, and it takes me about 10 minutes to resolve. And like I
said, this is a personal project, and I’m allowed to break the rules if I want.
So I built my patch:&lt;/p&gt;

&lt;div class=&quot;language-rb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Jekyll&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LayoutReader&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;layout_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.html&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To preserve backward compatibility, it first checks if the layout has an
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.html&lt;/code&gt; extension and if so, preserves the existing functionality. Otherwise,
instead of adding just the basename of the file to the hash table, it adds the
entire filename, extension and all. So now I can have multiple layouts for each
file with the same name but different file extensions. Yay.&lt;/p&gt;

&lt;p&gt;I ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake&lt;/code&gt;, and lo and behold, it works! You can now view all of my blog
posts as man pages by replacing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.html&lt;/code&gt; extension on the URL with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.7&lt;/code&gt;.
Give it a go!&lt;/p&gt;


		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/08/browser-redirects</id>
		<title type="html">Browser redirects</title>
		<link href="https://evencuriouser.net/2025/08/browser-redirects.html" rel="alternate" type="text/html" title="" />
		<published>2025-08-08T00:00:00+10:00</published>
		<updated>2025-08-08T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I recently had the need to set up a redirect on a website I was working on. The
problem is the website is hosted on Github pages, so I couldn’t just set up a
redirect on the server. My first thought was to use Javascript, by writing
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.location.href = &quot;https://example.com&quot;&lt;/code&gt;. But I’d prefer not to use
Javascript if I don’t have to.&lt;/p&gt;

&lt;p&gt;I did some digging and learned that browsers support redirects via the
‘refresh’ meta tag. This is really nifty as it allows you to set up redirects
in situations where your site is entirely static and you don’t have access to
a server. All you need is an HTML file that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Redirecting&lt;span class=&quot;ni&quot;&gt;&amp;amp;hellip;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;canonical&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://example.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https://example.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;http-equiv=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;refresh&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0; url=https://example.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;robots&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;noindex&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Redirecting&lt;span class=&quot;ni&quot;&gt;&amp;amp;hellip;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://example.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Click here if you are not redirected.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The tag that makes it work is this one: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;meta http-equiv=&quot;refresh&quot;
content=&quot;0; url=https://example.com&quot;&amp;gt;&lt;/code&gt;. But this little snippet also has a few
additional features which help make it a bit more robust. It includes a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag with a Javascript redirect as a fallback if the meta tag doesn’t
work for whatever reason. And as a catch-all (say someone doesn’t have
Javascript enabled in their browser), it provides a link you can click on to
manually follow the redirect. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robots&lt;/code&gt; meta tag ensures that search engines
don’t index this page, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonical&lt;/code&gt; meta tag ensures that search engines
see both sites as the same.&lt;/p&gt;

&lt;p&gt;There are a few downsides to this method. Firstly, it’s not a proper redirect -
if you open up dev tools, you’ll see that the status code is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;30x&lt;/code&gt;;
it’s basically just instructing the browser to fetch a new page. As a result it
can also mess with the browsers back button. It’s not a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;301&lt;/code&gt; (permanent)
redirect either, so search engines won’t update their index. Having said all
that, if your in an environment where you don’t have access to the server, this
is a pretty decent alternative.&lt;/p&gt;

&lt;p&gt;This doesn’t seem to be a particularly well-known feature, which is why I
thought I’d share it here. It’s nice to know that even after doing this web dev
thing for quite some years, I’m still always learning new things.&lt;/p&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/07/why-i-use-jekyll</id>
		<title type="html">Why I use Jekyll</title>
		<link href="https://evencuriouser.net/2025/07/why-i-use-jekyll.html" rel="alternate" type="text/html" title="" />
		<published>2025-07-27T00:00:00+10:00</published>
		<updated>2025-07-27T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;As of relatively recently, &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; has become my Static Site Generator
(SSG) of choice. In this post, I plan on outlining some of the alternatives I
considered, and my reasoning for using Jekyll. For anyone else who is
researching different SSGs, hopefully this post contains some useful
information.&lt;/p&gt;

&lt;h2 id=&quot;progressive-enhancement-of-my-tech-stack&quot;&gt;Progressive enhancement of my tech stack&lt;/h2&gt;

&lt;p&gt;Many of my websites start life as plain old hand-written HTML files. When the
number of pages starts to grow, or when hand-writing HTML pages starts becoming
unwieldy, I’ll consider adding in an SSG. There are two features of Jekyll
which facilitate this ‘progressive enhancement’ of my tech stack.&lt;/p&gt;

&lt;p&gt;The first is that Jekyll doesn’t have a ‘source’ directory. All of the pages
sit at the root directory, and the structure of the built site matches the
structure of the files in the root directory. This means I don’t have do do any
restructuring to introduce Jekyll to a hand-written website. This doesn’t seem
to be a particularly popular feature as almost all other SSGs stuff the pages
into a ‘source’ directory. But for me it works really well.&lt;/p&gt;

&lt;p&gt;The other feature is that Jekyll is almost entirely boilerplate free. I’ll
return to this idea a bit &lt;a href=&quot;#convention-over-configuration&quot;&gt;later&lt;/a&gt;, but for now
I just want to say that introducing it into a an existing project is as simple
as creating a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt; file with a few lines of code and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_layouts&lt;/code&gt;
directory.  Every additional feature is opt-in, and you do so by simply
creating new files and directories without altering the project structure at
all. Jekyll does have a site scaffolding feature, but I’ve never actually used
it because I’ve never needed it.&lt;/p&gt;

&lt;h2 id=&quot;liquid&quot;&gt;Liquid&lt;/h2&gt;

&lt;p&gt;I never thought I’d say this, but I’ve actually come to like using the Liquid
templating language. I’d always been a big advocate for ERB because it doesn’t
require learning yet another tool - you’re just using Ruby, a language you
already know. But there are some nice advantages of using Liquid.&lt;/p&gt;

&lt;p&gt;The first is that it puts you in a &lt;a href=&quot;https://josephg.com/blog/3-tribes/&quot;&gt;product mindset&lt;/a&gt;. I typically
fall in to the poet/mathematician tribe, but when I’m building websites for
work I’m making them for the end-product, not for the beauty of the code
itself. And it’s important for me to put myself into this mindset. When I’m
writing templates, I’m thinking about design, not programming. And with Liquid,
I’m using a tool built for design, not a general-purpose programming language.&lt;/p&gt;

&lt;p&gt;Which leads me to the second advantage of Liquid: it’s a DSL for writing
templates. I think DSLs are a &lt;em&gt;good thing&lt;/em&gt;. Yes, it means you have to learn yet
another language. But it also means you are using a language specifically
designed for the task at hand. And therefore the language is extremely good ak
the thing you’re using it for - in this case writing templates.&lt;/p&gt;

&lt;p&gt;The third advantage is that Liquid is very similar to other popular templating
languages such as Jinja, Twig, Tera, etc. In fact most languages&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; have some
sort of templating library that follows roughly the same design. The result is
that it’s become the de-facto templating language, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Dominant_design&quot;&gt;dominant
design&lt;/a&gt;. This makes it easier for others to understand your
code, easier to find resources, and easier to port your code to other
languages. Learning a DSL is an investment as by definition a DSL is less
generally applicable as other languages. So if you’re going to invest in
learning a DSL, I think it’s a better payoff to pick one that is more broadly
used.&lt;/p&gt;

&lt;p&gt;There are donwsides to using a templating language which is not a full-blown
programming language. Most notably you’re restricted to the set of features
that it provides. Jekyll supports creating custom filters and tags for Liquid.
So any limitations you might come across by not having the full power of Ruby
at your disposal &lt;em&gt;à la&lt;/em&gt; ERB, can easily be overcome by writing a bit of Ruby
code in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_plugins&lt;/code&gt; directory.&lt;/p&gt;

&lt;h2 id=&quot;convention-over-configuration&quot;&gt;Convention over configuration&lt;/h2&gt;

&lt;p&gt;A core part of the &lt;a href=&quot;https://jekyllrb.com/philosophy/&quot;&gt;Jekyll philosophy&lt;/a&gt; is that it should
have sane defaults that work for the vast majority of use cases. But it’s
extensible enough that you can do basically whatever you want with it when you
do need something out of the box.&lt;/p&gt;

&lt;p&gt;The SSG I was using prior to Jekyll was &lt;a href=&quot;https://nanoc.app/&quot;&gt;Nanoc&lt;/a&gt;. I really like Nanoc, in
particular I really like how you can define your own &lt;a href=&quot;https://nanoc.app/doc/rules/&quot;&gt;compiler
rules&lt;/a&gt; using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rules&lt;/code&gt; file. It’s almost like writing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Makefile&lt;/code&gt;
to build your website, and it gives you almost unlimited flexibility. But I
quickly discovered that for the vast majority of use cases, the rules are
exactly the same: process Markdown, run it through a templating engine, lay it
out, write to the output file. So the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rules&lt;/code&gt; file becomes boilerplate repeated
from project to project. In Jekyll these rules are the default. And if you want
to use alternative text processing tools—maybe you want to write content in
Asciidoc, maybe you want to generate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man(1)&lt;/code&gt; pages from your content—again,
you can just write a bit of Ruby code in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_plugins&lt;/code&gt; directory.&lt;/p&gt;

&lt;h2 id=&quot;extensibility&quot;&gt;Extensibility&lt;/h2&gt;

&lt;p&gt;When I first started researching Jekyll, something I read often was that
plugins are required to do just about anything non-standard. This put me off a
bit as it seemed very limiting. Until I realised that a plugin doesn’t have to
be an entire gem. It can literally be a file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_plugins&lt;/code&gt; directory with a
method in it. In this sense, Jekyll is a fully programmable tool, and you have
the power of the Ruby language and it’s &lt;em&gt;entire&lt;/em&gt; ecosystem to customise Jekyll
to your liking. This gives me confidence that I won’t run into limitations with
Jekyll any time soon. I’ve already discussed how you can use plugins to
overcome limitations in Liquid, and the default text processing and rendering
flow.&lt;/p&gt;

&lt;h2 id=&quot;stability&quot;&gt;Stability&lt;/h2&gt;

&lt;p&gt;Another core part of the Jekyll philosophy is that it should be stable, and
maintaining backward compatibility is a high priority. For me this is a big
deal as I build lots of small websites which are updated relatively
infrequently. Here’s an experience many people can relate to: you go to make an
update to a website which should take 5 minutes. But it ends up taking all day
because you can’t get the thing to compile. Life is too short for wasting time
dealing with dependency hell because the maintainers have made a breaking
change in the name of “improvement”.&lt;/p&gt;

&lt;h2 id=&quot;a-brief-discussion-of-other-static-site-generators&quot;&gt;A (brief) discussion of other Static Site Generators&lt;/h2&gt;

&lt;p&gt;I’m going to conclude this with a &lt;em&gt;very&lt;/em&gt; brief comparison to some of the other
SSGs I considered. I’m restricting my comparison to Ruby-based SSGs - purely
because I use Ruby for all my work projects, and I prefer to stay within a
single ecosystem where possible.&lt;/p&gt;

&lt;p&gt;I’ve already talked a little bit about Nanoc. The main advantage of Nanoc over
Jekyll is it’s extreme flexibility. But I haven’t yet come across something I
could do in Nanoc that I couldn’t do in Jekyll thanks to it’s plugin system. I
think Nanoc is a great project, but it doesn’t fit my use-case quite as well.&lt;/p&gt;

&lt;p&gt;There is also &lt;a href=&quot;https://www.bridgetownrb.com/&quot;&gt;Bridgetown&lt;/a&gt;, which I haven’t played around with, but
it looks like another great project. It’s a fork of Jekyll, so it shouldn’t be
too big of a jump to learn coming from Jekyll. It’s more of a full-blown
framework for generating static sites (think Rails for static sites), which is
a bit overkill for most of my projects. But I think it would be great for
larger projects, especially if you need a frontend bundler, a better component
system, or server-side routes.  &lt;a href=&quot;https://middlemanapp.com/&quot;&gt;Middleman&lt;/a&gt; is another Ruby-based
SSG which looks similar in scope to Bridgetown. I haven’t looked into this one
in great detail.&lt;/p&gt;

&lt;p&gt;There are many great Static Site Generators out there, and it can be quite
difficult to choose one. As with most tools, it’s a case of the right tool for
the job. I use Jekyll for my websites because it fits my use-case really
nicely. If my use-case changes in the future I will happily consider
alternatives, but for now I’m happy with Jekyll. YMMV.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Even many of the more “obscure” languages have libraries based on this design. For example Common Lisp has &lt;a href=&quot;https://github.com/mmontone/djula&quot;&gt;Djula&lt;/a&gt;, and OCaml has &lt;a href=&quot;https://github.com/tategakibunko/jingoo&quot;&gt;Jingoo&lt;/a&gt;. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/06/deepening-understanding</id>
		<title type="html">On deepening your understanding</title>
		<link href="https://evencuriouser.net/2025/06/deepening-understanding.html" rel="alternate" type="text/html" title="" />
		<published>2025-06-29T00:00:00+10:00</published>
		<updated>2025-06-29T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;Sometimes when working on something that requires particular knowledge or a
particular skillset, I find myself asking questions that I’ve never asked
before. Or never even though to ask before. My immediate reaction to this
experience is usually something along the lines of: &lt;em&gt;Why wasn’t I asking this
question before? How come I have all these questions now that I didn’t have
previously?  Does that mean I have a lesser understanding now than I did
previously? Am I forgetting things I once knew?&lt;/em&gt; Actually I think it means
quite the opposite.&lt;/p&gt;

&lt;p&gt;Not having asked the question previously is a symptom of ignorance, not
knowledge. The more we know, the more we realise we don’t know, and therefore
the more questions we have. The fact that we didn’t ask these questions
previously means we didn’t have the understanding that led us to ask the
question. We were obviously making unconscious assumptions, or not thinking
deep enough or critically enough for the question to surface.&lt;/p&gt;

&lt;p&gt;So next time you have questions that make you feel this way, it’s good to
remind yourself that this is a good thing. It means you are learning, thinking
more critically. It means that your understanding is deepening.&lt;/p&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/05/hello-from-ttyv1</id>
		<title type="html">Hello from ttyv1!</title>
		<link href="https://evencuriouser.net/2025/05/hello-from-ttyv1.html" rel="alternate" type="text/html" title="" />
		<published>2025-05-25T00:00:00+10:00</published>
		<updated>2025-05-25T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;Over the years, I’ve slowly started stripping back the layers of my computer’s
user interface. It helps me to learn about the various layers of abstraction
between the operating system and the typical graphical interface that most
people interact with. And also because it’s fun embracing the simplicty of
computing without all these layers. Just me, a shell, and an operating system.
Calm, quiet, bliss.&lt;/p&gt;

&lt;p&gt;This journey started when I decided to install and play around with FreeBSD (I
ended up loving it so much that it has since become my daily driver). After
installing and booting up, I got presented with a raw text console. Unlike most
Linux distributions, FreeBSD doesn’t come with any graphical environment by
default. I had to install X myself, learn how to configure it, and install a
graphical environment on top.&lt;/p&gt;

&lt;p&gt;I wanted to take this opportunity to start peeling back the layers.
Specifically I didn’t want to install a full desktop environment (DE). So I
decided to install and learn the i3 window manager. I also never bothered to
install a login manager. The login prompt on the virtual console, followed by
typing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startx&lt;/code&gt; is my login manager now. By stripping back this layer, and by
forgoing &lt;a href=&quot;https://xfce.org&quot;&gt;xfce&lt;/a&gt; - my DE of choice back in my Debian days - I learned an
awful lot.&lt;/p&gt;

&lt;p&gt;I learned how a DE fit’s into the stack. It’s just software which bundles up a
window manager, plus a bunch of other utilities that most people expect to have
installed on their system - network managers, taskbars, file browsers, settings
mangers, login managers, etc. I was forced to learn how to do most of these
tasks in the terminal, and it showed me that most of those utilities that come
bundled with a desktop environment are really just graphical wrappers over
basic system commands.&lt;/p&gt;

&lt;p&gt;I learnt that X is not some voodoo magic - it is just a program, no different
from any other program. And you start it like you would any other program on a
UNIX based machine - you type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startx&lt;/code&gt; at the console.  The only difference is
that instead of printing stuff to your terminal using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;printf&lt;/code&gt; and friends, it
renders pixels on your screen (presumably via graphics drivers). In order for X
to be useful, it needs to run some graphical program. Typically you’d configure
it to run your window manager&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. And a window manager is also just a program,
but it sits on top of X, and it’s job is to run other programs inside of
reorganisable graphical windows. All this is hidden from you when you use a
pre-installed desktop environment, because usually your computer boots straight
into the login manager, which kicks of X and your window manager for you. By
taking away this layer, it has greatly demystified how all this works.&lt;/p&gt;

&lt;p&gt;You’ll notice if you type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps -auxd&lt;/code&gt;&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; just how many layers deep you are when
using a desktop environment. This example is taken from a system running xfce
on FreeBSD (and truncated for clarity):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-- /sbin/init
 |-- login
 | `-- -bash
 |   `-- xinit /usr/local/etc/xdg/xfce4/xinitrc
 |     |-- /usr/local/libexec/Xorg
 |     `-- xfce4-session
 |       |-- xfwm4
 |       |-- xfsettingsd
 |       |-- xfce4-panel
 |       |-- Thunar --daemon
 |       |-- xfdesktop
 |       |-- xfce4-power-manager
 |       |-- /usr/local/lib/xfce4/notifyd/xfce4-notifyd
 |       |-- ...
 `-- xfce4-terminal
   `-- bash
     `-- vim _drafts/hello-from-ttyv1.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Having embraced life one layer lower on the stack, my process tree looks a
little simpler&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; (again truncated):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-- /sbin/init
 `-- login
   `-- -bash
     `-- /bin/sh /usr/local/bin/startx
       `-- xinit /home/aron/.xinitrc
         |-- /usr/local/libexec/Xorg
         `-- i3
           |-- i3bar
           | `-- i3status
           `-- alacritty
             `-- /usr/local/bin/bash
               `-- vim _drafts/hello-from-ttyv1.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;More recently, I’ve been thinking about stripping back another layer again.
What would it be like to use my computer without X? Most of the tools I use are
already text-based, so it wouldn’t be that big of an adjustment. The biggest
thing would be learning how to manage running multiple programs simulatneously.
I’d been meaning to try out &lt;a href=&quot;https://en.wikipedia.org/wiki/Tmux&quot;&gt;tmux&lt;/a&gt; for some time, so this weekend I set
out to install and learn it. Being a vim user, I found a simple configuration
that makes it fairly ergonomic for me&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;set-window-option -g mode-keys vi
bind-key h select-pane -L
bind-key j select-pane -D
bind-key k select-pane -U
bind-key l select-pane -R
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I can burn around tmux using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl-B&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;j&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;l&lt;/code&gt; which
feels quite comfortable, and not too different from moving around in vim or i3.&lt;/p&gt;

&lt;p&gt;And now my process tree is even simpler again:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-- /sbin/init
 |-- tmux: server
 | `-- bash
 |   `-- vim _drafts/hello-from-ttyv1.md
 `-- login
   `-- bash
     `-- tmux: client
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Naturally, there have been a few challenges adapting to life in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ttyv&lt;/code&gt;. Web
browsing is quite challenging in a text console. I already had w3m - a
text-based web browser - installed, but getting used to navigating the web with
w3m takes some getting used to. Sites which require Javascript don’t work, but
that’s not always a bad thing. After all, part of the motivation behind this
journey is simplicity and calmness. I’ve done some basic configuration here as
well to make it more ergonomic for me&lt;sup id=&quot;fnref:5&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Some other challenges I’ve come across so far are (and I’m sure there will be
more as I spend more time in the console):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Copy/pasting - tmux has a “copy mode”, but I haven’t looked into it yet. All in good time!&lt;/li&gt;
  &lt;li&gt;Monitoring my battery level without having to constantly type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apm&lt;/code&gt; (which is only relevant if running on a laptop).&lt;/li&gt;
  &lt;li&gt;Locking my screen without logging out of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ttyv&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Making the colours look a bit nicer.&lt;/li&gt;
  &lt;li&gt;Some weird stuff happens when you try to format text in vim running in tmux. I’m not yet sure if this is a tmux thing, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ttyv&lt;/code&gt; thing, or just a vim plugin not playing nicely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m sure as I find the solution to these, and any other hurdles that I come
across, I’ll learn even more!&lt;/p&gt;

&lt;p&gt;With every migration down the abstraction heirarchy, there are certain
“privelages” that are lost. But the knowledge and experience you gain from it
makes it an invaluable experience. Maybe one day I’ll go even further down and
abandon tmux in favour of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl-z&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fg&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bg&lt;/code&gt; workflow? Or maybe even just use
the built-in ttys and use the function keys to navigate between them?&lt;/p&gt;

&lt;p&gt;I don’t plan on abandoning X and i3 entirely, - it’s just not practical for
much of modern day computing, particularly not having access to a graphical
browser that can run Javascript. But I certainly hope I can incorporate more of
it into my daily workflow. The more productive I can become in a ttyv, the
more I can delay the point in my workflow where I have to type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Case-in-point, I’ve written this entire blog post and published it from tmux
running on a virtual console! Hello from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ttyv1&lt;/code&gt;!&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;You could equally well use it to run any graphical program. For instance you could configure X to boot directly into Firefox if you wanted to. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;If you’re running a Linux-based OS, I believe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps -auxf&lt;/code&gt; is the equivalent command. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;Well, it doesn’t look &lt;em&gt;much&lt;/em&gt; simpler, but most of what you see are just layers of shells. You can see that all the applicaton software is running as a subprocess of i3, which in turn is running as a subprocess of X. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;During my research, I did come across this nifty &lt;a href=&quot;https://github.com/christoomey/vim-tmux-navigator&quot;&gt;vim plugin&lt;/a&gt; which lets you navigate between vim splits and tmux splits n a uniform way. But I think when learning a new tool, it’s best to learn the defaults before getting too carried away with customisation. And it’s also nice to have different keys to force me to maintain awareness of which program I’m currently in. &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot;&gt;
      &lt;p&gt;If you’re interested in the configuration I’ve used, here it is:&lt;/p&gt;
      &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;keymap J        NEXT_TAB
keymap K        PREV_TAB
keymap L        NEXT
keymap H        PREV
keymap j        UP
keymap k        DOWN
keymap l        RIGHT
keymap h        LEFT
keymap gg       BEGIN
keymap G        END
keymap o        GOTO
keymap O        TAB_GOTO
keymap C-d      NEXT_PAGE
keymap C-u      PREV_PAGE
keymap f        GOTO_LINK
keymap F        TAB_LINK
keymap d        CLOSE_TAB
keymap !        COMMAND
keymap r        RELOAD
keymap 0        LINE_BEGIN
keymap $        LINE_END
keymap w        NEXT_WORD
keymap b        PREV_WORD
keymap /        ISEARCH
keymap n        SEARCH_NEXT
keymap N        SEARCH_PREV
keymap M        EXTERN
keymap :w       DOWNLOAD
keymap :q       QUIT
keymap :h       HELP
keymap *        OPTIONS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;      &lt;/div&gt;
      &lt;p&gt;&lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/05/objects-are-lambdas-are-objects-part-2</id>
		<title type="html">Objects Are Lambdas Are Objects - Part 2</title>
		<link href="https://evencuriouser.net/2025/05/objects-are-lambdas-are-objects-part-2.html" rel="alternate" type="text/html" title="" />
		<published>2025-05-14T00:00:00+10:00</published>
		<updated>2025-05-14T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;This post builds on my &lt;a href=&quot;/2025/04/objects-are-lambdas-are-objects.html&quot;&gt;previous post&lt;/a&gt; about the equivalence of
objects and lambdas. So to get the most out of it, I would recommend reading
that one first.&lt;/p&gt;

&lt;p&gt;In that post, we pretended like Ruby doesn’t already have lambdas, and tried to
simulate them in pure Ruby to show the equivalence of lambdas and objects.
Now you may have noticed I used a string to represent the lambda body. This is
not a very elegant way to do it, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eval&lt;/code&gt; takes a string, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eval&lt;/code&gt; is the
only way I can think of to execute some Ruby code within a given environment. I
could have passed a block, but that’s kind of cheating because in Ruby, blocks
are already closures, so we’d be defining a closure with a closure. There are
many different ways to simulate lambdas with objects, and I went down a bit of
a rabbit-hole with this. It got me thinking a lot more about this idea that
lambdas and objects are two sides of the same coin. Here are some of the
different approaches I took.&lt;/p&gt;

&lt;p&gt;If you don’t care about capturing variables from your environment, the simplest
way to implement a lambda with an object, which I showed in the previous post,
is an object with a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt; method.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;foo&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you only need a few variables from your environment, and you don’t need to
mutate them, you can always just pass them into the constructor of your object
like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lambda&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@foo&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We could get a little more fancy here and make our class more general-purpose.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lambda&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;instance_variable_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NotImplementedError&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@bar&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we wanted to automatically capture the entire environment, without having to
pass in each variable explicitly, we could pass a block like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lambda&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But this is technically cheating since a block is already a closure in Ruby. In
order to capture the environment without cheating, we can make use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eval&lt;/code&gt;,
which evaluates a string in a given execution context.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lambda&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@binding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is where we got to in the previous post, but it’s kind of ugly since we
have to pass the lambda body as a string. We also have to pass in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;binding&lt;/code&gt;
when we create the lambda. Can we do better? Ruby has some powerful
metaprogramming features, surely we can (ab)use this to get what we want…
Which brings us to the next attempt.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Binding&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local_variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;local_variable_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lambda&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NameError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Undefined local variable or method &apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_missing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/=\z/&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/=\z/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;instance_eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we are converting the environment to a hash table, and storing it as an
instance variable. Then we utilize the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_missing&lt;/code&gt; metaprogramming feature
to get/set the relevant variable from the binding. This method was shamelessly
stolen from Kelvin over on &lt;a href=&quot;https://stackoverflow.com/questions/3089643/is-eval-the-only-way-to-interact-with-binding-objects-in-ruby&quot;&gt;Stack Overflow&lt;/a&gt; and adapted slightly.&lt;/p&gt;

&lt;p&gt;Now you might have noticed that we are still passing a block which is already a
closure. So isn’t that still cheating? Well no, because this method doesn’t
&lt;em&gt;rely&lt;/em&gt; on the closure property of the block we pass in - we’re using it purely
as a nice way to pass a block of code to a method.  If you want proof, you can
do the same thing with a string and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eval&lt;/code&gt; and it still works:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lambda&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NameError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Undefined local variable or method &apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_missing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/=\z/&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/=\z/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yet another way to simulate a lambda with an object is using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;instance_eval&lt;/code&gt;.
This is also technically not cheating since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;instance_eval&lt;/code&gt; is the one function
(that I know of) that takes a block which doesn’t capture it’s environment. So
again, we’re using a block purely as a way of passing code to a method, rather
than for it’s environment capture property.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lambda&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NameError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Undefined local variable or method &apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_missing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/=\z/&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/=\z/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@vars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NotImplementedError&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_eval&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see there are many different ways to simulate lambdas with objects.
I’m sure there are others I haven’t thought of. The one that you would choose
(pretending like Ruby doesn’t already have lambdas), would be the simplest one
that gets the job done.&lt;/p&gt;

&lt;p&gt;All these methods rely on one or more of the following (suboptimal) strategies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;having to explicitly pass the environment variables you want to capture&lt;/li&gt;
  &lt;li&gt;passing the lambda body as a string and using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eval&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;having to explicitly pass in captured variables or the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;binding&lt;/code&gt; object&lt;/li&gt;
  &lt;li&gt;cheating by using the closure property of a block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given the difficulty of implementing this in pure Ruby, I can only assume that
the environment capture property of blocks is built into Ruby at the language
level.&lt;/p&gt;

&lt;p&gt;So what’s the point of all this? Well if there is no obvious and clean way to
simulate lambdas in pure Ruby without it being built in to the language, and if
objects can be simulated in Scheme pretty well, does that mean that lambdas are
in fact more fundamental than objects? I think the answer is still no. It’s not
the fact that you can implement one perfectly in terms of the other that proves
this duality - as we’ve demonstrated above, the ability to do that has a lot
more to do with the language semantics than anything else - it’s the fact that
you can use both methods equivalently to achieve the same &lt;em&gt;outcome&lt;/em&gt;. For
example if all you need is read-access to a few variables in something that can
be passed around your code, then the second method will suffice, and for all
intents and purposes, it is equivalent to a lambda. In principle there is
an OO language which could simulate lambdas with objects perfectly.&lt;/p&gt;

&lt;p&gt;In other words, it’s not some blessed definition of a lambda that is relevant,
it’s the definition that meets your requirements. Just as the implementations I
showed of lambdas in Ruby don’t fully implement a lambda in terms of the
definition I outlined in the previous post, the implementation I showed of an
object in Scheme also doesn’t fully implement an object by some definitions -
for example it achieves encapsulation but not inheritance. But if it gets the
job done, then it is equivalent. In fact to quote from the man
page for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby(1)&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Closures
        In Ruby, you can objectify the procedure.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If a lambda is just an objectified procedure, then all you need is an object
with a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt; method to do the job.&lt;/p&gt;

&lt;p&gt;To conclude this little journey, I will present another short snippet of code:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;_x&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:set_x&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;_x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:set_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;baz&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yep, objects simulating lambdas, simulating objects. We’ve come full circle.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit 22/05/2025 - add quote from ruby man page.&lt;/em&gt;&lt;/p&gt;


		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/05/turning-my-smartphone-into-a-dumbphone</id>
		<title type="html">Turning My Smartphone Into A Dumbphone</title>
		<link href="https://evencuriouser.net/2025/05/turning-my-smartphone-into-a-dumbphone.html" rel="alternate" type="text/html" title="" />
		<published>2025-05-11T00:00:00+10:00</published>
		<updated>2025-05-11T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I recently became very interested in so-called “dumbphones”. These are phones
that are deliberately paired-back in features, mirroring the simple and
distraction-free phones of the early 2000’s. It’s a refreshing change of pace,
and obviously I’m not the only one who’s become disillusioned with modern
phones because there are quite a few products available in this space.&lt;/p&gt;

&lt;p&gt;The biggest downside to these phones is that they don’t let you install
third-party apps&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Which is obviously the point. But for me I treat my phone
as a tool. And I still want to be able to use apps that genuinely add value to
my life - apps which I use with intention, not just because I’m bored.&lt;/p&gt;

&lt;p&gt;I currently use the iPhone SE - the most basic iPhone on the market, and I
spent some time figuring out how to set it up in a way that makes it as close
to a dumbphone as possible. Turns out you can get quite far with this, you just
need a little discipline. I thought I’d share my tips here in case there are
others out there in the same boat.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Delete almost all apps&lt;/p&gt;

    &lt;p&gt;I’ve deleted every app off my phone which is not strictly necessary and
doesn’t add value to my life. This includes email! I prefer to do most
things on my computer, because I am doing it with intention, not just
because my phone happends to be sitting next to me. The only apps I have
installed on my phone are the basic utility apps, messaging and phone calls
(obviously), and Duolingo (this is an example of an app that adds value to
my life).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Turn off almost all notifications&lt;/p&gt;

    &lt;p&gt;I only have notifications for messages, phone calls, and my alarm.
That’s it. Nothing else is important enough to interrupt my day.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Put it in greyscale&lt;/p&gt;

    &lt;p&gt;When your phone is in greyscale, you naturally spend much less time on it
simply because it’s less engaging. I’ve set it up so that I can toggle
greyscale on and off by triple tapping the home button if I need to
look at something in colour.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these techniques, I’ve made my phone so boring that whenever I pick it up
I end up putting it straight back down again, because there’s simply nothing
interesting to do on there. Of course you have to have a little bit of
discipline - for example there’s nothing stopping you from re-downloading
Reddit or keeping it in colour mode.&lt;/p&gt;

&lt;p&gt;Dumbphones are great if you want to simplify your life. But they definitely
have some downsides as some third-party apps are genuinely useful when they are
used with intention. If you have a little bit of discipline, you can get pretty
close to a dumbphone with a regular smartphone.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;There is a line of phones which run an operating system called KaiOS which has a third-party app store. But I imagine that companies have a very low vested interest in maintaining apps for this marginal corner of the market. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/05/constraints-make-better-art</id>
		<title type="html">Constraints Make Better Art</title>
		<link href="https://evencuriouser.net/2025/05/constraints-make-better-art.html" rel="alternate" type="text/html" title="" />
		<published>2025-05-09T00:00:00+10:00</published>
		<updated>2025-05-09T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I try to make the websites I build for my clients fun and interesting and
different from your typical cookie-cutter Squarespace websites. Most of them
are inspired by a physical artistic medium, such as
&lt;a href=&quot;https://ecoako.com&quot;&gt;hand-drawings&lt;/a&gt;, a &lt;a href=&quot;https://lebani.dev&quot;&gt;typewriter&lt;/a&gt;, or even &lt;a href=&quot;https://didirri.com&quot;&gt;fridge
magnets&lt;/a&gt;. A little while ago, I was working on a website for a dear
friend of mine and very talented musician. I was looking through some photos
they sent me and brainstorming ideas, when I came up with the idea of making a
collage website.&lt;/p&gt;

&lt;p&gt;I had digital copies of the photos, so the first thing I did was import them
into Inkscape, start chopping them up, rearranging them, and trying to assemble
them into something that looked nice. But I couldn’t for the life of me get
something that I liked. So after messing around on my computer for hours, I
decided to change my approach. I popped all the photos in a word processor
document, not thinking too hard about the size of the images, and took it to my
local library to get it printed. I came home and started hacking away with a
pair of scissors and a gluestick. And suddenly it all started falling into
place.&lt;/p&gt;

&lt;p&gt;I didn’t have the luxury of resizing images pixel-by-pixel on my computer, I
couldn’t edit the photos or duplicate them, I couldn’t adjust the shapes I’d
cut. I couldn’t easily shuffle them around once they were glued down. I was
stuck with what I had. I was limited by the pair of scissors I had, the colour
profile of the printer that I happened to use, the image sizes I happened to
dump into the document for the printer. And yet I was able to come up with
something that really &lt;a href=&quot;https://rowenawise.com&quot;&gt;worked&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The lesson I took away from this experience is that constraints often make
better art. If you have too many choices, such as that afforded by a computer
program, you can spend forever endlessly tweaking the most minute of details.
But all it does is distract you from the big picture. Not only that, it’s the
implications of the things you don’t control, the &lt;em&gt;incidental&lt;/em&gt; constraints that
gives art it’s personality. The timbre of a musical instrument due to the grain
of the wood that it’s made from, the expressiveness of a poem due to the
language you write it in and the words in your vocabulary, the colours of a
drawing due to the pencils you happen to have in your collection. If we remove
these incidental constraints and exert too much control in making things &lt;em&gt;too&lt;/em&gt;
perfect, art loses it’s personality.&lt;/p&gt;

&lt;p&gt;To look at it another way, if engineering is essentially solving a problem by
making decisions within hard, well defined constraints, then art is making
decisions within very loose, almost non-existent constraints. The solution
space for art is &lt;em&gt;much&lt;/em&gt; larger than the solution space for engineering. But you
have to have some mechanism for deciding amongst the infinite possibilities.
That’s effectively what the creative process is&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. But imposing constraints
on yourself by not having a huge pencil collection, or haphazardly dumping
images in a document for printing without thinking too much about it, can be a
very powerful technique for guiding your art. Letting it be guided by
incidental forces which are outside your control can result in art which has
more character and personality.&lt;/p&gt;

&lt;p&gt;If I were to take a guess, I’d say that the reason for this is that that is how
nature works.  Nature is the way it is because of the mechanism of evolution,
which is mostly incidental. So it seems natural that we should be drawn to this
accidental imperfection, and find implicit beauty in it.&lt;/p&gt;

&lt;p&gt;I now typically start all my creative projects on a physical medium, even if it
will eventually end up on a computer. Whether it be pencil and paper, collage,
paint, whatever. Once I’m happy with the result, only then will I transfer it
to a computer to become a website. And I try to remain as faithful to my
physical prototype as possible. As someone who can be quite obsessive and
likes to have a tight control over my environment, this can be challenging. But
the result is very liberating, and I think it results in more beautiful art.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;I see it as a spectrum. On one end, we have the engineering process which is about making decisions within constraints, and on the other end we have the creative process which is about making decisions outside of constriants. Engineering is still creative because the constraints are not all-encompassing - you still have room to make decisions unguided by constraints. Similarly, there is engineering in art because the constraints are not nothing - there are still some constraints to work with. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/05/server-side-includes</id>
		<title type="html">Server Side Includes</title>
		<link href="https://evencuriouser.net/2025/05/server-side-includes.html" rel="alternate" type="text/html" title="" />
		<published>2025-05-04T00:00:00+10:00</published>
		<updated>2025-05-04T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I was reading my RSS feed this morning when I came across &lt;a href=&quot;https://lobste.rs/s/qmhxwf/seeking_answer_why_can_t_html_alone_do&quot;&gt;this Lobsters
discussion&lt;/a&gt; about HTML includes. Reading through the comments
section, I discovered this really nifty technology called &lt;a href=&quot;https://en.m.wikipedia.org/wiki/Server_Side_Includes&quot;&gt;Server Side
Includes&lt;/a&gt;. Turns out this is quite an old technology, and probably went
out of style with the rise of full-blown server-side scripting languages.
Admittedly it is quite limited in what it can do - for instance it doesn’t have
loops. It does have basic logic using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statements though, and it can even
execute CGI scripts. It happens to be supported natively by nginx, my web
server of choice. It is also quite simple to use:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--#include file=&quot;header.html&quot; --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, they are just HTML comments, which means that they are
completely compatible with HTML, and if anything goes wrong, they simply won’t
render. You won’t get any weird error messages, and it won’t prevent the entire
page from loading.&lt;/p&gt;

&lt;p&gt;When I build websites for my clients, I typically subscribe to the YAGNI
philosophy. Most of my websites start life as hand-written HTML and CSS files
(perhaps with a bit of client-side JS if it’s needed). When things get beyond a
few pages and I find myself doing a lot of copy-pasting, I’ll usually migrate
to a Static Site Generator. When things start to get really complex - requiring
server-side logic or databases for example - I’ll move to a server-rendered app
running behind a reverse proxy. But each transition to the right adds
significant complexity, and therefore I only make the jump when it is
necessary.&lt;/p&gt;

&lt;p&gt;The reason why I think Server Side Includes are so cool is that it moves the
threshold for moving from hand-written HTML to using an SSG further to the
right. And this is a &lt;em&gt;good&lt;/em&gt; thing. Moving from hand-written HTML to using an
SSG is a big jump because it introduces a compile step, which has a &lt;a href=&quot;https://jvns.ca/blog/2023/02/16/writing-javascript-without-a-build-system/&quot;&gt;bunch of
consequences&lt;/a&gt;. As a freelancer who manages many websites, every bit
of complexity saved counts. So the more I can delay moving to the next stage in
complexity, the better. And Server Side Includes do just that.&lt;/p&gt;

&lt;p&gt;Websites where I might reach for an SSG just because there are multiple pages
which need to include a common snippet of HTML such as a header or footer, can
now be completely hand-written and compile-free again. Server Side Includes are
very simple and limited in functionality, but that’s why they’re so useful. If
I want looping, or complex logic, I’ll bite the bullet and use an SSG. But if I
just want to avoid copy-pasting a few HTML snippets, Server Side Includes are
the perfect tool for the job.&lt;/p&gt;

&lt;p&gt;It often amazes me how old, forgotten technologies can actually be incredibly
useful, and often dramatically simplify your workflow. It’s a lesson in
stepping back and evaluating technology without the bias of norms and current
trends. Often technological progress is accompanied by a large increase in
complexity, and it’s important to step back and asses the trade-off.&lt;/p&gt;


		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/04/objects-are-lambdas-are-objects</id>
		<title type="html">Objects Are Lambdas Are Objects</title>
		<link href="https://evencuriouser.net/2025/04/objects-are-lambdas-are-objects.html" rel="alternate" type="text/html" title="" />
		<published>2025-04-17T00:00:00+10:00</published>
		<updated>2025-04-17T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;This is the story about how a simple question about some Ruby syntax led me to
finally understanding a key concept in computer science.&lt;/p&gt;

&lt;p&gt;The other day I was wondering why in Ruby you have to use the syntax &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo.call&lt;/code&gt;
to call a lambda. Why can’t you just call it like a regular function? Which -
since you don’t need braces for a function without arguments in ruby - would
simply look like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;. It got me thinking, what exactly is a lambda in
Ruby anyway? Lambdas have three main properties that I can think of:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;They are anonymous&lt;/li&gt;
  &lt;li&gt;They are first-class&lt;/li&gt;
  &lt;li&gt;They capture their environment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In most languages, a lambda is a first-class function. But Ruby doesn’t even
have functions, not really. Check this out:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; :foo&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;private_instance_methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So a “function” definition is really just a private method on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object&lt;/code&gt;. Ruby
doesn’t have functions, it has methods. When you “call” a method, really you’re
sending a message to the object, and in return it gives you a response.&lt;/p&gt;

&lt;p&gt;Now, objects are anonymous and first-class by default - you can pass them
around, and you can assign them to variables. In Ruby, everything is an object,
therefore everything is first-class by default. That’s a really nice
consequence of the “everything is an object” model of computation. In-keeping
with this model, lambda’s should also be objects. And if lambdas are objects,
they would automatically satisfy two of the required properties. Well it turns
out that that’s exactly what they are. Lambdas in Ruby are just objects with a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt; method. Check this out&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Proc&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So you might implement a simple lambda like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But while this is first-class and anonymous, it still doesn’t capture it’s
environment - the third property of lambdas.  In Ruby you can capture the
environment at a particular point in the code using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;binding&lt;/code&gt; object, which
is an instance of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Binding&lt;/code&gt; class. So if we store an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;binding&lt;/code&gt;
in our object at the point where it is created, the object will satisfy all the
properties of a lambda! Thus a lambda could be implemented as an object like
this&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyLambda&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@binding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Low and behold, it behaves just like a lambda:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;MyLambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;foo&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 1&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;my_lambda&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyLambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bar&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; NameError&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;baz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_lambda&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyLambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;baz&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;baz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;my_lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 2&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;qux&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;MyLambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;qux = 2&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qux&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is really neat! Ruby doesn’t need a special concept of first-class
functions because objects are inherently first class, and you can represent a
function as an object. A lambda is just a special case of this, which captures
it’s environment as well&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. So this answers our question of why you need to
use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo.call&lt;/code&gt; to call a lambda in Ruby, and also shows how objects can be used
to represent lambdas. Now let’s look at it from the other direction and work
our way back.&lt;/p&gt;

&lt;p&gt;If Ruby’s philosophy is “everything is an object”, Scheme’s could be
“everything is a lambda” (along with &lt;a href=&quot;https://3e8.org/pub/pdf-t1/the-roots-of-lisp.pdf&quot;&gt;a few axioms&lt;/a&gt;[pdf]). This model
of computation is called lambda calculus. Given that lambdas capture their
environment, we can construct objects using lambdas like this:&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;make-foo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cond&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eq?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eq?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;set-x!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set!&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can then send messages to the object like so:&lt;/p&gt;

&lt;div class=&quot;language-scheme highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;make-foo&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;; =&amp;gt; &quot;bar&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;set-x!&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;; =&amp;gt; &quot;baz&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can define &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; in terms of lambda as well, i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(let ((var1 exp1) (var2
exp2)) body)&lt;/code&gt; is equivalent to: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;((lambda (var1 var2) body) exp1 exp2)&lt;/code&gt;. And
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(define (foo x y) body)&lt;/code&gt; is just syntactic sugar for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(define foo (lambda (x
y) body))&lt;/code&gt;. So (and given that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;define&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cond&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eq?&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set!&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;car&lt;/code&gt; are
primitives of the language), we have simulated objects using only lambdas.&lt;/p&gt;

&lt;p&gt;So in Ruby, we have a model of computation where everything is an object, and
we can use objects to simulate lambdas. In Scheme, we have a model of
computation where everything is a lambda, and we can use lambdas to simulate
objects!  That’s a really elegant symmetry. Objects and lambdas can be
explained in terms of each other, and neither is more fundamental than the
other. Object Oriented and Functional programming are two sides of the same
coin. This is captured nicely by the following saying: “Closures are a poor
man’s objects, and Objects are a poor man’s closures”. Sure the implementation
of objects in Scheme is a little bit ugly, and the implementation of lambdas in
Ruby is a little bit ugly as well. But that’s what is meant by “poor”. And the
ugliness is really just a result of the limitations of the languages in which
I’ve chosen to implement them&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; - some languages will be able to implement
them more elegantly than others. The higher-level idea still stands.&lt;/p&gt;

&lt;p&gt;I had understood for some time that you can simulate objects with lambdas. But
it’s only now that I’ve thought about how lambdas work in Ruby that I’ve come
to understand how lambdas can be simulated with objects. This is not a new idea
by any means, but it was a fun journey getting to this point. And I feel like I
have a much better understanding of the relationship between Functional and
Object Oriented models of computation.&lt;/p&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further reading:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html&quot;&gt;RE: What’s so cool about Scheme?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://wiki.c2.com/?ClosuresAndObjectsAreEquivalent&quot;&gt;Closures And Objects Are Equivalent&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Abelson and Sussman, Structure and Interpretation of Computer Programs - 2nd Edition, The MIT Press, 1996.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;In case you’re wondering why the class of a lambda is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt;, technically lambdas are implemented as Procs, which behave slightly differently from lambdas. Procs have a predicate function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lambda?&lt;/code&gt; which can be used to distinguish between them. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;Having to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eval&lt;/code&gt; here is kind of ugly, but it’s the only way I can think of to evaluate an expression in a given execution context. If we don’t care about the environment capture property, then implementing a lambda as an object is trivial. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;The implementation of lambdas in Ruby does this by default, so you don’t have to explicitly pass in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;binding&lt;/code&gt; instance each time you create one as we do here. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;Of course, in Scheme, you can use macros to abstract away that ugliness. That is how most object systems in scheme are implemented. Maybe lambdas could be implemented more elegantly in Ruby using it’s metaprogramming features? &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/04/on-having-a-thing</id>
		<title type="html">On Having a &apos;Thing&apos;</title>
		<link href="https://evencuriouser.net/2025/04/on-having-a-thing.html" rel="alternate" type="text/html" title="" />
		<published>2025-04-06T00:00:00+11:00</published>
		<updated>2025-04-06T00:00:00+11:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I’ve been through a lot of personas and a lot of hobbies throughout the journey
of figuring out who I am. And I’m sure this is not a unique experience.
Although by now I’ve more or less plateaued on this journey, I still have a
&lt;em&gt;lot&lt;/em&gt; of different hobbies. It’s always made me feel somewhat unsettled, like I
don’t have a singular, cohesive and well defined identity&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. I don’t have a
&lt;em&gt;thing&lt;/em&gt;. I’ve got many things. I’ve always romanticised people who have a
thing. Something that everyone knows them by. Sometimes I think about what my
“tag line” would be. Every time I try and write one down, I keep adding to it
and modifying it over time. I can’t succinctly say in a sentence, “what do I
do?”&lt;/p&gt;

&lt;p&gt;Someone once told me the story of how on the day he retired, Hayao Miyazaki -
writer of the beloved Studio Ghibli films - went back to the same old desk at
the same old place, and just kept drawing. Only now he was doing it for free.
That is probably the best example I can think of of the saying “do what you
love and you’ll never work a day in your life”. That’s a human who knows what
he loves, who knows what his &lt;em&gt;thing&lt;/em&gt; is. I also once read about Alan Turing,
who had two big passions: computing, and long distance running. Long distance
running was how he sparked creativity and freed his mind to solve the difficult
problems in his work. So it’s still really a single cohesive &lt;em&gt;thing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I romanticise this idea so much and yet I’ve never been able to obtain it. I
love computer programming and I can and do do it for free. But I can’t say that
it’s my &lt;em&gt;thing&lt;/em&gt;, because there are many other things in my life that I also
love. Things like music, philosophy, electronics, drawing, hiking, reading,
writing. The list is goes on. Further, it constantly changes over time. One
week I’m an artist, the next I’m an engineer, the next I want to spend all my
time outside exploring. Even with programming languages I’ve never identified
with a single one. I’ve never been a “rubyist” or a “lisper”. But maybe it’s
okay to be a jack-of-all-trades. In fact, the full quote actually reads “A Jack
of all trades is a master of none, but often times better than a master of
one.” I’m sure Miyazaki and Turing had more passions and hobbies than just the
things they are renowned for. They get talked about in that way because it
makes for a nice story. Because society as a whole also seems to romanticise
this idea of having a &lt;em&gt;thing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There is an extraordinary satisfaction that comes from devoting your life to
one thing, exploring it in the utmost depth, knowing it inside and out. Making
a commitment to one thing necessarily means excluding all else. And that in
itself is powerful. It’s not just about the thing you’ve said yes to. It’s also
about all the things you’ve said no to. Programming is the thing that I’ve spent
the most time doing in my life. It’s probably the closest thing I have to a
&lt;em&gt;thing&lt;/em&gt;. I’ve been doing it for close to 10 years and I’m only just beginning
to experience this satisfaction of depth. I can only image how I will feel when
I’ve been doing it for 40 years. Even then, my programming career has also been
quite broad, I can’t imagine how rewarding it would be to stick with a &lt;em&gt;single
field&lt;/em&gt; of programming for 40 years.&lt;/p&gt;

&lt;p&gt;However there is also something to be said for experiencing the richness of
life, which comes from having a broad range of experiences. It makes you more
“worldly”. And having a wide range of experiences under your belt, when you do
say no to things, you know exactly what you are saying no to. I think that a
rich life is a fulfilling life. And to live a rich life means you need to do
and experience many different things. David Bowie is one of my favourite
artists. Not necessarily for his music (though I do like his music), but as an
artist. His &lt;em&gt;thing&lt;/em&gt; is that change itself &lt;em&gt;is&lt;/em&gt; his identity. And as someone who
has never managed to stick to one persona for too long, this is very
comforting. We romanticise those who have a &lt;em&gt;thing&lt;/em&gt;. But the reality is that
not everyone is like that. Some people are generalists. And that should be
celebrated too.&lt;/p&gt;

&lt;p&gt;So it really boils down to the question: is it better to have one thing that
you commit really deeply to, or to have a more shallow experience of many
different things?&lt;/p&gt;

&lt;p&gt;Depth vs breadth.&lt;/p&gt;

&lt;p&gt;Well, as with most things, the unsatisfying truth is that the answer is
always a balance. Think of it like a bell curve. Have one or two things that
you know really well or are really skilled in, that you make your life’s work,
your identity, your &lt;em&gt;thing&lt;/em&gt;. And have lots of other things that you enjoy
spending your time on. That way you get to experience the beauty and
satisfaction of depth, as well as the richness that comes with breadth.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bell-curve.png&quot; alt=&quot;Picture of a bell curve&quot; /&gt;&lt;/p&gt;

&lt;p&gt;While I have no doubt that the likes of Miyazaki and Turing have other
passions, I also have no doubt that their bell curves are much sharper than
most. People who are known for having a &lt;em&gt;thing&lt;/em&gt; typically have curves that look
like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bell-curve-narrow.png&quot; alt=&quot;Picture of a really sharp bell curve&quot; /&gt;&lt;/p&gt;

&lt;p&gt;While people who are more of a jack-of-all-trades have a bell curve that
looks more like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/bell-curve-wide.png&quot; alt=&quot;Picture of a flat bell curve&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Again, neither is right or wrong. Everyone is different, striking a different
balance between depth and breadth.&lt;/p&gt;

&lt;p&gt;But how do you figure out what lies at the center of your bell curve and what
sits at the fringes? Don’t try to force it, or try to sit down and plan it all
out. Let it fall out organically. If you do the things you enjoy the most, your
bell curve will fall into place. Discover, don’t define. Don’t listen to what
others think, or what you think you “should” do. Follow your heart. The biggest
challenge then of course, is learning how to listen.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;I’m sure there are many definitions of identity, and that itself is a deeply philosophical matter. When I talk about identity here, I am talking about it in terms of what you do with your life, what your passions and hobbies are, what your &lt;em&gt;thing&lt;/em&gt; is. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2025/03/a-strategy-for-documenting-non-obvious-code</id>
		<title type="html">A Strategy For Documenting Non-Obvious Code</title>
		<link href="https://evencuriouser.net/2025/03/a-strategy-for-documenting-non-obvious-code.html" rel="alternate" type="text/html" title="" />
		<published>2025-03-09T00:00:00+11:00</published>
		<updated>2025-03-09T00:00:00+11:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;When it comes to commenting code, I typically subscribe to the “comments should
describe code that is non-obvious” philosophy. I won’t go into the details of
why I subscribe to this method, because there are many resources that talk
about it in great depth already. For one such example, see &lt;em&gt;A Philosophy of
Software Design&lt;/em&gt; by John Ousterhout&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;One of the challenges with this is that sometimes it’s difficult to know what
is obvious and what is non-obvious when writing code. Sometimes it doesn’t even
occur to me that something is non-obvious while I’m writing it (I know &lt;em&gt;why&lt;/em&gt;
I’m writing it at the time), so I don’t even think to ask myself the question.
There is however a guaranteed way to determine that something is non-obvious:
when you go back to some code you’ve written in the past and you can’t figure
out why the heck you’ve written it the way you have. Sigh, hindsight…&lt;/p&gt;

&lt;p&gt;So whenever this happens to me, I’ll spend some time figuring out why I wrote
it the way I did. Once I’ve figured it out, I’ll retrospectively add a
comment describing my findings. This has two benefits:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Over time, my code becomes better documented&lt;/li&gt;
  &lt;li&gt;My brain starts to recognise patterns, and over time I start to get better
at recognising non-obvious code as I write it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Time for example…&lt;/p&gt;

&lt;p&gt;I was recently working on a web project using &lt;a href=&quot;https://alpinejs.dev/&quot;&gt;Alpine.js&lt;/a&gt; and I
needed persistent storage across page loads. It’s not something I use
frequently enough that it’s already stored in my head, so I referred to a
previous project where I knew that I had used it. I found the following
(paraphrased) line:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;$persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I couldn’t remember why I’d explicitly asked it to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sessionStorage&lt;/code&gt; (or
even what the default storage mechanism is for that matter). There must be a
reason I didn’t just use the simpler &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this.$persist(0)&lt;/code&gt;. So I looked it up in
the Alpine docs and discovered that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$persist&lt;/code&gt; uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; by default.
Aha! I clearly wanted the stored value to be dropped when a user closed the
tab. So I put in a little comment explaining that:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Use sessionStorage instead of the default localStorage so that&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// the data is cleared when the tab is closed.&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;$persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now if I ever come back to that code, I know exactly why I explicitly asked for
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sessionStorage&lt;/code&gt; and what the default storage mechanism is. And my brain is
just that little bit better at picking up patterns of non-obvious code.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;John Ousterhout, A Philosophy of Software Design, Yaknyam Press, 2022. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2024/12/the-arts-are-not-drugs</id>
		<title type="html">The Arts Are Not Drugs</title>
		<link href="https://evencuriouser.net/2024/12/the-arts-are-not-drugs.html" rel="alternate" type="text/html" title="" />
		<published>2024-12-14T00:00:00+11:00</published>
		<updated>2024-12-14T00:00:00+11:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I recently came across a quote by E. M. Forster which I’ve been thinking
about a lot.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Arts are not drugs. They are not guaranteed to act when taken. Something
as mysterious and capricious as the creative impulse has to be released
before they can act.&lt;/p&gt;

  &lt;p&gt;– E. M. Forster&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I discovered this quote indirectly in the book &lt;em&gt;Musicophilia&lt;/em&gt; by Oliver Sacks
&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; (I would definitely recommend checking this out if you are interested in
music and/or neurology). I haven’t been able to find a reliable source for the
origin. There are many layers to this quote. The quote is written in the
context of music, and music for me is the most powerful of the arts, so I will
talk about it primarily in the context of music. But I have no doubt it applies
equally to the other arts as well.&lt;/p&gt;

&lt;p&gt;The top layer - the overall message of the quote, I can directly relate to.
Often I find myself looking to music in order to help “cure” how I am feeling.
That’s not to say I’m always looking for it to cheer me up. Sometimes listening
to sad music is just the right thing when you’re feeling sad. Sometimes you’re
looking for that deep sense of relatability that music can provide. Sometimes
catharsis. Sometimes you just want to feel a certain way, whether that be
happy, sad, nostalgic, angsty, etc. But sometimes it just doesn’t work. More
often than not, it doesn’t work when you try to force it. It has to arise
spontaneously and unexpectedly. This idea is profound to me because it is
deeply relatable, and because it is novel - it’s not something I’ve heard
talked about before.&lt;/p&gt;

&lt;p&gt;Dive a layer deeper, and the quote hints at the enigmatic nature of creativity.
Most creatives intuitively understand that ideas seem to just “arrive” in your
brain. There’s not really any reliable process you can use to spark it. And it
often leaves you with the feeling that you didn’t really come up with the idea
yourself, rather it was planted in your head by some greater power. Julia
Cameron talks about this in her book &lt;em&gt;The Artists Way&lt;/em&gt; &lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. She suggests that
an artist is merely a channel or conduit, through which ideas flow from the
heavens (for lack of a better term), that an artist is someone who just so
happens to know how to convert those ideas to an earthly representation. What
is most fascinating about all this is that the quote hints at this enigmatic
nature of the creative process, but seems to take it as assumed knowledge. It
is in itself a pretty deep concept, but E. M. Forster decides that it’s
well-known enough to use it as an analogy to explain a yet more
thought-provoking idea.&lt;/p&gt;

&lt;p&gt;Finally, the quote suggests an elegant mirror between the processes of artistic
creation, and artistic consumption. It suggests that they are both equally as
“mysterious” and “capricious”. Maybe it makes sense that experiencing the
result of a process so enigmatic should also be shrouded in a similar level of
mystery. This needs more thought… If nothing else, the symmetry is deeply
satisfying.&lt;/p&gt;

&lt;p&gt;I don’t recall hearing anyone talk about the arts in this way before, yet upon
reflection, it is something I experience deeply. I’m sure it’s quite
commonplace for others to experience too. It is one of those truths that you
might not have necessarily thought about, but when you hear it, you immediately
know it to be fundamentally true. It’s also comforting to know that when I have
moments where the arts don’t seem to “work”, it’s not because I’ve become an
unfeeling zombie, it’s because the arts are not something that can be called
upon on demand. And in a way, that’s what makes the experience so beautiful.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Oliver Sacks, Musicophilia, Picador, 2011. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;Julia Cameron, The Artists Way: A Spiritual Path to Higher Creativity, Souvenir Press, 2021. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2024/07/wabi-sabi-and-dotfiles</id>
		<title type="html">Wabi-sabi and dotfiles</title>
		<link href="https://evencuriouser.net/2024/07/wabi-sabi-and-dotfiles.html" rel="alternate" type="text/html" title="" />
		<published>2024-07-21T00:00:00+10:00</published>
		<updated>2024-07-21T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;One of the things I love about computers and open-source software is that you
can customize &lt;em&gt;everything&lt;/em&gt;. If you don’t like how something looks or functions,
you can just tweak it to your hearts content. If the software doesn’t offer a
config option for it (which is rare, as a lot of open-source software is
&lt;em&gt;extremely&lt;/em&gt; flexible), you can always modify the source code yourself. There is
no end. In a world where I constantly feel overwhelmed by how complex things
are, and how little control I have of the world around me, this is a very
appealing respite.&lt;/p&gt;

&lt;p&gt;I’ve recently found myself with a lot more free time than usual, and I’ve been
spending a lot of time customising my &lt;a href=&quot;https://git.sr.ht/~evencuriouser/dotfiles&quot;&gt;dotfiles&lt;/a&gt;. I’ve also been
thinking a lot about the Japanese philosophy of &lt;a href=&quot;https://en.wikipedia.org/wiki/Wabi-sabi&quot;&gt;Wabi-sabi&lt;/a&gt;. By the
work of the higher powers that be, this &lt;a href=&quot;https://arkadiuszchmura.com/posts/stop-perfecting-your-config&quot;&gt;article&lt;/a&gt; came into
my RSS feed the other day. It was a bit of a much needed slap in the face.  It
talks about why we should stop spending so much time configuring everything,
and even delves into the deeper, more philosophical reasons for doing this.&lt;/p&gt;

&lt;p&gt;There is a &lt;a href=&quot;https://buttondown.email/hillelwayne/archive/keep-perfecting-your-config&quot;&gt;counter argument&lt;/a&gt; to this. Which is only sort of
a counter argument, in that it argues that it’s okay to spend time making
tweaks which make you more productive, but not to spend time making superficial
tweaks such as colours and aesthetics.&lt;/p&gt;

&lt;p&gt;I’m also a creative, and aesthetics are important to me, which makes this a
hard pill to swallow. Having an environment that appeals to me aesthetically
makes me happier, so surely more productive? But alas, the Wabi-sabi principle
applies specifically to aesthetics. There is beauty in inconsistency, in
imperfection. Some of my best art comes from the tightest of constraints. So
maybe I should embrace this?&lt;/p&gt;

&lt;p&gt;I took a little advice from both articles - I stripped a bunch of stuff out of
my dotfiles which I don’t need. I’m rolling with the imperfect defaults. I made
one final commit - &lt;a href=&quot;https://git.sr.ht/~evencuriouser/dotfiles/commit/7d5573d6ed7b1a209ec400af2dfe812181a2fa99&quot;&gt;Wabi sabi (7d5573d)&lt;/a&gt;. My goal is to not make
another commit for at least a month. Hopefully putting this short story out
into the world will hold me accountable to that goal.&lt;/p&gt;


		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2024/06/10-things-i-love-about-winter</id>
		<title type="html">10 Things I Love About Winter</title>
		<link href="https://evencuriouser.net/2024/06/10-things-i-love-about-winter.html" rel="alternate" type="text/html" title="" />
		<published>2024-06-20T00:00:00+10:00</published>
		<updated>2024-06-20T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;We’re right in the thick of winter here in Melbourne. Recently I asked a dear
friend of mine to make a list of 10 things they love about winter, in order to
help stay positive in this dreary time of year. I thought maybe I should make
one of my own, so here it is…&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Going for a walk at night after it’s been raining. Seeing the glistening of
all the different coloured lights reflecting off of the water on the
streets.&lt;/li&gt;
  &lt;li&gt;Listening to records. The soft crackle of old vinyl breaking the moody
silence between tracks, for some reason sounds so much better in winter.&lt;/li&gt;
  &lt;li&gt;The food.&lt;/li&gt;
  &lt;li&gt;The outfits.&lt;/li&gt;
  &lt;li&gt;Endless cups of tea.&lt;/li&gt;
  &lt;li&gt;An excuse to stay at home and read or write or watch a movie.&lt;/li&gt;
  &lt;li&gt;Rugging up with blankets and scarfes and gloves and sharing music and
stories around a fire at night time.&lt;/li&gt;
  &lt;li&gt;The feeling of getting home after a freezing cold walk or bike ride. Slowly
defrosting when your nose and ears are numb from the cold.&lt;/li&gt;
  &lt;li&gt;Waking up, making coffee, crawling back into bed to drink your coffee.&lt;/li&gt;
  &lt;li&gt;Moody music while it’s pouring with rain outside. Listening to the sound of
the rain, feeling grateful for being warm and dry, feeling the mood in your
whole body.&lt;/li&gt;
  &lt;li&gt;That singular moment when the sun comes out from hiding. Feeling the it’s
rays penetrating deep into your body and warming your soul.&lt;/li&gt;
&lt;/ol&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2024/05/a-handy-alias</id>
		<title type="html">Sudo roulette</title>
		<link href="https://evencuriouser.net/2024/05/a-handy-alias.html" rel="alternate" type="text/html" title="" />
		<published>2024-05-15T00:00:00+10:00</published>
		<updated>2024-05-15T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I discovered a handy alias.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo_roulette&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$RANDOM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /
    &lt;span class=&quot;k&quot;&gt;else
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;alias sudo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;sudo_roulette
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please don’t actually do this :grimace:&lt;/p&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2024/04/make-coffee</id>
		<title type="html">Make coffee</title>
		<link href="https://evencuriouser.net/2024/04/make-coffee.html" rel="alternate" type="text/html" title="" />
		<published>2024-04-28T00:00:00+10:00</published>
		<updated>2024-04-28T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;&lt;a href=&quot;/make-coffee&quot;&gt;Make coffee&lt;/a&gt;&lt;/p&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2024/04/til</id>
		<title type="html">TIL</title>
		<link href="https://evencuriouser.net/2024/04/til.html" rel="alternate" type="text/html" title="" />
		<published>2024-04-09T00:00:00+10:00</published>
		<updated>2024-04-09T00:00:00+10:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;TIL: The characters &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$&lt;/code&gt; used to move to the start and end of the line
in vim are the same as the regex characters that match against the start and
end of a string.&lt;/p&gt;

		</content>
	</entry>
	
	<entry>
		<id>https://evencuriouser.net/2024/04/first-post</id>
		<title type="html">First post</title>
		<link href="https://evencuriouser.net/2024/04/first-post.html" rel="alternate" type="text/html" title="" />
		<published>2024-04-07T00:00:00+11:00</published>
		<updated>2024-04-07T00:00:00+11:00</updated>
		<author>
			<name>Aron Lebani</name>
			<uri>https://evencuriouser.net</uri>
		</author>
		<content type="html">
			&lt;p&gt;I plan on using this space to share small updates with the world. It might be
something short I’ve written, what I’m up to at the moment, the latest book I’m
reading, a photo I’ve taken… I’ll try to keep adding to it over time. See how
we go.&lt;/p&gt;

		</content>
	</entry>
	
</feed>
