<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Zach R. Burke</title>
        <link>https://zachrburke.com/</link>
        <description>Recent content from my personal blog</description>
        <lastBuildDate>Invalid Date</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Zach R. Burke</title>
            <url>https://zachrburke.com/favicon.png</url>
            <link>https://zachrburke.com/</link>
        </image>
        <copyright>All rights reserved 2014, Zach Burke</copyright>
        <category>Technology</category>
        <atom:link href="https://zachrburke.com/feed.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Being Married]]></title>
            <link>https://zachrburke.com/blog/being-married</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/being-married</guid>
            <pubDate>Invalid Date</pubDate>
            <description><![CDATA[<html><head></head><body><p>I don't write often about anything that is deeply personal despite this website
being my personal blog. On February 16th, 2025, I asked Madeline to marry me.
The proposal was a surprise I decided to spring on her while spending the
weekend in Denver. You might think as a Texan why we were in Denver in
February, during a particularly harsh blizzard. We were exploring the option of
possibly living there given we both had jobs we could work remotely, and we
wanted to see Denver at what we felt would be the harshest weather, given the
relatively mild winters in Texas.</p>
<p>I remember each of Madeline's reactions as we approached what I not so
delicately placed on our itinerary as a surprise.  Given our plans of
potentially moving, it made sense that we would spend time walking in the cold
snow, looking at potential houses and picturing what a life in Denver might
look like. Maybe we would fill our houses with the latest outdoor fashion and
live our new lives as another one of those couples that spends all of their
weekends hiking, possibly even trail running, leaving all of our friends in awe
as we stay fit and brag about the number of miles and steps we get each day.
We could have a catio where the cats would paw desperately for attention from
passing neighbors.  We could take a bus to Union Station and check out the
latest hip new coffee shop in downtown Denver, or walk down ___ street and grab
some new antiques to decorate the house.  Attend book clubs and meet new
friends.</p>
<p>However, this neighborhood in particular did not inspire much confidence for
us, especially Madeline.  As we walked around our surprise destination, waiting
for a specific moment in time she tried to guess the surprise.  One guess was
"you take me to an air bnb you own, and that's where you strangle and murder
me. I always knew it was too good to be true."  We passed a junvenile detention
center and pondered what we might do as parents if our kids ever ended up in
"juvi."</p>
<p>The clock hit 6:30, and it was time. I navigated us to our destination we had
passed several times, feeling anxious. She had no clue, and I was worried that
I may have to change my plans that evening, despite quietly announcing to her
Dad before he dropped us off at the airport what I was planning to do this
weekend. Something Madeline had announced that weekend, which wasn't entirely a
shock, was that she "did not like the taste of beer, and maybe it's time to
stop trying to like it".  Knowing her parents were awaiting news of our
proposal, yet also her new disgust towards beer, I was rapidly pondering ways
to back out of proposing at the Oakwell Beer Spa in Denver. Despite getting the
Valentines Day lovers special, the surroundings gave little confidence that the
"spa" far exceeded the "beer" part of that experience.</p>
<p>I was in luck as we passed through the door and were greeted with both a well
put interior and an exceptionally warm staff. They explained the experience to
her, and she was immediately giddy with excitement. One thing I'll always love
about Madeline is her enthusiam towards things, and despite her statements
about beer, she was really excited about getting to take a dip in what was
effectively a hot bath loaded with beer hops. That and there were samples of
wine and cider for her to choose from.</p>
<p>We got to our private spa room as it filled with hot water, sampled with our
chosen set of minerals and hops, and dined on a basket full of chocolate
covered strawberries, and made mimosas out of champagne and orange/cranberry
juice. After some time in a thermal spa, and a shower, we moved to the tub,
where I would give her a hand crafted valentines day note.  As she finished
reading, that's when I popped the question. Madeline claims she blacked out
several times that evening unable to process everything. I'll never forget
her expression as we entered the spa, when she got to see a giant dispenser
full of different wines and ciders, when she saw the strawberries and
champagne, and when she saw the temporary ring I used to propose to her
from James Avery.  Her expression through all of it was full of the same
infectious enthusiasm I fell in love with.</p>
<p>I'm writing this on April 13th, 2026, two days after our wedding. Madeline did
all of the planning for the honeymoon, including buying the plane tickets to
our location, Japan. It's worth noting that her Dad, Ted, retired working at
American Airlines. I was totally unaware of what was going to happen when we
went to the Admiral Lounge at the DFW airport. "My dad got us into the lounge"
she said, correct in that I would assume this was his gift to us for our
honeymoon. I had wondered why he only gave us access to the lounge this time
and never before, but I was touched by the idea.</p>
<p>After enjoying a full breakfast buffet with french toast bread pudding, we
left for our gate uncharacteristically early. Apparently, Ted had also
got us priority boarding, a suspicious perk.</p>
<p>Now, to my surprise, I am sitting here in a massive adjustable
seat, that can fold down into a full bed, in the first class cabin of
AA0175 in a first class seat. A type of seat going extinct as they create
room for more business class seats. It's always been a dream of ours to
fly first class on a bed in the sky as we approach a faraway country, and
now we're both living it.  Stuffed with wine and the 100th anniversary
beef wellington, I couldn't be happier.</p>
<p>As it turns out, these were all purchased by Madeline, as part of a wonderful
planned surprise. A full year later, my surprise Oakwell Spa visit
has been outdone.</p>
<p>I've always had these small pockets of the internet where I would choose
to reside. I publish ideas in these corners but I never try too hard to make
them seen by others. In the case of this blog, not worrying about who reads
it has been very freeing. It's a kind of "social media" if you will where the
"social" part is prioritized last. By being social last, I've always felt
more comfortable being my full introspective self. Alas, this sense of comfort
is false as I focus more on the technical details of my profession, and none
of the personal details of my own life. This has been a personal flaw that
I've slowly been correcting for the last 6 years.</p>
<p>I've been hesitant to post openly about being engaged because I didn't know
where this fit.  Of course, there are the normal socials, IG and
Facebook. But should I write about it, what about my bluesky? To be fair, I
never know what to post on bluesky.</p>
<p>As I reflect on an amazing wedding, and our wonderful relationship, in this
surprise first class seat, it dawned on me that being married means sharing
everything. In celebration of living the rest of our lives together, as we
embark on what bodes to be a wonderful honeymoon (here's hoping your throat
is getting better!), I wanted to welcome you, Madeline, to my little
corner on the internet where I write about weird techy things and my life
sometimes.</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[From the All-Stream to Baby-Streams]]></title>
            <link>https://zachrburke.com/blog/all-streams-to-baby-streams</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/all-streams-to-baby-streams</guid>
            <pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>Back in 2021, event sourcing was a concept I had read about but never put into practice.  I was looking for an opportunity to try it out and at <a href="https://kaizen.io">kaizen.io</a> I got my chance in the construction domain for an erosion control company.</p>
<p>My first event sourced system tried to test the boundaries of storage and time by storing all events in a single stream.  Don't ever do this.  At the time I thought the number of events for a single division would never be big enough for it to matter.  The problem is if all the events are tangled together, then you have to read about year-old events to understand what's happening right now.  Both fortunately, and unfortunately for me, this system still proved to be very useful, and I was stuck supporting this very awkward model.</p>
<p>I had thought for some time I was stuck in this reality, but after some deliberation (perhaps years of deliberation) I saw a way out.  To understand the answer, I have to explain lifetimes.  An event by itself can live on indefinitely, and that's fine.  What may not live on is the thing that event happened to.  A work order may have been completed a year ago, and it's separate from today's work order, so I don't need to look at both to understand today's work order.  If I take this further, I may not care about year-old work orders altogether, and can safely delete or archive them away to some other form of persistence altogether, like a PDF of the final state sitting in cold storage.</p>
<p>A lifetime is the answer to the question, "how long do I want to keep this thing around?", or "how long is this relevant?"  These are questions you should always be asking when you're choosing events.  Before we can ask this question, another more important question needs to be asked.  "What is this happening to?" Is it a work order, a cart, a residential subdivision?  In the all-stream case, there was no "what", or perhaps the "what" could be the entire world, which is its biggest drawback.  In truth, every event <em>did</em> have some kind of identifier associated with it, which was key in digging out of my hole.</p>
<p>Individual lots had identifiers, so I knew how to look up the material counts for particular addresses.  These lots each have bids tied to them so that identifier is very important!  Lots belong to a community which has an identifier.  When a crew was assigned to visit that subdivision, that assignment had its own identifier.</p>
<p>I've had the fortune of learning from this mistake in future projects and created event models where every event has a distinct <code>StreamId</code> to identify the "what" it belongs to.  In the examples I listed above, this "what" already gets tricky even if I had known this from the start.  Domain-driven design has terminology for this, but I find it simpler to think of entities that can have children, and those children can have children.</p>
<p>My approach to modeling this relationship in the identifier is to simply make the id reflect it in a straightforward way.  So if I wanted to know about lot 212 in community 2 for customer 12, then I would get all the events for <code>community/12-2/lot/212</code>.  A nice little resourceful name.  This gives us the benefit of being able to query the events for a specific lot, or all of those events for the subdivision.  There are cases where we want to follow the lot, but we are also providing analytics at a community level, so it's important we can do both.</p>
<p>So from here, my solution finally reveals itself.  I don't need to do a big migration or modify my event schema, or at least not the events directly.  I simply need to add some metadata based on the event data that helps me identify the "what" for each event.  I'm lucky those identifiers already exist.  Here's how I do that:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">return</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> @event </span><span style="color:#F57D26;--shiki-dark:#E69875">switch</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">    // Lot-related events</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    LotServiced e </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> $</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"community/{e.CustomerId}-{e.CommunityId}/lot/{e.LotId}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    InstallmentBilled e </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> $</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"community/{e.CustomerId}-{e.CommunityId}/lot/{e.LotId}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">    // Assignment-related events (use assignment ID as stream)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    CrewAssigned e </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> $</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"assignment/{e.AssignmentId}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    CrewAssignmentRemoved e </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> $</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"assignment/{e.AssignmentId}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    WorklogSubmitted e </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> $</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"assignment/{e.AssignmentId}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">    // Community-level events</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    CrewDispatchedToCommunity e </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> $</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"community/{e.CustomerId}-{e.CommunityId}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">    // Purchase Order events (EPO is a type of purchase order, so they share the same stream)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    PurchaseOrderReceived e </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> $</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"purchase-order/{e.Id}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    EpoCompleted e </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> $</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"purchase-order/{e.Id}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    _ </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> throw new </span><span style="color:#8DA101;--shiki-dark:#A7C080">ArgumentException</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">($</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"Unknown event type: {@event.GetType().Name}"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">};</span></span>
<span class="line"></span></code></pre>
<p>Using this to add a <code>StreamId</code> to new events, plus an additional batch update to existing events to include this new field was fairly straightforward.  That is because nothing about the system changes aside from some extra storage.  I still depend on reading the full history of events to build my views today, but this provides the mechanism to get away from that.</p>
<p>Even before adding new view builders that use the <code>StreamId</code>, this had a unique operational benefit.  If I knew anything about a community that was having problems, it was easy for me to query all the events for a community.  Beforehand, it was surprisingly difficult and often required me having to write a throwaway application just to see the events.</p>
<p>These events live in a Postgres database as a single table.  It's straightforward to get all the events for a lot or an assignment, but what about a community?  I do consider lot serviced to be events on the community and that is probably one of the most important use cases.  It turns out a <code>LIKE</code> query here works very well given the predictable nature of our <code>StreamId</code>.  By itself, a query like <code>community/2-12/%</code> can take seconds if there are over 2 million events in the table!  Luckily, a cheap index brings this down to milliseconds.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">CREATE</span><span style="color:#F85552;--shiki-dark:#E67E80"> INDEX</span><span style="color:#8DA101;--shiki-dark:#A7C080"> idx_community_maintenance_events_stream_pattern</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">ON</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> community_maintenance_events(stream_id varchar_pattern_ops);</span></span>
<span class="line"></span></code></pre>
<p>This index treats the <code>StreamId</code> as a set of raw byte values.  By doing this, it can use a b-tree lookup to find the exact range of values, or entities in this case, that fall between <code>community/2-11/</code> and <code>community/2-13/</code> rather than scanning the entire table and checking for every entry that matches the pattern.</p>
<p>I still depend on the all-stream today, but I have pulled pieces of functionality away from it that now leverage these "baby streams" and I'm much happier with how these re-writes are working. Maybe in the next year, I'll be done with the all-stream altogether.</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Moving to Neovim and Neogit]]></title>
            <link>https://zachrburke.com/blog/moving-to-nvim</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/moving-to-nvim</guid>
            <pubDate>Sun, 01 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><h3 id="whatvimisnot">What Vim is not</h3>
<p>I think it's important to point out that nothing I'm going to write about here is
going to sell nvim as this tool that made me magically more efficient.  It
could have made me a faster developer, but it also could have made me slower.
Nothing here 10x'd my speed as a developer and I get just as giddy when I see
VS Code open in the wild as I do nvim. You can use whatever editor or
methodology to create software.  This is about my personal experience with nvim
and what I believe my motivations to be.</p>
<p>Any idle glance at programming content on YouTube is often very annoyingly
stuffed with videos about developers 10x, 40x, 100x'ing some thing that I
assume has to do with their productivity.  At best, this means they are
writing 10x the code, which != more efficient. Lines of code are a liability
not an asset.  There is no metric I know of to measure a software developer's
speed, and I think that's also true for the people creating this kind of
content.</p>
<h3 id="motivation">Motivation</h3>
<p>I have wanted to try out neovim for years, solely out of
curiosity, and maybe even a little insecurity. I've only ever used vim a few
times when on the server, and when occasionally committing a file. I've never
tried it as an editor for writing code. Vim has a reputation as a tool that
makes you fast, and I'll admit that I wanted to see if there was something to
the hype.</p>
<p>I didn't get speed, but instead I got something else.  A massive change to the
way I write code and edit text, that shakes up what I've been doing for the
last decade of programming. I could write the same function in nvim that I
wrote a year prior and it would suddenly end up being so much more fun, simply
because the interface I'm using to write the code is different. It makes coding
feel as novel as it did when I first began web development back in late 2013.
I acquired a dopamine hit that made writing code more enjoyable.</p>
<h3 id="transition">Transition</h3>
<p>I started the move in August last year while waiting on a flight to Philly
where I planned to work remote for that month. I wanted a break from my current
side project and decided to run nvim. I went through <code>:Tutor</code> from beginning to
end, then I went through it again and stopped at each chapter to try and apply
each lesson in my own code. I took to trying to add features to my side project
very slowly with the raw distribution, then began watching videos on how people
customize vim. I found
<a href="https://www.youtube.com/playlist?list=PLsz00TDipIffreIaUNk64KxTIkQaGguqn">typecraft's</a>
channel to be really helpful here, and much of my current configuration borrows
from some of the stuff he shows, namely how nvim and tmux can be configured to
work together. I still use <code>&lt;leader&gt;-r</code> to <code>cargo run</code> my current bevy project.</p>
<p>In Philly while I was getting the hang of my own configuration, I chanced upon a
meetup where <a href="https://gvasquez.dev/">Graham Vasquez</a> was giving a presentation
on how to use vim. Honestly it was perfectly timed because I was just getting
the hang of my own personal configuration. The presentation was great, and I
highly recommend it if you're in the area and he feels like giving it again!</p>
<p>I know there are distros out there that give you a nice pre-configured nvim
experience, but I wanted to really think about how vim and modal editing could
change the way I work.  I was worried that using a distribution would get me
too close to VS Code with vim motions, which is already achievable in VS Code.</p>
<p>I still try to be somewhat minimal in my configuration. I only recently added
nvim-notify to address the idiosyncratic way that vim shows error
messages. I don't do anything special with my status bar yet, but I have
customized the status bar with tmux so I haven't had to worry about that.</p>
<p>That being said, if you're reading this and considering the transition, I
would recommend looking at TJ DeVries's <a href="https://www.youtube.com/watch?v=m8C0Cq9Uv9o&amp;pp=ygUKdGogZGV2cmllc9gGiAk%3D">getting started
guide</a>
which walks through using kickstart to get to where I got a little bit quicker.</p>
<h3 id="howitfeelsandwhythatsimportant">How it feels and why that's important</h3>
<p>Multi-modal editing is a joy.  We're used to thinking the arrow keys are the
only way to get around a text document, so it's easy to think the mouse is the
best option to navigate a screen.  When you're in "Normal" mode in vim, you can
navigate using what are called vim motions.  These allow you to navigate the
document efficiently without your hands leaving the keyboard.  Many have
struggled to capture how this works, and the only way to really "get it" is to
try it.</p>
<p>For me personally, as someone who has invested over 10 years of playing
fighting games, it makes the editor feel like training mode in a game like
Street Fighter 6 or Tekken 7.  It's comforting to me to bring something in my
professional life to a personal place of familiarity.</p>
<p>Example of motions I use commonly: <code>2f"</code> to jump the end of an html attribute,
from here I can hit <code>hci"</code> to replace the value of that attribute with whatever
I want to type.  If I have multiple attributes on the same line, I'll usually
hit <code>2f"</code> again and again until I get to the attribute. I can hit these characters
fast, it's almost second nature to me at this point.  It feels like in Tekken
when doing a korean backdash to create space between me and my opponent.</p>
<figure>
  <img src="https://media3.giphy.com/media/cAhta5br359KMVf4ZY/giphy.gif" alt="korean backdash">
  <figcaption>Korean backdash in Tekken</figcaption>
</figure>
<figure>
  <img src="/content/images/screenshots/nvim-backdash.gif" alt="vim backdash">
  <figcaption>Vim motions in action</figcaption>
</figure>
<p><code>dap</code>, and more importantly, <code>dat</code> are great for cleaning up code by selecting
continuous blocks of text.  If you have a big commented out method you were
trying to replace, <code>dap</code> will delete around the "paragraph".  Paragraph in this
case meaning several lines of text uninterrupted. <code>dat</code> will delete around an
html/xml tag which includes the tag and its contents.  <code>cit</code> will replace the
content of an html tag.</p>
<p>Much like in fighting games which sometimes require practice to literally move
around, I still struggle with navigating vertically in vim. I periodically look
for better strategies than my own, which relies heavily on <code>C-u/d</code> to scroll up
and down 50% of the file I'm viewing.</p>
<p>The movement in vim feels like a game now, and the dopamine hit from that is real.</p>
<h3 id="anewgitexperience">A new git experience</h3>
<p>Before nvim I used a combination of VS Code and Rider.  I was an early adopter
of VS Code.  I enjoyed Sublime text, but the draw for me to use VS Code was the
integrated git experience.  At the time, being someone who went between Visual
Studio and Sublime text, I wasn't a fan of managing git in the editor, often
opting to use the command line. I never liked the experience that Visual Studio
tried to offer.  VS Code changed my mind, and became not only an editor, but my
new interface into git.</p>
<p>One struggle I had with nvim was giving up this experience. It took me a few
tries to find something that actually worked for me. I tried to get by with
just git signs and git fugitive, which appears to be the most popular approach
to git in nvim. These just weren't landing for me, and I can't say why exactly.
There was another plugin out there called vgit, that worked okay, but had bugs
and felt like a step back from VS Code. I tried lazygit, which was an
impressive tool, but I struggled with the out of editor git experience. It's
nice seeing a diff of something you forgot about or didn't expect, and being
able to navigate directly to it in your code editor.</p>
<p>Enter <a href="https://github.com/NeogitOrg/neogit">Neogit</a>.  Neogit has become my go
to tool for interacting with git. By default, it operates very similar to how
VS Code does in showing you quickly what files have changed and what's staged.
It's easy to open up a diff and navigate to the exact line, hit enter then be
right in the file where you can make edits.</p>
<p>However, the killer Neogit feature for me is the documentation. It decrypts and
teaches you the git command line almost automatically.  Every time I hit <code>c</code> to
commit, it presents me with a helpful list of options and keyboard shortcuts to
activate them. It shows a list of keyboard shortcuts, as well as the command
line equivalent of each shortcut.  However, because it's navigated with the
keyboard, it's not annoying.  If I just want to commit, I can quickly hit <code>cc</code>
without having to read the dialog.  If I want to amend my last commit, then I
can hit <code>ca</code>.</p>
<p>Because Neogit makes it easier to see what's possible, it has made rebasing and
cleaning previous commits so simple that I do it much more often. I've tried
experimenting with different git flows last year, including stacked diffs. Now
if I'm working off a branch off of <code>main</code>, I'm far more comfortable going back
to a previous commit and adding to it if I realize there was something I missed
that I came to depend on in a later commit in that branch.  As a result, my PRs
are much easier to follow.</p>
<p>Helix is another modal editor out there that is designed to introduce common
text objects out of the box that are more aligned with code syntax.  So it's
easier to use vim motions to navigate a function signature out of the box. I
want to try it but I actually like Neogit so much that it's hard to imagine the
transition.</p>
<h3 id="wrappingup">Wrapping up</h3>
<p>This turned out longer than I had planned, and there is probably way more that I
can write.  I'm not sure how to wrap this up. Learning vim has been fun, but it's
also been useful even if it hasn't made me 10x faster at editing code. Creating my
own nvim config from scratch has been a project of its own, which serves as a kind
of practice of its own in making software. I'm in what may be a neverending process
of building my own editor and finding what I like. I think the real value here is
having another place to practice my craft.</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Revival]]></title>
            <link>https://zachrburke.com/blog/revival</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/revival</guid>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>So, here I am in 2026, writing my first "blog" post in about 9 years. It's
fascinating how so much has happened in 9 years, but one thing that I still see
is software engineers keeping personal sites with a blog. Blogging appears to
still be in fashion.</p>
<p>Side note, and maybe a large part of what brought me here, is the domain name
zachrburke.com. Sometime back in 2018 or 2019, a combination of an expired credit
card and total lack of due diligence on my part lead to me losing this domain.
I perhaps thought at the time that no one would want zachrburke.com so there was
no hurry to renew it. I thought wrong. Someone decided to grab zachrburke.com
and use it as a piece of disposable infrastructure to peddle unlicensed sports
betting.</p>
<p>So I was delighted to find out this week that zachrburke.com was back up for
sale. Although there might be a strong argument that I may not want it anymore.
Time will tell.</p>
<p>Anyway, I had to do some work to bring this thing back to life.</p>
<p>Rather than waste time building a new blog "engine", I decided to just fix the
rough edges of this one that I wrote 9 years ago. I really wanted to know how
hard that would be.  Given news of various compromised npm packages in 2025,
with the <a href="https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem">Sha1-Hulud Supply Chain attack</a>
and the general churn of javascript libraries and frameworks that happens in
the nodejs ecosystem (or should I say <a href="https://deno.com">deno</a>, or should I say
<a href="https://bun.com">bun</a>?), I expected this to be kind of a pain.</p>
<p>So I wasn't shocked at all when running <code>npm audit</code> revealed over 100+ critical
vulnerabilities. Fortunately, past me decided that I didn't actually need to
use webpack to generate a static site.  There were still references to it though.
Deleting them, and updating the actual packages I used, like showdown and cheerio
got me to 0 known vulnerabilities pretty quickly.</p>
<p>After that, I decided to clean up a bit.  node has native typescript support
now, so I converted everything to typescript, added a simple type for a post so I
get better feedback when <code>build.ts</code> fails, and now we're golden. I'm keeping
the tradition of having a very strange approach to a blogging "engine", that is
essentially a handcrafted static site generator. I think it's fun and gives me
a space to experiment with web standards.</p>
<p>In the past I think I used to try various product analytics tools so I know if
people are reading. I'm trying <a href="https://posthog.com/">Post Hog</a> for a product
I'm working on right now and it's pretty solid. In theory I could use Post Hog
here, but I've decided that it's best to not obsess over whether or not people
are actually reading anything here and instead to write for the sake of it.
That will at least help with the likely fact that no one is finding this blog
organically any time soon, given all the flags it has for illegal gambling on
the world cup in Qatar in 2022.</p>
<p>So what next? I want to write some more, and I have some other ideas that
I want to include on my site, like my collection of vinyls inspired by Andy
Bell's <a href="https://bell.bz/music-collection/">music collection</a>.</p>
<p>Some topics that I've had rattling around in my head that I want to get down,
maybe somewhat rapidly so I can push down those dreaded posts from 2017..</p>
<ul>
<li>Switching to nvim last year and my thoughts so far (this post is being
written in nvim).</li>
<li>Covering some of the lessons learned in my year-and-a-half pickleball
simulator project (Maybe even start a devlog to show progress as it happens).</li>
<li>My on and off again relationship with journaling and
<a href="https://obsidian.md/">obsidian.md</a></li>
<li>Intersections between software engineering and construction.</li>
</ul></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Static ENI for Autoscale Groups]]></title>
            <link>https://zachrburke.com/blog/static-eni-for-autoscale-groups</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/static-eni-for-autoscale-groups</guid>
            <pubDate>Sun, 28 Apr 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>Recently, I've found myself running an EC2 instance in a hybrid cloud environment,
for software where the private and public IP of a machine matter and must remain static.
This was a lift and shift effort, and we didn't want to lose the 99.9% uptime guaranteed
from running inside of a data center.  While EC2 offers 99.99% uptime, this is only the case
when paired with other tools AWS offers, like EC2 Auto Recovery and AutoScale Groups.</p>
<p>In our case, we could have created single instances, in multiple availability zones, without
the need of an autoscale group.  The design of this application goes against the design of a
typical AutoScale application, as the IP's, as well as the machine name, are very important.</p>
<p>So to avoid using an Autoscale group to guarantee HA, we looked at EC2 Auto Recovery as a simple
answer to situations like Hardware, or OS failures within EC2.</p>
<h2 id="theproblemwithec2autorecovery">The problem with EC2 Auto Recovery</h2>
<p>Auto Recovery only triggers when AWS detects a hardware failure.  There is no way to simulate this
type of event, so there is no way to tell if auto recovery works, and if it will bring the instance
back up in the state you want it.</p>
<h2 id="gettingbywithanautoscalegroup">Getting by with an AutoScale Group</h2>
<p>So the next solution was to put our single instance inside of an autoscale group.  The challenge here
is that a Launch Configuration won't accept a network interface as a parameter.  This makes sense, an
autoscale group needs to be able to make n number of instances to match load.  This means it would need
a way to accept an unknown number of network interfaces in order to create a new EC2 instance.</p>
<p>To get around this, we added a command for cfn-init to attach an Elastic Network Interface (eni) to
our newly created instance.  This eni is created in a Cloudformation template along with the instance
in the autoscale group.  The template also creates an Elastic IP (eip) and attaches that to the eni.</p>
<p>To be on the safe side, we dismount the eni from any instance it might be attached to, then attach it.</p>
<p>This means that we now have two interfaces running on the instance.  This can cause other problems if
your software assumes there is only one network interface.  Because we are running on Windows, to work
around this, we set the interface priority to prioritize our newly attached interface over the default
one that gets attached.</p>
<pre><code class="ps1 language-ps1">Set-NetIPInterface -InterfaceIndex 2 -InterfaceMetrix 30
</code></pre>
<p>With that, we can now terminate our instance and watch our autoscale group spin up a new instance,
with it's private/public IP in tact.</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Making embeds more generic with postMessage]]></title>
            <link>https://zachrburke.com/blog/postmessage-trick</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/postmessage-trick</guid>
            <pubDate>Wed, 06 Sep 2017 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>Are you working on a single page application that needs to be embedded in an iframe?  Did you have to give control of that iframe back to the parent because it too was a single page application?</p>
<p>You can use window.parent.postMessage to send messages up to any pages embedding your application.  The parent page can subscribe to that message and take whatever action it needs to take after a user has completed something in your embedded application.</p>
<p>The nice thing about this is it makes our application more generic, meaning it won't need to change because a new client wants to consume it using an iframe!</p>
<p>Here's an example:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">// framed code</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">window</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">parent</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#8DA101;--shiki-dark:#A7C080">postMessage</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"finished!"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'*'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">// parent code</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">window</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#8DA101;--shiki-dark:#A7C080">addEventListener</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'message'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#F85552;--shiki-dark:#E67E80">function</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(event)</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">    // make sure the message is coming from where you expect!</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">    if</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> (event</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">origin</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#8DA101;--shiki-dark:#A7C080">includes</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'my-own.domain.com'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">        document.querySelector(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'iframe'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">).remove();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">        // do things now that the user is done</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">});</span></span>
<span class="line"></span></code></pre>
<h2 id="gotchas">Gotchas</h2>
<p>First off, if you're in React, don't forget to keep track of your listener id so you can remove it when your component unmounts.  If you don't do this, you run the risk of your message getting handled multiple times for each time your component mounts.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">class</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> MyFrame</span><span style="color:#F57D26;--shiki-dark:#E69875"> extends</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> Component</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">    constructor</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(props) {</span></span>
<span class="line"><span style="color:#DF69BA;--shiki-dark:#D699B6">        super</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(props);</span></span>
<span class="line"><span style="color:#DF69BA;--shiki-dark:#D699B6">        this</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">state</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">finished </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> false</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    componentDidMount</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">() {</span></span>
<span class="line"><span style="color:#DF69BA;--shiki-dark:#D699B6">        this</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">listenerId </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> window</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#8DA101;--shiki-dark:#A7C080">addEventListener</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'message'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, (event) </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">            if</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> (event</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">origin</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#8DA101;--shiki-dark:#A7C080">includes</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'my-own.domain.com'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">) {</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">                this.setState({ </span><span style="color:#35A77C;--shiki-dark:#83C092">finished</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> true</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">                // do things now that the user is done</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    componentWillUnmount</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">() {</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        window.removeEventListener(this.listenerId);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    render</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">() {</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        return !this.state.finished ?</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            &lt;iframe src</span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"https://my-own.domain.com/app"</span><span style="color:#F57D26;--shiki-dark:#E69875"> /&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> :</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            &lt;div /&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>Secondly, if you want to hide the frame right after your message is published, you may see that your iframe has this flicker effect if it still has content inside of it.  As the maintainer of the framed application, assuming you can't just get rid of the content, you can get around this problem by using css transitions.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#F85552;--shiki-dark:#E67E80">finished</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#35A77C;--shiki-dark:#83C092">    animation</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> 400</span><span style="color:#F57D26;--shiki-dark:#E69875">ms</span><span style="color:#8DA101;--shiki-dark:#A7C080"> fade-in</span><span style="color:#939F91;--shiki-dark:#859289">;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#DF69BA;--shiki-dark:#D699B6">@keyframes</span><span style="color:#8DA101;--shiki-dark:#A7C080"> fade-in</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    0% { </span><span style="color:#35A77C;--shiki-dark:#83C092">opacity</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> 0</span><span style="color:#F57D26;--shiki-dark:#E69875">%</span><span style="color:#939F91;--shiki-dark:#859289">;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    100% { </span><span style="color:#35A77C;--shiki-dark:#83C092">opacity</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> 100</span><span style="color:#F57D26;--shiki-dark:#E69875">%</span><span style="color:#939F91;--shiki-dark:#859289">;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>This will actually cause the message to be published and processed first, and transition to come second.  Meaning your client can hide your iframe before you get a chance to change state, eliminating the flicker.</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[I wrote a twitter bot in Clojure]]></title>
            <link>https://zachrburke.com/blog/learning-clojure</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/learning-clojure</guid>
            <pubDate>Sat, 07 Mar 2015 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>I recently stumbled across <a href="https://www.youtube.com/watch?v=xzTH_ZqaFKI">this</a> video of "someone" (would love to know more about the author of the video) live coding in <a href="https://common-lisp.net/">common lisp</a>. In about 15 minutes, he "codes" a song and visually outputs what notes are being played on the screen as he is writing. What impressed me is that he could do this without having to refresh or rebuild anything. At most, you will occassionally see him highlight a method and run it through a <a href="http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">REPL</a>.  </p>
<p>This video piqued my interest in functional programming languages. In .NET, I've come to enjoy using lambda expressions and LINQ statements, which are a means to write <a href="http://www.codeproject.com/Articles/375166/Functional-programming-in-Csharp">functionally</a> in C#. One thing I can't do in C# is start up a program and actively modify it while it's running. I decided to start sifting through the functional languages that were at my disposal.</p>
<p>Before doing that though, I wanted a concrete problem to solve. When someone asks me what to write when learning a new language, I say start with "Hello World." After pointing out they already thought of that, I suggest writing a twitter bot. Doing this allows you to see, in your new language of choice, how you would tackle http requests and some kind of algorithim depending on what you would have your twitter bot do. I decided that I wanted to write a twitter bot that would tweet something random every hour.</p>
<p>As Heroku tends to be my goto for this kind of project, I looked at what functional languages it supports without the need for any custom buildpacks. Clojure pops up on the list as the lone item in this category, so I decide to roll with that.</p>
<p>Like many other bots, I took inspiration from <a href="https://twitter.com/horse_ebooks">horse_ebooks</a> and named my bot <code>zach_bebooks</code>. I wound up using the <a href="https://github.com/adamwynne/twitter-api">twitter-api</a> library by <a href="https://github.com/adamwynne">Adam Wynne</a>. My original plan was to hit the twitter api manually using <a href="https://github.com/dakrone/clj-http">clj-http</a>. However, learning the payloads that return from twitter, as well as figuring out how to generate an oauth signature and how to insert and parse JSON for a rest request/response proved to be a lot to learn alongside a new language.  </p>
<p>I let heroku take care of the scheduling by using the <a href="https://devcenter.heroku.com/articles/scheduler">Heroku Scheduler</a>. Borrowing some logic from <a href="http://diegobasch.com/markov-chains-in-clojure-part-2-scaling-up">Diego Basch's blog</a>, I was able to do a crude Markov Chain implementation, allowing <code>zach_bebooks</code> to speak.</p>
<p><img src="https://i.imgur.com/OcRFviQ.png" alt="zach_bebooks"></p>
<p>For the random sentence generation to work, I needed data. I created a makeshift persistence engine by storing the data used to generate markov chains in an atom. I actually loaded the data that is used now by running the program inside a repl and calling <code>learn-files</code> on directories loaded with text files.  </p>
<p>The full source code for <code>zach_bebooks</code> can be found <a href="https://github.com/zach-binary/zach_ebooks">here</a>.  </p>
<p>I'm happy to say I think I achieved my goal of writing a twitter bot without having to hack anything (with the exception of <code>learn-files</code> where I return "Complete" to keep all the newly learned data from getting dumped to the REPL). It surprised me how difficult it was to write functionally without an imperative crutch. Concepts like appending to a list during a loop took what felt like an eternity to grasp because I'm now working with immutable data structures.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#F85552;--shiki-dark:#E67E80">defn</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> gen-sentence</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">[]</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  (</span><span style="color:#F57D26;--shiki-dark:#E69875">let</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> [corpus (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">deref</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> corpus/data)]</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    (</span><span style="color:#F57D26;--shiki-dark:#E69875">loop</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> [phrase [] word (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">start-chain</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> corpus) n </span><span style="color:#DF69BA;--shiki-dark:#D699B6">0</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">]</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">       (</span><span style="color:#F57D26;--shiki-dark:#E69875">if</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> n phrase-length)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">          (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">clojure.string/join</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> " "</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> phrase)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">       (</span><span style="color:#F57D26;--shiki-dark:#E69875">let</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> [next-word (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">get-next-word</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> corpus word)]</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">          (</span><span style="color:#F57D26;--shiki-dark:#E69875">recur</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">conj</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> phrase next-word) next-word (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">inc</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> n)))))))</span></span>
<span class="line"></span></code></pre>
<p>This is the function <code>zach_bebooks</code> uses to speak. It gets the data (data being a persistent hash map of words and follow-ups to those words) from an atom (<code>corpus/data</code>). If we pass the length of our phrase, 20 words in this case, then it packs together the vector <code>phrase</code> into a string and returns that. Otherwise, it "calls" the loop again with a newly created vector that is a combination of the old vector and the recently selected word.  </p>
<p>One thing about clojure in particular is that <a href="http://martintrojer.github.io/clojure/2014/04/05/the-clojure-repl-a-blessing-and-a-curse/">it takes awhile for the REPL to start</a>. Despite the time it takes a REPL to start, the ability to reload code in a REPL session let me keep a single REPL going for a real long time. After getting past some of the nuances of Clojure, it didn't take long to feel the realization that <a href="http://java.dzone.com/articles/code-data-data-code">code is data</a> (I say this but I didn't use any macros :)).  </p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Let's Make Our Sitemap Dynamic in Lapis]]></title>
            <link>https://zachrburke.com/blog/sitemap-xml</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/sitemap-xml</guid>
            <pubDate>Thu, 11 Sep 2014 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>For far too long my sitemap has been served as a static xml file that I update every time I write a new post.  I'm forgetful, and I don't want any new articles to be missed by google, so it's a good idea to build up the sitemap when it's requested based on the content I've written so far.</p>
<p>Lapis makes serving up xml requests really easy with it's <code>@html</code> builder.  The <code>@html</code> builder isn't limited to creating only html elements, so it can be used to generate any arbitrary xml element.  Just change the <code>Content-Type</code> of your request to return xml and serve the rest of your request like normal.  Here is what my sitemap action looks like:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">[</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">sitemap</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"/sitemap.xml"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">]: </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    postList </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> articleRepo.getPostList!</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    @res.headers[</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"Content-Type"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">] </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "application/xml"</span></span>
<span class="line"><span style="color:#3A94C5;--shiki-dark:#7FBBB3">    layout</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#35A77C;--shiki-dark:#83C092">false</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, @html </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">        urlset</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> { </span></span>
<span class="line"><span style="color:#3A94C5;--shiki-dark:#7FBBB3">            xmlns</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'http://www.sitemaps.org/schemas/sitemap/0.9'</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            [</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'xmlns:xsi'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">]: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'http://www.w3.org/2001/XMLSchema-instance'</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            [</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'xsi:schemaLocation'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">]: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">[[http://www.sitemaps.org/schemas/sitemap/0.9</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">                http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd]]</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        }, </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">            for</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> i, post </span><span style="color:#F85552;--shiki-dark:#E67E80">in</span><span style="color:#8DA101;--shiki-dark:#A7C080"> ipairs</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> postList</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">                url </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">                    loc</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "http://throw-up.com/#{post.slug}"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            url </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">                loc</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "http://throw-up.com/me"</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            url </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">                loc</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "http://throw-up.com/me/portfolio"</span></span>
<span class="line"></span></code></pre>
<p>I also want to make sure that <code>/sitemap.xml</code> always serves the sitemap and doesn't get mistaken for any other routes.  To do that, rather than depend on lapis to find the route, I'm going to offload that responsibility onto nginx.  </p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">location </span><span style="color:#F57D26;--shiki-dark:#E69875">/</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">sitemap.</span><span style="color:#8DA101;--shiki-dark:#A7C080">xml</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    default_type application</span><span style="color:#F57D26;--shiki-dark:#E69875">/</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">xml;</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    content_by_lua_file</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "applications/sitemap.lua"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>So rather than looking at <code>web.moon</code> where the other applications are, I have <code>/sitemap.xml</code> look at a seperate application that only gets served when nginx receives that uri.  I just have to tell <code>sitemap.lua</code> to serve itself.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">lapis.serve class SiteMap extends lapis.Application </span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    [</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">sitemap</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"/sitemap.xml"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">]: </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span></span>
<span class="line"><span style="color:#939F91;--shiki-light-font-style:italic;--shiki-dark:#859289;--shiki-dark-font-style:italic">    -- ..</span></span>
<span class="line"></span></code></pre>
<p>That's it, we're done!</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Extending Sprite Kit classes with swift protocols]]></title>
            <link>https://zachrburke.com/blog/swift-protocol-extension</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/swift-protocol-extension</guid>
            <pubDate>Fri, 13 Jun 2014 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>Like many programmers over the past two weeks, I have been experimenting with Apple's newly announced <a href="https://developer.apple.com/swift/">Swift Programming language</a>.  I've been working (slowly) on porting my recent <a href="http://www.ludumdare.com/compo/ludum-dare-29/?action=preview&amp;uid=8599">ludum dare entry "Dig'em"</a> over to the new language as an iOS application.</p>
<p>In doing this I have been learning how to use Apple's <a href="https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Introduction/Introduction.html">Sprite Kit game libarary</a>.  Last night I started looking at how Sprite Kit handles collision detection and found the <code>SKPhysicsContactDelegate</code> protocol.  You can conform to this protocal by implementing two methods <code>didBeginContact</code> and <code>didEndContact</code> which will get fired for every collision that happens between two <code>SKNodes</code> in your scene.  Here is an example:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">class</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> GameScene</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> SKScene, </span><span style="color:#8DA101;--shiki-dark:#A7C080">SKPhysicsContactDelegate </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    init</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">()</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#DF69BA;--shiki-dark:#D699B6">        super</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">.</span><span style="color:#8DA101;--shiki-dark:#A7C080">init</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">()</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        self.physicsWorld.contactDelegate</span><span style="color:#F57D26;--shiki-dark:#E69875"> =</span><span style="color:#8DA101;--shiki-dark:#A7C080"> self</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    func didBeginContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(contact</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> SKPhysicsContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">        //</span><span style="color:#8DA101;--shiki-dark:#A7C080"> check the </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">contact.bodyA</span><span style="color:#F57D26;--shiki-dark:#E69875"> and</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> contact.bodyB</span><span style="color:#F57D26;--shiki-dark:#E69875"> and</span><span style="color:#8DA101;--shiki-dark:#A7C080"> see </span><span style="color:#F85552;--shiki-dark:#E67E80">if</span><span style="color:#8DA101;--shiki-dark:#A7C080"> you need to </span><span style="color:#F85552;--shiki-dark:#E67E80">do</span><span style="color:#8DA101;--shiki-dark:#A7C080"> something</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>This way, you then check the nodes that are stored in the contact object to see if you need to do any particular processing for a certain type of collision.  For instance, you can kill your hero if he/she collided with a lava object.  Because there are so many varieties of collision that can occur in a more than trivial game, I wanted a way for an <code>SKNode</code> to be able to keep track of it's own collision logic, rather than store all of that in one method with a potentially high amount of branching logic.  However, I didn't want to subclass <code>SKNode</code> and downcast from the delegate method, because that meant creating an extra layer between me and Sprite Kit just to do custom collision logic.</p>
<p>Swift offers the ability to extend existing objects with new methods or variables.  What's cool is in extending these objects, you can also add new protocols (swift's equivalent of an interface), effectively giving existing objects new types to conform to.  I am able to offload collision detection logic to the <code>SKNode</code> with the following code:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">@</span><span style="color:#8DA101;--shiki-dark:#A7C080">objc protocol Collidable </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    @</span><span style="color:#8DA101;--shiki-dark:#A7C080">optional func onContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(contact</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> SKPhysicsContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    @</span><span style="color:#8DA101;--shiki-dark:#A7C080">optional func onEndContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(contact</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> SKPhysicsContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">extension </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">SKNode</span><span style="color:#F57D26;--shiki-dark:#E69875"> :</span><span style="color:#8DA101;--shiki-dark:#A7C080"> Collidable </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    func distanceTo</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(point</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> CGPoint</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span><span style="color:#F57D26;--shiki-dark:#E69875"> -&gt;</span><span style="color:#8DA101;--shiki-dark:#A7C080"> CGFloat </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">        return</span><span style="color:#8DA101;--shiki-dark:#A7C080"> hypotf</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(point.x</span><span style="color:#F57D26;--shiki-dark:#E69875"> -</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> self.position.x,</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> point.y</span><span style="color:#F57D26;--shiki-dark:#E69875"> -</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> self.position.y);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>Here is where things get interesting.  By making the methods in the <code>Collidable</code> protocal optional, the methods <code>onContact</code> and <code>onEndContact</code> aren't guranteed to exist.  Swift allows you to call these optional methods by appending a ? to the end of a call to indicate the method might not be there. Here is how my <code>didContactBegin</code> method looks now:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">func didBeginContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(contact</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> SKPhysicsContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">    let</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> bodyA</span><span style="color:#F57D26;--shiki-dark:#E69875"> =</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> contact.bodyA.</span><span style="color:#8DA101;--shiki-dark:#A7C080">node </span><span style="color:#F85552;--shiki-dark:#E67E80">as</span><span style="color:#8DA101;--shiki-dark:#A7C080"> Collidable</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    bodyA.onContact</span><span style="color:#F57D26;--shiki-dark:#E69875">?</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#8DA101;--shiki-dark:#A7C080">contact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}    </span></span>
<span class="line"></span></code></pre>
<p>Now for any collisions that occur, the contact delegate will simply try to call onContact on the first node listed in the collision, and do nothing if the method is not there.  I can implement custom collision logic in my nodes by doing the following:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">class</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> HeroNode</span><span style="color:#F57D26;--shiki-dark:#E69875"> :</span><span style="color:#8DA101;--shiki-dark:#A7C080"> SKNode </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    init</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">()</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#DF69BA;--shiki-dark:#D699B6">        super</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">.</span><span style="color:#8DA101;--shiki-dark:#A7C080">init</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">()</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    func onContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(contact</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> SKPhysicsContact</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">        //</span><span style="color:#F85552;--shiki-dark:#E67E80"> do</span><span style="color:#8DA101;--shiki-dark:#A7C080"> custom collision logic here</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>I was a little dissapointed that I had to cast the node to a Collidable before the onContact method was recognized, but I am pleased overall with what I was able to accomplish.  </p>
<p>Being able to extend existing classes with new protocols is a cool idea and I am interested to see how other developers use this feature in the future.</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Embedding lua in .NET]]></title>
            <link>https://zachrburke.com/blog/lua-dot-net</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/lua-dot-net</guid>
            <pubDate>Fri, 30 May 2014 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>Lua was intended to be used as an embedded language.  It's gotten a lot of use in games like <a href="http://www.garrysmod.com/">garry's mod</a> and in the World of Warcraft in <a href="http://www.wowwiki.com/Lua">add-on api</a>.  For the sake of demonstration I wrote a small game in .NET using MonoGame that allows you to manipulate certain aspects of the game through lua code.  The game itself is a very simple simulation of gravity being applied to a ball tossed into the air.</p>
<p><img src="/content/images/screenshots/ball-screen1.gif" alt="Screenshot 1"></p>
<p>The above screenshot is how the game would look without writing any lua.  The game exposes a lua api that allows you to modify behaviour in the simulation by writing lua code.  With the appropriate lua script, we can get the following:</p>
<p><img src="/content/images/screenshots/ball-screen2.gif" alt="Screenshot 2"></p>
<p>This is a achieved with the following lua code</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#3A94C5;--shiki-dark:#7FBBB3">local</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> color </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> { r </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> 0</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, g </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> 100</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, b </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> 100</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">local trajectory </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> { x </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> 0</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, y </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> 0</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">function </span><span style="color:#8DA101;--shiki-dark:#A7C080">incrementor</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(start, inc)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    local value </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> start</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    return </span><span style="color:#8DA101;--shiki-dark:#A7C080">function</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">()</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        value </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> value </span><span style="color:#F57D26;--shiki-dark:#E69875">+</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> inc</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        return value</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    end</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">local theta </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#8DA101;--shiki-dark:#A7C080"> incrementor</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DF69BA;--shiki-dark:#D699B6">0</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#DF69BA;--shiki-dark:#D699B6">0.50</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">function </span><span style="color:#8DA101;--shiki-dark:#A7C080">Run</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> (ticks)</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    ChangeTrajectory</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(trajectory.</span><span style="color:#35A77C;--shiki-dark:#83C092">x</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, trajectory.</span><span style="color:#35A77C;--shiki-dark:#83C092">y</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    trajectory.</span><span style="color:#35A77C;--shiki-dark:#83C092">y</span><span style="color:#F57D26;--shiki-dark:#E69875"> =</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> math.</span><span style="color:#8DA101;--shiki-dark:#A7C080">sin</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#8DA101;--shiki-dark:#A7C080">theta</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">())</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">SetClearColor</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(color.</span><span style="color:#35A77C;--shiki-dark:#83C092">r</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, color.</span><span style="color:#35A77C;--shiki-dark:#83C092">g</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, color.</span><span style="color:#35A77C;--shiki-dark:#83C092">b</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">SetBallColor</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DF69BA;--shiki-dark:#D699B6">255</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#DF69BA;--shiki-dark:#D699B6">255</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#DF69BA;--shiki-dark:#D699B6">255</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"></span></code></pre>
<p>The methods SetClearColor, SetBallColor and ChangeTrajectory are actually implemented in .NET and are being called in lua.  The Run() method is being kept after running the lua script then called every frame.  I am using a .NET library called <a href="https://github.com/nrother/dynamiclua">DynamicLua</a> to achieve this, However I would recommend looking into <a href="https://github.com/NLua/NLua">NLua</a> for more involved projects.  Here is the code used to actually embed the lua script shown above. </p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">public</span><span style="color:#8DA101;--shiki-dark:#A7C080"> ClientAPI</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">string</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> scriptPath)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    ClearColor </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> Color.</span><span style="color:#35A77C;--shiki-dark:#83C092">SkyBlue</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    BallColor </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> Color.</span><span style="color:#35A77C;--shiki-dark:#83C092">White</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    Trajectory </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> Vector2.</span><span style="color:#35A77C;--shiki-dark:#83C092">Zero</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    _lua </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#F85552;--shiki-dark:#E67E80"> new</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> DynamicLua</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">.</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">DynamicLua</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">();</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    _lua.</span><span style="color:#35A77C;--shiki-dark:#83C092">SetBallColor</span><span style="color:#F57D26;--shiki-dark:#E69875"> =</span><span style="color:#F85552;--shiki-dark:#E67E80"> new</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> Action</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">&lt;</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">&gt;(SetBallColor);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    _lua.</span><span style="color:#35A77C;--shiki-dark:#83C092">SetClearColor</span><span style="color:#F57D26;--shiki-dark:#E69875"> =</span><span style="color:#F85552;--shiki-dark:#E67E80"> new</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> Action</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">&lt;</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">&gt;(SetClearcolor);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    _lua.</span><span style="color:#35A77C;--shiki-dark:#83C092">ChangeTrajectory</span><span style="color:#F57D26;--shiki-dark:#E69875"> =</span><span style="color:#F85552;--shiki-dark:#E67E80"> new</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> Action</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">&lt;</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">&gt;(ChangeTrajectory);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    _lua.</span><span style="color:#8DA101;--shiki-dark:#A7C080">DoFile</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(scriptPath);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">private</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> void</span><span style="color:#8DA101;--shiki-dark:#A7C080"> SetBallColor</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> red, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> green, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> blue)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    BallColor </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#F85552;--shiki-dark:#E67E80"> new</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> Color</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">((</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">int</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)red, (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">int</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)green, (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">int</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)blue);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">private</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> void</span><span style="color:#8DA101;--shiki-dark:#A7C080"> SetClearcolor</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> red, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> green, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> blue)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    ClearColor </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#F85552;--shiki-dark:#E67E80"> new</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> Color</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">((</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">int</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)red, (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">int</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)green, (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">int</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)blue);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">private</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> void</span><span style="color:#8DA101;--shiki-dark:#A7C080"> ChangeTrajectory</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> x, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> y)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    Trajectory </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#F85552;--shiki-dark:#E67E80"> new</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> Vector2</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">((</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">float</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">) x, (</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">float</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">) y);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">public</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3"> void</span><span style="color:#8DA101;--shiki-dark:#A7C080"> Run</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">double</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> ticks)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">    if</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> (_lua.</span><span style="color:#35A77C;--shiki-dark:#83C092">Run</span><span style="color:#F57D26;--shiki-dark:#E69875"> !=</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> null</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        _lua.</span><span style="color:#8DA101;--shiki-dark:#A7C080">Run</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(ticks);</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>DynamicLua provides a dynamic object that you can assign methods to.  Doing this will expose those methods in your lua context, so when DoFile is run, those functions will be available to the script passed to that method.  Any methods or variables stored globally in the lua script are also stored in the dynamic after the file has been ran.  This makes it possible to run methods that are implemented in lua, like the Run method.</p>
<p>You can view the full source on github <a href="https://github.com/zach-binary/aluminumball">here</a></p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Why aren't there more ways to represent data?]]></title>
            <link>https://zachrburke.com/blog/data-representation</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/data-representation</guid>
            <pubDate>Sun, 23 Mar 2014 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>About a few months ago I read <a href="http://www.sarahmei.com/blog/2013/11/11/why-you-should-never-use-mongodb/">this</a> article by Sarah Mei that flags some possible issues with using MongoDB as a data store for a social networking application.  The bulk of the article talks about using MongoDB as an approach to store activity streams such as those found on the news feed for your facebook account.  It then talks about falling back to PostGreSQL, which was also not exactly optimal for solving the problem either.</p>
<p>This got me thinking, why is it that after so long we are only now considering something <strong>other</strong> than SQL storage for our production applications.  Why is the only alternative to SQL storage a document database?  Here is a quote from that article that really struck me:</p>
<blockquote>
  <p>For quite a few years now, the received wisdom has been that social data is not relational, and that if you store it in a relational database, you're doing it wrong.</p>
  <p>But what are the alternatives? Some folks say graph databases are more natural, but I'm not going to cover those here, <strong>since graph databases are too niche to be put into production</strong></p>
</blockquote>
<p>This is interesting, because a graph database strikes me as a perfect model for a social network.  I am betting that the growing popularity of Facebook and social networking was what spawned the idea of a graph database.  So why not use that?  The funny thing is that if you went into most software shops and proposed something like <a href="http://www.neo4j.org/">neo4j</a> then you would have to explain why you shouldn't solve your graphing problem with a SQL database.  </p>
<p>What I don't understand is why developers can only get comfortable with the idea of a SQL or document database and why those are the only two popular ideas.  There are countless ways to deliver application code.  For example, a simple application to serve web requests can be done easily in so many languages now.</p>
<p>Since Facebook is on my brain right now, I'll give another example about them.  <a href="http://hhvm.com/">Hip Hop Virtual Machine</a> is a VM capabile of jit compiling PHP for better performance.  On top of that, they also invented the <a href="http://hacklang.org/">Hack</a> programming language which adds strong typing to PHP.  They would rather have invented a way to compile PHP into C++ than move to another programming language, then invented a new language on top of that.</p>
<p>Another example is the web framework that powers this blog.  <a href="http://leafo.net/lapis/">Lapis</a> was written by Leaf Corcoran so that he could write <a href="http://itch.io">itch.io</a> using Moonscript, which was also a language he himself wrote.</p>
<p>Why does this never seem to happen with database technology?  I have seen some technologies like <a href="http://www.actordb.com/">ActorDB</a>, which seeks to make a SQL database that is horizontally scalable and compatible with existing MySQL drivers.  There is also <a href="http://rethinkdb.com/">RethinkDB</a> which builds off a lot of MongoDB's ideas but adds things like joins and a nice admin UI. </p>
<p>Those are cool, but to me it's just more of the same.  You are forced to choose between a strict schema that maps to a flat table, or a big unorganized glob of data.  I would like to see developers consider other ways to represent their data, such as the graphing database, or if the problem is something new, then coming up with something new to tackle the problem rather than shoehorning that data into anothe relational database.</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Makin' changes, switchin' to Moot]]></title>
            <link>https://zachrburke.com/blog/some-changes</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/some-changes</guid>
            <pubDate>Sat, 25 Jan 2014 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>For the past few weeks I have been making changes to the look and feel of throw-up.  The changes boil down to</p>
<ul>
<li>Upgrading HighlightJS</li>
<li>Squashing the side articles bar</li>
<li>Switching out <a href="http://disqus.com/">Disqus</a> with <a href="http://moot.it">Moot</a> for handling comments</li>
</ul>
<h5 id="highlightjs">HighlightJS</h5>
<p>In my last post about Guard, I mentioned that I turned off <a href="http://highlightjs.org/">HighlightJS</a> because I was having issues getting it to detect the right language for a code block, and because I had a ruby snippet to show but didn't initially include ruby with my packed highlightjs file.  At the time I decided it was easier to just drop it instead of rebuild it with the ruby language included.</p>
<p>The decision to pick a small set of languages wasn't to reduce page weight, although that is a qualifying factor.  It was to reduce the probability that the wrong language would be detected on a page where I refer to code.  With their 0.80 release, they fix this problem by allowing you to specify what languages highlightjs should look for before having it auto highlight anything.  Now, in my post list I specify the programming languages to be expected in a blog post, like so:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">return</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    Title</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "Makin' changes, switchin' to Moot"</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    Slug</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "some-changes"</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    FileName</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "some-changes.md"</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    PubDate</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "January 25, 2014"</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    Languages</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> { </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'coffeescript'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'js'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">--...</span><span style="color:#8DA101;--shiki-dark:#A7C080"> more code</span></span>
<span class="line"></span></code></pre>
<p>I then initialize HighlightJS like this:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">if</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> @Post.Languages</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    script </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">src</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> '/content/js/highlight.pack.js'</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    script </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> </span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">        raw </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"var languages = </span><span style="color:#8DA101;--shiki-dark:#A7C080">#{</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">util.</span><span style="color:#8DA101;--shiki-dark:#A7C080">to_json</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(@Post.Languages)</span><span style="color:#8DA101;--shiki-dark:#A7C080">}</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">\</span><span style="color:#8DA101;--shiki-dark:#A7C080">n</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">        raw </span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">[[</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        hljs.</span><span style="color:#8DA101;--shiki-dark:#A7C080">configure</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">({</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            languages</span><span style="color:#F57D26;--shiki-dark:#E69875">:</span><span style="color:#8DA101;--shiki-dark:#A7C080"> languages</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        });</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        hljs.</span><span style="color:#8DA101;--shiki-dark:#A7C080">initHighlightingOnLoad</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">();</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    ]]</span></span>
<span class="line"></span></code></pre>
<p>It's worthwhile to note the Languages attribute is optional, if it doesn't exist I don't even request HLJS which saves me about 8-10ms in page load time.</p>
<h5 id="squashingthearchive">Squashing the Archive</h5>
<p>I decided to move the archive to the bottom of the page because I don't have that many posts yet.  So I could no longer justify using up 20% of the width of the screen when a long post like this one would mean leaving a huge gap of empty space beside the main content.  Afterall, it's the content that people care about, or so I hope.  </p>
<h5 id="switchingoutdisquswithmoot">Switching out Disqus with Moot</h5>
<p>This is probably the most signifigant change.  Comments haven't really brought any value to throw-up yet, but I didn't want to remove the option to leave a comment.  However, Disqus weighs a lot to keep around just in case someone wants to leave a comment, so I decided to try out <a href="http://moot.it">Moot</a> instead.  </p>
<p>Moot is a forum and commenting engine that leverages Redis as a primary datastore.  That last fact is interesting because Redis lives in memory, so to use it in lieu of a database might actually frighten some people.  <a href="https://moot.it/blog/technology/redis-as-primary-datastore-wtf.html">this</a> post here details the pains they went through to make the Redis datastore a reality and may calm some fears of trusting a memory store to persist data.  It's also a worthy read for anyone considering using Redis at all, even for a caching layer. </p>
<p>Because of their storage strategy, they boast 2ms load speeds when under load.  Meaning I can have my comments without as much weight.  Unfortanetly this means you have to use a moot or facebook account to leave any comments.  One cool thing is when you register with moot is that you get a forum by default.  So if this takes off, adding a forum to capture the discussion that happens on the site would be trivial.</p>
<h5 id="othersmallchanges">Other small changes</h5>
<p>I addded some share buttons to the top of the blog post in hopes to improve traffic.  I didn't like how my posts looked on facebook without an image so I decided to add a little logo.  It was surprisingly easy to find a non-offensive image depicting someone throwing up.</p>
<p>That's it!  Maybe one day I'll write about code not related to throw-up.  :P</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Cleaning up, moonc -t and Guard]]></title>
            <link>https://zachrburke.com/blog/cleaning-up</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/cleaning-up</guid>
            <pubDate>Sat, 04 Jan 2014 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>Today I was bored so I decided to do some cleaning up on throw-up.  A few things have bothered me since I started coding with the lapis/openresty stack.  First is that nginx likes to create several temp directories at the location I run, which is where my code is.  That generates about 4 directories of noise in my code base.  The other thing is that I would compile my moonscript files right where they were, so anytime I glance at my codebase I would see two versions of every .moon file which made a blog that is almost no code seem like a big project.</p>
<p>Using moonc -t, I was able to kill two birds with one stone.  the -t flag tells moonc to place compiled lua files in directory of choice.  I decided to create a bin directory that rests at the root level of my code and put the compiled lua files in there.  I wrote a small shellscript to copy over the content, mime types and nginx.conf file as well so I could run the lapis server in the bin directory instead of the root directory of my code.  Now those temp directories only exist in the bin directory where I don't have to look at them to navigate code.</p>
<p>In hindsight, I should have taken a before and after screenshot, but overall I feel like my code is a lot slimmer now, even though it was technically always slim.  </p>
<h5 id="replacinglivereloadwithguardbutkeepinglivereload">Replacing LiveReload with guard (but keeping live-reload)</h5>
<p>One thing I was dissapointed by when first picking up Lapis was that I couldn't use tup to watch and build my moonscript files into lua.  Fortunately I found LiveReload, which not only watched my file system for changes, but also refreshes Chrome for me whenever there were changes.  </p>
<p>This is nice, but one problem is I now have another thing to look at for troubleshooting.  There have been brief spurts where I had a compilation problem that I would glance over for several minutes because I kept forgetting to check my LiveReload log.  </p>
<p>I use iTerm as my terminal emulator on OSX and I already have trained myself to look at that frequently when I encounter bugs.  Plus, it has a nifty split pane ability to give me multiple feeds of data I can easily see at one time.  Wouldn't it be nice if I could pipe the live reload output to iTerm and have all my logging go to one place?</p>
<p>That is where Guard comes in.  <a href="https://github.com/guard/guard">Guard</a> is a command line tool designed to handle file system events.  Using ruby, you can write a Guardfile that defines how those events get handled.  There is a live reload plugin available for guard that allows you to refresh your browser in response to those events.  </p>
<p>Now I can run that in iTerm and have my build output right beside my server output.  Here is what my Guardfile looks like:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">filter </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">/bin/</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">guard </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'livereload'</span><span style="color:#F85552;--shiki-dark:#E67E80"> do</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    watch</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">%r{</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">bin/views/.+</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    watch</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">%r{</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">bin/content/.+</span><span style="color:#8DA101;--shiki-dark:#A7C080">\.</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">(css|js|html|md)</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    watch</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">%r{</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">bin/web.lua</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">    watch</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">%r{</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">bin/models/.+</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">watch</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">%r{</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">.+</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">) { </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">`sh build.cmd`</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> }</span></span>
<span class="line"></span></code></pre>
<p>Also, this is all free.  Shame I already purchased LiveReload.  Also, as far as I can tell it's cross-platform, so this may work on Windows.  Having tried .NET Demon at work, I was dissapointed that it wouldn't refresh the browser after changing .cshtml files.  This might be a nice alternative to get past that.</p>
<h5 id="oneotherthing">One other thing</h5>
<p>I'm turning off highlightjs for now.  Half my code is moonscript which isn't supported.  I also never thought I would post up Ruby code so it wasn't included when generating the script file.  I need to rethink how I'm going to post code on the site.  </p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Benefits of the article Tag]]></title>
            <link>https://zachrburke.com/blog/benefits-of-article</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/benefits-of-article</guid>
            <pubDate>Sun, 22 Dec 2013 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>Christmas is coming up so I went ahead and got myself (and some family) a Kindle Fire HD 7" tablet.  While playing around with the Kindle, I thought I would pull throw-up on the Silk browser.  I noticed two things:</p>
<ul>
<li>I was crazy to show the sidebar in portait mode on a 7" tablet.  The text gets way too tall.</li>
</ul>
<p>but more importantly:</p>
<ul>
<li>there is a "reading view" that shows only the content from my blog post.</li>
</ul>
<p>I was surprised to see this appear on my site and to see it work and work well.  As it turns out, it's just grabbing the article tags on my page and displaying them in the order they appear on the page (and maybe doing some other things.  I didn't thoroughly research how it worked).  </p>
<p>It was cool to see that happen without me really having to do anything, besides make what is considered a good design choice according to <a href="http://www.w3schools.com/tags/tag_article.asp">w3</a>:</p>
<blockquote>
  <p>An article should make sense on its own and it should be possible to distribute it independently from the rest of the site.</p>
</blockquote>
<p>I would be lying if I said I did that from the beginning.  I actually made some slight view changes so that the disqus widget wasn't rendered with the article as I wrote this.  Now my reading view is pristine on Silk.  </p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[SOLR in Lua/Moonscript using SPORE]]></title>
            <link>https://zachrburke.com/blog/solr-spore</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/solr-spore</guid>
            <pubDate>Thu, 14 Nov 2013 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>The other day I had an idea for a website that would basically be a CRUD application where the data store is a search engine.  I wondered how I would do this while still being able to write a web application using the Moonscript/Lapis stack.  </p>
<p>For my search engine, I chose <a href="http://lucene.apache.org/solr/">Apache Solr</a>.  Namely because I have had a little experience with it already and I think it is a fascinating technology that I would like to learn more about.  </p>
<p>One problem is that there are no well known tested client libraries for solr that are written in lua/moonscript.  I did find a couple of sample solutions out on Github but they have both gone untouched for three years which doesn't bid well considering I'm trying to use an up to date version of Solr.  </p>
<p>Then I had the realization that I don't need someone to write a client library for me.  Solr primarily talks using REST over endpoints that you can configure.  So now my quest shifted from looking for a solr client to a rest client for lua.  </p>
<p>My search lead me to <a href="http://fperrad.github.io/lua-Spore/index.html">SPORE</a>.  With SPORE, you write what's called a spec file using JSON to describe a REST API.  From there, your SPORE client takes that spec file and converts it into high level methods for your programming environment.  </p>
<p>So I can describe my Solr API like so:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">{</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "methods"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: {</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">    "select"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">      "path"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "/select"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">      "method"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "GET"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">      "required_params"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> [</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">        "q"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">        "wt"</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">      ]</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    },</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">    "update"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">      "path"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "/update/:format"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">      "method"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "POST"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">      "required_params"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> [</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">        "format"</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">      ],</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">      "required_payload"</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#DF69BA;--shiki-dark:#D699B6"> true</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>Here is how to use this in Lua.  (I would have shown it in moonscript but the highlighter kept thinking this sample was SQL)</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">local Spore </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#8DA101;--shiki-dark:#A7C080"> require</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'Spore'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">local solr </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> Spore</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#8DA101;--shiki-dark:#A7C080">new_from_spec</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'solr-spec.json'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, {</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  base_url </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'http://domain.com/solr/collection'</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">})</span></span>
<span class="line"><span style="color:#35A77C;--shiki-dark:#83C092">solr</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#8DA101;--shiki-dark:#A7C080">enable</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'Format.JSON'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">local result </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#35A77C;--shiki-dark:#83C092"> solr</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#8DA101;--shiki-dark:#A7C080">select</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">({ q </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> '*:*'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, wt </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'json'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> })</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">print</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(result</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">body</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">response</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">docs[</span><span style="color:#DF69BA;--shiki-dark:#D699B6">1</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">]</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">id)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">local docs </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> { { id </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> '123'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, title </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'stuff'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> },</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">               { id </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'whatever'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, some_string_s </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'more_stuff'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> } }</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">result </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#35A77C;--shiki-dark:#83C092"> solr</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#8DA101;--shiki-dark:#A7C080">update</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">({ format </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "json"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, payload </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> docs })</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">print</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(result</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">body</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">responseHeader</span><span style="color:#939F91;--shiki-dark:#859289">.</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">QTime)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">result </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#35A77C;--shiki-dark:#83C092"> solr</span><span style="color:#939F91;--shiki-dark:#859289">:</span><span style="color:#8DA101;--shiki-dark:#A7C080">update</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">({ format </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "json"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, payload </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> { commit </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {} } })</span></span>
<span class="line"></span></code></pre>
<p>And there we are!  A mostly functioning solr integration with less than 50 lines of code, and without all the noise that usually comes from making an http request in code. If that commit method is a little too wordy you can always add another method in the spec file that uses solr's shortcut commit url.  </p>
<p>Now, is this too magical?  The fact is, I am taking it on faith that the methods I am getting back from SPORE simply wrap an http request with data I give it.  If that's the case, then I'm fine with it, and maybe I will dig into the lua-spore client sometime to see if that's the case.  Even if it's not the case, SPORE is just the specification not the client (although this client is by the same person who came up with that specification).  </p>
<p>I could in theory write my own spore client. That may be worthwhile someday as I'm sure there are some optimizations that can be made to complement Openresty's non-blocking io functionality that I have yet to read about :P</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Blogging With Lapis, Deploying to Heroku]]></title>
            <link>https://zachrburke.com/blog/deploying-to-heroku</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/deploying-to-heroku</guid>
            <pubDate>Tue, 29 Oct 2013 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>Sometimes, I insist on being different, and sometimes that can come back to bite me.  Writing the code for throw-up was a fun educational journey into front-end design and the lua scripting language.  Deployment on the other hand, was educational in a much more painful way.  </p>
<p>I decided to go with heroku since a lot of the work for that had been done for me.  While Heroku doesn't natively support lua, there is the handy dandy <a href="https://github.com/leafo/heroku-buildpack-lua">lua build kit</a>.  To give Heroku openresty support, there is <a href="https://github.com/leafo/heroku-openresty">this luarock</a> which makes me wonder if I can't just install openresty using luarocks on my own machine.  </p>
<p>In order to make heroku aware of your projects dependencies on other lua libraries, the lua build kit looks in your project for a .rockspec file.  Mine looks like this</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">dependencies </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "https://raw.github.com/leafo/heroku-openresty/master/heroku-openresty-dev-1.rockspec"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "https://raw.github.com/jtarchie/underscore-lua/master/underscore-dev-1.rockspec"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "https://raw.github.com/leafo/lapis-console/master/lapis_console-dev-1.rockspec"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "https://raw.github.com/zach-binary/LuaHTML/master/luahtml-1.1.1-1.rockspec"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "lua-discount"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "moonscript"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "lapis"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">,</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}</span></span>
<span class="line"></span></code></pre>
<p>This is similar to node where you would define your dependencies in a package.json file for npm to go grab when you deploy.  The process isn't perfect.  I found when working with luahtml that it wasn't getting retrieved because it's rockspec file specified a release branch.  The build kit didn't like that, so I had to fork the luahtml repo and modify it's rockspec file in order to make it work. </p>
<p>Next thing I wanted to do was define an nginx configuration to use in a production environment.  Lapis makes this easy, you can define all your config values in a config.moon/lua file at the top level of your project, like so:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">config</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'production'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  port </span><span style="color:#8DA101;--shiki-dark:#A7C080">os.getenv</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "PORT"</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  num_workers </span><span style="color:#DF69BA;--shiki-dark:#D699B6">4</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">  code_cache</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'on'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">  blogFilePath</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'content/blog/'</span></span>
<span class="line"></span></code></pre>
<p>Again, this is just moonscript that gets interpreted into a lua table, so if you want you can nest objects as configuration values.  The code_cache tells nginx, I believe, to store an execution plan / build your lua server files in order to improve performance.</p>
<p>Finally, there was the Procfile to tell heroku how to start your server.  Here's what mine looked like originally.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#3A94C5;--shiki-dark:#7FBBB3">web</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: lapis server production</span></span>
<span class="line"></span></code></pre>
<p>So now I'm ready to deploy my site!  I commit my changes, do a <code>git push heroku master</code>, and everything seems to get copied correctly.  Except for the fact that my site is throwing 500's.  What did I miss?</p>
<p>After about 3 hours of cursing heroku and punching empty shoe boxes I found that I forgot to tell heroku to compile my moon files into lua files.  Now I've opened up the troubles of getting heroku to do specific things before running your site.  </p>
<p>The only "clean" solution I found for this was to make a custom buildpack.  That seemed like a lot of work considering I just wanted to do a moonc before starting the server.  Not to mention that heroku only allows you to have one buildpack at a time, and I still need the lua buildpack.  You can bypass this using a special buildpack that is designed to load other buildpacks, but that adds even more excess to an already excessive solution. </p>
<p>Finally I just added it to the Procfile.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#3A94C5;--shiki-dark:#7FBBB3">web</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: moonc .; moonc views</span><span style="color:#F57D26;--shiki-dark:#E69875">/</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">; moonc models; lapis server production</span></span>
<span class="line"></span></code></pre>
<p>Not the prettiest solution, but it works.  </p>
<h4 id="keepingherokudynosalive">Keeping Heroku dynos alive</h4>
<p>I only need one dyno for this blog right now.  The problem with this is that when you only have one dyno, it gets put to sleep after a hour of inactivity.  Sleep meaning whoever loads the site when the dyno is asleep has to put up with a long page load.  </p>
<p>To get around that, I followed the steps in <a href="http://beouk.blogspot.com/2012/02/keeping-heroku-awake.html">this blog post</a> and created a scheduled task to ping my site every hour.  No more slacking dynos.</p>
<h4 id="conclusion">Conclusion</h4>
<p>This concludes the exciting trilogy of posts about making a blog in Lapis.  Here's a quick list of the pros and cons I found</p>
<p><em>pros:</em></p>
<ul>
<li><strong>speed</strong>: openresty is fast.  This is one of the key reasons I wanted to try it out.  Running httperf with 1000 sequential connections on my macbook pro retina against the building-openresty page yielded a rate of 0.8 ms per request.  That's sub millisecond to send an http request, parse markdown and send that markdown back.  That seems fast anyway, I'll need a better way to compare metrics to other frameworks.</li>
<li><strong>developer experience</strong>: moonscript was designed so you can write clean code.  It takes a little getting used too, and there was a point where I lost time due to an indentation mistake.  However, I haven't really had to struggle understanding my own code.  It's easy to keep files small and the lack of needed brackets cuts down on a lot of typing and makes writing code flow real quickly.  Sure you have a build step but I quickly forgot about it once I started using live reload (which admittedly did bite me in deployment)</li>
</ul>
<p><em>cons</em></p>
<ul>
<li><strong>no repl/debugger</strong>: this is pretty self explanatory. </li>
<li><strong>deployment</strong>: Per this post, there are some improvements that can be made here.  Having nginx interperet moon files directly is a todo for the lapis project, and would be a huge deal sealer for me and probably many other developers.</li>
</ul>
<p>Some things I may tackle next are writing tests using busted to check my posts for spelling errors and broken links.  </p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Blogging With Lapis, Implementation]]></title>
            <link>https://zachrburke.com/blog/lapis-implementation</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/lapis-implementation</guid>
            <pubDate>Sun, 27 Oct 2013 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>This has been exciting.  In my freetime for the past 5-6 days I have been working furiously on this blog.  I've decided that I need three posts to accurately sum up what has been an englightening project for me.  This post I'll write about the actual implementation of throw-up.</p>
<p>Yes, you heard me correctly, throw-up.  I thought the name was so clever I bought the domain sometime in late 2012-early 2013.  I thought I would dedicate it to the don'ts of the programming world.  My own version of <a href="http://thedailywtf.com/">the daily wtf</a>. </p>
<p>My requirements were simple.  I liked the idea of writing posts in markdown.  I didn't want to bother with a database since I don't think I will write that much.  Also, since Kijana claimed he was able to load his page in under 50ms before adding disqus, I wanted to be faster than that.</p>
<p>Somewhere in working on this I decided I wanted to have syntactical highlighting since I would primarily talk about code.  In the end, here are the front-end tools I ended up using.</p>
<p><a href="http://typeplate.com/">Typelate</a> for typography.<br>
<a href="http://softwaremaniacs.org/soft/highlight/en/">Highlight.JS</a> for syntax highlighting.<br>
<a href="http://shalinguyen.github.io/socialicious/">Socialicious</a> for social media icons.<br>
also <a href="http://disqus.com/">Disqus</a> of course because they freakin' sweet.</p>
<p>As for CSS frameworks, I opted out of those (with the exception of typelate).  I realized that when I look at frameworks like bootstrap I really just want a grid and I don't care about or need the rest of the stuff. I looked around for a standalone css grid then eventually rolled my own after reading <a href="http://css-tricks.com/dont-overthink-it-grids/">this</a> post about grids by Chris Coyier.  I like how my design is less "poppy" than a default twitter bootstrap build would get you.</p>
<p>One problem with writing posts in markdown is it's a challenge to apply syntax highlighting.  Highlight JS solves this by looking at any code blocks in a document and using a heuristic to guess what kind language the code is in. This does mean it can be wrong.  It also doesn't support moonscript so I had to try and trick it into thinking code blocks on this page were coffeescript.  </p>
<p>I tried Font Awesome at first for icon support, but I don't think nginx liked it.  Rather than look into the issue I looked for another solution.  Socialicious was better for me since I really just wanted social media icons, and Socialicious is much smaller than font awesome so less page weight.</p>
<h4 id="learninglapis">Learning Lapis</h4>
<p>Lapis and moonscript held up pretty well considering they are both under two years old (Lapis being less than a year old itself).  Since moonscript compiles into lua, you get access to any existing lua library, and I was surprised to see there are tons of useful lua libraries if you are building a website.  For markdown parsing, there was <a href="http://asbradbury.org/projects/lua-discount/">lua-discount</a>.  For templating there is <a href="https://github.com/TheLinx/LuaHTML">luahtml</a>.  I originally wanted to use <a href="https://github.com/leafo/etlua">etlua</a> but it won't work with lapis just <a href="https://github.com/leafo/etlua/issues/1">yet</a></p>
<p>One thing that concerned me about using Lapis was lack of robustness when dealing with lists/tables of information.  lua doesn't have a lot of built in support for tables, so doing something like getting the size of a table involves writing a function to iterate through the table and count how many elements are contained in it.  Fortunately, there exists <a href="https://github.com/jtarchie/underscore-lua">underscore-lua</a>, a 1-to-1 port of underscorejs to lua.  This pretty much grants us functionaltiy similar to Microsoft LINQ.  So it's possible to write code like this (last example from underscore documentation):</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">local</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> stooges </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> {{name</span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'curly'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, age</span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6">25</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}, {name</span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'moe'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, age</span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6">21</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}, {name</span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'larry'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, age</span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#DF69BA;--shiki-dark:#D699B6">23</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">}}</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">local</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> youngest </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> _.</span><span style="color:#8DA101;--shiki-dark:#A7C080">chain</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(stooges)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  :</span><span style="color:#8DA101;--shiki-dark:#A7C080">sortBy</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#F85552;--shiki-dark:#E67E80">function</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(stooge) </span><span style="color:#F85552;--shiki-dark:#E67E80">return</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> stooge.age </span><span style="color:#F85552;--shiki-dark:#E67E80">end</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  :</span><span style="color:#8DA101;--shiki-dark:#A7C080">map</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#F85552;--shiki-dark:#E67E80">function</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(stooge) </span><span style="color:#F85552;--shiki-dark:#E67E80">return</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> stooge.name </span><span style="color:#F57D26;--shiki-dark:#E69875">..</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> ' is ' </span><span style="color:#F57D26;--shiki-dark:#E69875">..</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> stooge.age </span><span style="color:#F85552;--shiki-dark:#E67E80">end</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">)</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  :</span><span style="color:#8DA101;--shiki-dark:#A7C080">first</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">()</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  :</span><span style="color:#8DA101;--shiki-dark:#A7C080">value</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">()</span></span>
<span class="line"><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "moe is 21"</span></span>
<span class="line"></span></code></pre>
<p>Lapis has it's own idea of how to create a view which I'm sure some of you will find a bit wacky.  Here is a snippet of the code used in my layout</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#3A94C5;--shiki-dark:#7FBBB3">content</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    html_5 </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">      head </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> </span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        link </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">rel</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">"icon"</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">href</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'/content/images/barf.ico'</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        title </span><span style="color:#F85552;--shiki-dark:#E67E80">if</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> @Title </span><span style="color:#F85552;--shiki-dark:#E67E80">then</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> @Title </span><span style="color:#F85552;--shiki-dark:#E67E80">else</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'throw up;'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">      body </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> </span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        header </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">          hgroup </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">            div </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">              small</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> "var up = new Exception();"</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">              h3</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">  "throw up;"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">          section </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">             ul </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">              li </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">                a </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">href</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'http://twitter.com/zach_no_beard'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">target</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'_blank'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">                  i </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">class</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'icon-twitter'</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">              li </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">                a </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">href</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'https://github.com/zach-binary'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">target</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'_blank'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">                  i </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">class</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'icon-github'</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">              li </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">                a </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">href</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'http://ren.itch.io'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">target</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'_blank'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">                  small</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'itch'</span></span>
<span class="line"></span></code></pre>
<p>So what is this?  Well, this is moonscript.  What we have here is a very clever use of moonscript syntax.  I have a view called Index, and in that view I have a content method which defines a template for my view.  From there you can call just about any method you want, and if that method name isn't defined somewhere then it will be reflected into an html element.  The first parameter can be a table defining any number of attributes you want attached to that element.  You can pass in a callback that will generate output to be nested in that element, or for brevity, you can pass in a single value if you only want a small amount of content to be stored in that element.  The result is a templating language that resembles <a href="http://jade-lang.com/">Jade</a></p>
<p>This is an interesting approach since most people would hand you a view and say "it's just data", but here I'm handing you a view and I'm saying "it's just code."  At the same time, I can see how this approach could be very powerful, yet it's clean and understandable.  It may be considered overkill for view generation though.  </p>
<p>If this approach is too magical, then you can always introduce a templating engine.  Here I use a template to render the disqus widget because dumping the html for that in my moonscript views looked pretty ugly to me.  </p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span style="color:#3A94C5;--shiki-dark:#7FBBB3">content</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    article </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">      section </span><span style="color:#F57D26;--shiki-dark:#E69875">-&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">        if</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> @Post</span></span>
<span class="line"><span style="color:#8DA101;--shiki-dark:#A7C080">          small</span><span style="color:#DFA000;--shiki-dark:#DBBC7F"> 'posted sometime around ' </span><span style="color:#F57D26;--shiki-dark:#E69875">..</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> @Post.PubDate </span></span>
<span class="line"></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">        unless @errors</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">          raw @PostBody</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">          @RenderDisqus!</span></span>
<span class="line"><span style="color:#F85552;--shiki-dark:#E67E80">        else</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> </span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">          raw @errors</span></span>
<span class="line"></span>
<span class="line"><span style="color:#3A94C5;--shiki-dark:#7FBBB3">RenderDisqus</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: </span><span style="color:#F57D26;--shiki-dark:#E69875">=&gt;</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA"> </span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">  status, error </span><span style="color:#F57D26;--shiki-dark:#E69875">=</span><span style="color:#8DA101;--shiki-dark:#A7C080"> pcall</span><span style="color:#F57D26;--shiki-dark:#E69875"> -&gt;</span></span>
<span class="line"><span style="color:#5C6A72;--shiki-dark:#D3C6AA">    raw luahtml.</span><span style="color:#8DA101;--shiki-dark:#A7C080">open</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">(</span><span style="color:#DFA000;--shiki-dark:#DBBC7F">'templates/disqus.html'</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">, { </span><span style="color:#3A94C5;--shiki-dark:#7FBBB3">slug</span><span style="color:#5C6A72;--shiki-dark:#D3C6AA">: @Post.Slug })</span></span>
<span class="line"></span></code></pre>
<p>The full code is up on github.  I'm happy to say I was able to hit my page load goal of under 50ms.  The building openresty page clocks in between 20-25ms pre-disqus when running with production settings on my local machine.  </p>
<p>I would like to note that Lapis has a pretty slick looking integration with PostGres SQL.  I may consider changing my storage approach later to leverage that.  </p>
<h4 id="lifewithoutadebugger">Life without a debugger</h4>
<p>Moonscript is still a new language and as a result, advanced features like a repl have not been implemented yet.  As a result, that means there is no way (that I know of anyway) to step through code using a debugger.  </p>
<p>Lapis attempts to combat this with a web console you can route to from your lapis application.  One thing you get from this is the ability to unfold and see what is in your tables, which the lua/moonscript console doesn't do.  </p>
<p>Still, I found it easier to add a debug directory to my project and populate that with scripts when any form of advanced investigation of my objects was necessary.  This way I can write code with Sublime.  I would prefer a debugger much more though ;-)</p>
<p>My next post I'll write about deployment.</p></body></html>]]></description>
        </item>
        <item>
            <title><![CDATA[Blogging With Lapis, Building Openresty]]></title>
            <link>https://zachrburke.com/blog/building-openresty</link>
            <guid isPermaLink="false">https://zachrburke.com/blog/building-openresty</guid>
            <pubDate>Tue, 22 Oct 2013 00:00:00 GMT</pubDate>
            <description><![CDATA[<html><head></head><body><p>A few weeks ago I read <a href="http://kijanawoodard.com/building-a-blog-engine">this</a> blog post by Kijana Woodard, which inspired me to create my own blog.  This gives me an opportunity to touch on a number of technologies I've been meaning to checkout.  Namely, <a href="http://openresty.org">Openresty</a> and <a href="http://leafo.net/lapis">Lapis</a>, a web framework that leverages <a href="http://moonscript.org">moonscript</a>.</p>
<p>I am going to break this out over two posts since I wanted cover the installation of openresty seperate from the rest of the blog implementation.  It should be noted that I did this development on a Mac machine, and I am not sure how I would setup openresty on a windows box just yet.</p>
<p>So I went to the openresty website and faithfully followed their step by step instructions.  I downloaded the latest version which at this time of writing in 1.4.2.9.</p>
<p>PCRE is a prerequisite for Mac users so I started by running this:</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span>brew install pcre</span></span>
<span class="line"><span></span></span></code></pre>
<p>Then I moved to an appropriate development folder and untarred the build files.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span>tar -xvf ngx_openresty-1.4.2.9</span></span>
<span class="line"><span>cd ngx_openresty-1.4.2.9</span></span>
<span class="line"><span></span></span></code></pre>
<p>Next I had to configure the build.  Since PCRE was a prereq for me because I'm on a mac, I had to specify where it could find that using the --with-ld-opt and --with-cc-opt arguments.  </p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span>./configure  --with-luajit --with-http_postgres_module --with-cc-opt="-I/usr/local/include" --with-ld-opt="-L/usr/local/lib"</span></span>
<span class="line"><span></span></span></code></pre>
<p>I also told it to include the postgres http module because that is recommended for Lapis, although I have no intention of using a database for this project.  Luajit should just about always be added because it improves performance.</p>
<p>With that, all that was left was to make and install it.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span>make</span></span>
<span class="line"><span>make install</span></span>
<span class="line"><span></span></span></code></pre>
<p>I did attempt to do this without PCRE only to run into an error in the last step.  Once I installed that everything built smoothly.  </p>
<p>With that I went ahead and installed lapis, which was pretty easy.</p>
<pre class="shiki shiki-themes everforest-light everforest-dark" style="background-color:#fdf6e3;--shiki-dark-bg:#2d353b;color:#5c6a72;--shiki-dark:#d3c6aa" tabindex="0"><code><span class="line"><span>luarocks install --server=http://rocks.moonscript.org/manifests/leafo lapis`  </span></span>
<span class="line"><span></span></span></code></pre>
<p>Created a new site using <code>lapis new</code> then tested it with <code>lapis server</code> after building the example web.moon.  Everything is green so far!</p></body></html>]]></description>
        </item>
    </channel>
</rss>