<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Shiny Things</title>
    <description>Jim likes shiny things. Technology is shiny. So are synth knobs.
</description>
    <link>http://jimmenard.com/</link>
    <atom:link href="http://jimmenard.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 14 Apr 2026 18:26:46 -0400</pubDate>
    <lastBuildDate>Tue, 14 Apr 2026 18:26:46 -0400</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Emacs Book Intro</title>
        <description>&lt;p&gt;&lt;em&gt;Here’s something I wrote for the introduction to the Emacs book I started
years ago and will never, ever finish writing (“Emacs Mastery: Attaining
Coding Supremacy”):&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Emacs is a master craftsman’s tool. No, wait — Emacs is an empty canvas,
complete with brushes, paints (oil and acrylic), charcoal, scrapers,
sponges, turpentine, and a nuclear-powered color wheel.&lt;/p&gt;

&lt;p&gt;Since I’m a musician and not a woodworker or painter, here’s an analogy I
can get behind: Emacs is a synthesizer. At first, you play the presets and
have fun making music. You don’t need all those knobs, but you do wonder
what they’re for. One day you’re unhappy with the preset bass sound you’re
using — it needs a sharper attack, a longer decay, and a brighter sound.
You tweak a few knobs. They don’t do what you want at first, but after a bit
of reading the manual (ugh!) you start to understand what some of them do
and you achieve that sound you were looking for.&lt;/p&gt;

&lt;p&gt;After reading more of the manual you begin to understand how the synth
creates sounds and what’s really going on when you turn those knobs. You
start finding things that the synth can do that you never imagined it could.
Not only that, getting the sounds you want is easier so you’re spending less
time tweaking knobs and more time making music.&lt;/p&gt;

&lt;p&gt;Meanwhile, you’ve come across terms like “subtractive synthesis”,
“wavetables”, and “frequency modulation” so you start reading up on what
those things mean. You’re learning how sound synthesis works and that there
are different ways of creating sounds. This knowledge helps you create new
sounds and textures, opening up a universe of sound explosions you use to
create mega-hit after mega-hit making you so mega-rich you mega-retire and
give all your money to charity. A combination music/philanthropy prize is
named after you and statues to you are erected everywhere.&lt;/p&gt;

&lt;p&gt;That’s Emacs.&lt;/p&gt;

&lt;p&gt;I’ve been using Emacs since about 1982, and I still discover new things it
can do or new ways to be more efficient. It may not be possible to know all
there is to know about Emacs, but that’s not the point. The point is to have
a tool that helps me create what I want and be more efficient so I can
create more. And knobs. Lots of knobs.&lt;/p&gt;
</description>
        <pubDate>Wed, 17 Apr 2024 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2024/04/17/emacs-book-intro.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2024/04/17/emacs-book-intro.html</guid>
        
        <category>programming</category>
        
        <category>emacs</category>
        
        <category>writing</category>
        
        
      </item>
    
      <item>
        <title>Asynchronous Audits are Awesome</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/collectiveidea/audited&quot;&gt;Audited&lt;/a&gt; is a Ruby gem that adds
change logging to Rails models. It’s great for figuring out what happened to
a model or a group of related models over time.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This post was originally published
on &lt;a href=&quot;http://dirtybirds.chloeandisabel.com/&quot;&gt;The Dirty Birds&lt;/a&gt;, the Chloe +
Isabel tech team blog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you have an application that generates a lot of audit records they can
start to slow down your application a bit. Not only do the audit records
have to get written to the database, but since they are versioned the save
process needs to read the database first to get the current max version
number before the new record can be written.&lt;/p&gt;

&lt;p&gt;Making these saves asynchronous can improve your Rails app’s response times.
Testing with our application showed a 14% speedup with audit-intensive code.
That is of course an anecdote, not data.&lt;/p&gt;

&lt;p&gt;We had originally forked the Audited gem and modified it to work with
multiple asynchronous job processing libraries (we
use &lt;a href=&quot;https://github.com/resque/resque&quot;&gt;Resque&lt;/a&gt;). After we opened
a &lt;a href=&quot;https://github.com/collectiveidea/audited/pull/288&quot;&gt;pull request&lt;/a&gt; and
some great help from project members, a discussion took place in the PR
about whether the code should be merged into the Audited gem’s code base.&lt;/p&gt;

&lt;p&gt;After some thoughtful comments pro and con, we decided to close the PR.
Instead, we’ve come up with a way to do this that is simpler and doesn’t
require that the gem be changed at all.&lt;/p&gt;

&lt;h2 id=&quot;show-me-the-code&quot;&gt;Show Me the Code!&lt;/h2&gt;

&lt;p&gt;To force audits to be saved asynchronously we started by monkeypatching the
audit model’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;save&lt;/code&gt; method. It puts the data to be saved in the work queue
to be written to the database later.&lt;/p&gt;

&lt;p&gt;We use &lt;a href=&quot;https://github.com/resque/resque&quot;&gt;Resque&lt;/a&gt; to manage work queues and
perform work asynchronously.&lt;/p&gt;

&lt;p&gt;This code lives in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/initializers/audited.rb&lt;/code&gt;. (Note that this is not
the final version.)&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Audited&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Audit&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;alias_method&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:orig_save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:save&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Resque&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;AuditingJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;AsyncSave&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The asynchronous job does the actual save, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orig_save&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AuditingJob::AsyncSave&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...boilerplate removed...&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Audited&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Audit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;orig_save&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This works, but if you run this you’ll notice at least two problems. First,
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;created_at&lt;/code&gt; is set during the save in the asynchronous job which means
that it is not the time that the original audit record’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;save&lt;/code&gt; method was
called. Second, the fields related to the user who made the audit and the
request itself (IP address and request UUID) are blank. That’s because the
Audit gem’s save code gets that information from Rails’ current controller.
The problem is that inside this asynchronous job there is no “current
controller”.&lt;/p&gt;

&lt;p&gt;The solution is to set those values in the monkeypatched &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;save&lt;/code&gt; method.
Setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;created_at&lt;/code&gt; attribute directly is easy. As for the other
values, though the save method doesn’t directly have access to the current
controller the Audited gem already has a way to get it. We can use that to
get information about the current user and the request.&lt;/p&gt;

&lt;p&gt;One final thing we need to do is muck with the datetime attributes before
sending the to the async job. Datetimes are serialized as strings, but the
string format of a Time object is not directly readable by the database. So
we turn it into a format readable by the database. (We’re jumping ahead a
bit — we will be using a SQL INSERT directly; see below.)&lt;/p&gt;

&lt;p&gt;Here’s a modified version of the monkeypatched &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;save&lt;/code&gt; method:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Audited&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Audit&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;alias_method&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:orig_save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:save&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ctl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Audited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:current_controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctl&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current_user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current_user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;request_uuid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;uuid&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remote_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remote_ip&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;email&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# Need to hack datetime values so they&apos;re encoded properly&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;attrs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;no&quot;&gt;Resque&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;AuditingJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;AsyncSave&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re using Ruby’s safe navigation operator (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;.&lt;/code&gt;) which was introduced in
Ruby 2.3.0. If you’re running an earlier version of Ruby you can use the
Rails &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;try&lt;/code&gt; method instead.&lt;/p&gt;

&lt;p&gt;Now that we’ve set all of these values, we need to prevent Audited from
overwriting them when it saves the record in our asynchronous job. To do
that, we use a SQL INSERT statements instead of calling the Audited model’s
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;save&lt;/code&gt; (via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orig_save&lt;/code&gt;). That means we have to not only create the INSERT
statement but also determine the version number for the record we are about
to save.&lt;/p&gt;

&lt;p&gt;Here’s the new version of the asynchronous job:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AuditingJob::AsyncSave&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ... boilerplate removed...&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Using raw SQL is not only simpler, but it also bypasses Audited code&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# that (re)sets some values we don&apos;t want touched.&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Audited&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Audit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transaction&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;audit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;auditable_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;auditable_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Audited&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Audit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;column_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keys&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;safe_values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audit_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;quote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;INSERT INTO &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Audited&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Audit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
                   &lt;span class=&quot;s2&quot;&gt;&quot; VALUES (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;safe_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auditable_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auditable_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auditable_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auditable_type&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;curr_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Audited&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Audit&lt;/span&gt;
                     &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;auditable_id: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auditable_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;auditable_type: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auditable_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                     &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;maximum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curr_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it.&lt;/p&gt;
</description>
        <pubDate>Wed, 03 May 2017 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2017/05/03/async-audits-are-awesome.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2017/05/03/async-audits-are-awesome.html</guid>
        
        <category>performance</category>
        
        <category>database</category>
        
        <category>ruby</category>
        
        <category>rails</category>
        
        <category>programming</category>
        
        
      </item>
    
      <item>
        <title>Phoenix - Seeding Your Tests</title>
        <description>&lt;p&gt;If you need to seed your test database when running
&lt;a href=&quot;http://www.phoenixframework.org/&quot;&gt;Phoenix&lt;/a&gt; tests, you can require the seed
file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/test_helper.exs&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span class=&quot;no&quot;&gt;ExUnit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Mix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ecto.create&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--quiet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Mix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ecto.migrate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--quiet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Add this line:&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;require_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;priv/repo/seeds.exs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Ecto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Adapters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin_test_transaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Expensive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can, of course, require a different file instead.&lt;/p&gt;
</description>
        <pubDate>Tue, 08 Sep 2015 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2015/09/08/phoenix-seeding-your-tests.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2015/09/08/phoenix-seeding-your-tests.html</guid>
        
        <category>elixir</category>
        
        <category>phoenix</category>
        
        <category>testing</category>
        
        <category>database</category>
        
        
      </item>
    
      <item>
        <title>Emacs and Rails Testing</title>
        <description>&lt;p&gt;I’m an inveterate Emacs user. It’s been my Swiss Army chainsaw of choice for
over 33 years. Part of the beauty of Emacs is that it’s extensible and
configurable. The relatively new&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; package manager and ELPA package
archive are a great start, but sometimes it’s better — or simply more fun
— to roll your own.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This post was originally published
on &lt;a href=&quot;http://dirtybirds.chloeandisabel.com/&quot;&gt;The Dirty Birds&lt;/a&gt;, the Chloe +
Isabel tech team blog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve developed a few Emacs Lisp functions that let me run our Rails
&lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt; tests at varying levels of granularity and
within various environments.&lt;/p&gt;

&lt;p&gt;Granularity: &lt;em&gt;what&lt;/em&gt; to test.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Run all tests in a directory and its subdirectories.&lt;/li&gt;
  &lt;li&gt;Run all tests in a single file, by default the one that’s in the current
buffer.&lt;/li&gt;
  &lt;li&gt;Run a single test or single context within a spec file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Environment: &lt;em&gt;where&lt;/em&gt; to run the test.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Within an Emacs
&lt;a href=&quot;http://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html&quot;&gt;compilation buffer&lt;/a&gt;,
which has features like jumping right to the source code referenced in any
error message output can be used to jump directly to the relevant line of
code.&lt;/li&gt;
  &lt;li&gt;Within a shell running in an external terminal program such as the Mac OS
X Terminal program or &lt;a href=&quot;https://www.iterm2.com/&quot;&gt;iTerm 2&lt;/a&gt;. Running within a
shell instead of using compilation mode is necessary when you need to
interact with the tests, for example if you insert a
&lt;a href=&quot;https://github.com/deivid-rodriguez/byebug&quot;&gt;Byebug&lt;/a&gt; breakpoint in the
code. To send text to these external programs, we’ll use AppleScript.&lt;/li&gt;
  &lt;li&gt;Within a shell running inside Emacs. This is left as an exercise for the
reader.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many of the code samples here come from my Emacs init files, which are
available on Github at &lt;a href=&quot;https://github.com/jimm/elisp&quot;&gt;jimm/elisp&lt;/a&gt;. The bulk
of this code can be found in
&lt;a href=&quot;https://github.com/jimm/elisp/blob/master/progmodes/ruby-testing.el&quot;&gt;ruby-testing.el&lt;/a&gt;
and
&lt;a href=&quot;https://github.com/jimm/elisp/blob/master/progmodes/my-rails.el&quot;&gt;my-rails.el&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-command&quot;&gt;The Command&lt;/h3&gt;

&lt;p&gt;The first thing we need is the command we want to run. Variables that we
will fill in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;LOOK_LIKE_THIS&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &amp;lt;RAILS_ROOT&amp;gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; log/test.log &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test &lt;/span&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rspec &lt;span class=&quot;nt&quot;&gt;--seed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;SEED&amp;gt; &amp;lt;RPEC_PATH_AND_LINE&amp;gt;...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The first line takes us to the root of the Rails project.&lt;/p&gt;

&lt;p&gt;The second line empties out the log file without deleting it. If you move or
delete a log file that Rails has open it won’t recreate a new one, so
instead you can zero out the existing file by doing this. The third line
runs the test.&lt;/p&gt;

&lt;p&gt;The command to run the tests needs to specify a random seed and one or more
paths and optionally lines within files. The random seed is used by RSpec to
determine in what order tests are run. By default, we’ll use the value
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$RANDOM&lt;/code&gt; which is a magic variable defined by most Unix shells that returns
a different value every time it is read. If a test fails sometimes but not
others, that could be because the order of the tests matters to the outcome.
(If that is the case, it’s a sign that the test needs to be rewritten so
that it has no external or intra-test data dependencies.) In that case you
may want to make sure you use the same random seed while debugging so that
the tests are run in the same order.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RSPEC_PATH_AND_LINE&lt;/code&gt; can be a directory (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$RAILS_ROOT/spec/models&lt;/code&gt;), file
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$RAILS_ROOT/spec/models/foo.rb&lt;/code&gt;), or file plus line number
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$RAILS_ROOT/spec/models/foo.rb:42&lt;/code&gt;). If a line number is specified, then
the test(s) containing that line is run. The line number doesn’t have to be
the first line of the test; any line in the test will do. Additionally, if
the line number is outside of any single test but within a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context&lt;/code&gt; or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;describe&lt;/code&gt; block, then all tests in that block will be run.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rspec&lt;/code&gt; command can actually take multiple dir/file name arguments, but
we’re only going to worry about running one right now.&lt;/p&gt;

&lt;h4 id=&quot;generating-the-command&quot;&gt;Generating the Command&lt;/h4&gt;

&lt;p&gt;Here’s the elisp&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; code that generates the command. The function
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-rails--rspec-command&lt;/code&gt; takes a seed value and a file name (or directory
name or file-plus-line-number) and returns a string containing the command
we want. It calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find-rails-root&lt;/code&gt; to search for the Rails root directory
at or above the specified file’s directory. The two dashes in the function’s
name are a convention that tells the reader that this is a locally-used
internal function not intended for export or use by other code.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-emacs-lisp&quot; data-lang=&quot;emacs-lisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-rails--rspec-command&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rails-root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;find-rails-root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-name-directory&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rspec-cmd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-exists-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rails-root&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bin/rspec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;&quot;bin/rspec&quot;&lt;/span&gt;
                      &lt;span class=&quot;s&quot;&gt;&quot;rspec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cd &quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rails-root&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &amp;amp;&amp;amp; &quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;echo &amp;gt; log/test.log &amp;amp;&amp;amp; &quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;RAILS_ENV=test bundle exec &quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rspec-cmd&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-rails--seed-arg-string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The function that finds the Rails root directory at or above a file’s
directory is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find-rails-root&lt;/code&gt;. It uses the predicate function
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails-root-p&lt;/code&gt; to determine if a directory is a Rails root directory. By
long-standing Lisp convention, the names of predicate functions (those that
return true or false) end with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&lt;/code&gt;, like Ruby’s convention of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-emacs-lisp&quot; data-lang=&quot;emacs-lisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rails-root-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Returns true if path is a Rails root directory. Uses a heuristic that
involves looking for known Rails directories.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-directory-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-exists-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rakefile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-directory-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;app&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-directory-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;config&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-directory-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;lib&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-directory-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;find-rails-root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Returns Rails root dir at or above PATH. Returns nil if PATH is nil or no
Rails root dir is found. Uses `rails-root-p&apos;.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locate-dominating-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rails-root-p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The seed option for the command is generated by this function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-emacs-lisp&quot; data-lang=&quot;emacs-lisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-rails--seed-arg-string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Returns \&quot;--seed=SEED\&quot;. If SEED is 1, returns \&quot;--seed=$RANDOM\&quot;.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;--seed=&quot;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;equal&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;$RANDOM&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int-to-string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;One more addition lets us run the test(s) that are at point&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-emacs-lisp&quot; data-lang=&quot;emacs-lisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-rails--rspec-at-point-command&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;line-num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-rails--rspec-command&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;&quot;:&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int-to-string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line-number-at-pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;actually-running-the-tests&quot;&gt;Actually Running the Tests&lt;/h3&gt;

&lt;p&gt;Now that we can build the command we need, it’s simple to write functions
that run the command for the current line, current file, or an entire
directory.&lt;/p&gt;

&lt;p&gt;There are two “environments” (a compilation buffer within emacs or an
external terminal program) and really only two granularities (whatever is at
point or a file/directory). We can abstract those options a bit by passing
around functions. One pair of functions takes a command string and executes
it in the proper environment. For those we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&apos;compile&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&apos;send-to-iterm&lt;/code&gt;. The former function is built in to Emacs and the latter
is defined below.&lt;/p&gt;

&lt;p&gt;We could theoretically create another pair that determine granularity by
returning the proper final argument for the command string, but when I
starting thinking about doing that it seemed to me that the code would
become unnecessarily obtuse.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-emacs-lisp&quot; data-lang=&quot;emacs-lisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;run-spec-using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;run-func&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Run RSpec test FNAME from the rails root directory above it.
If SEED is 1, $RANDOM will be used. Calls
`my-rails--rspec-command&apos; to generated the command to run.
RUN-FUNC must be a function such as `compile&apos; that takes a string
and executes it.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;funcall&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;run-func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-rails--rspec-command&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;run-spec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Run RSpec test FNAME from the Rails root directory above it.
If SEED is 1, $RANDOM will be used. FNAME may contain extra line
number info (e.g., &apos;foo.rb::42&apos;).&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;p\nF&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;; possibly nonexistent file name so we can append &quot;:NNN&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;run-spec-using&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;run-spec-in-iterm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Run RSpec test at point in iTerm. If SEED is 1, $RANDOM will
be used.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;p\nF&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;; possibly nonexistent file name so we can append &quot;:NNN&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;run-spec-using&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;send-to-iterm&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;


&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;run-spec-at-point-using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Run RSpec test FNAME from the rails root directory above it.
If SEED is 1, $RANDOM will be used. Calls
`my-rails--rspec-at-point-command&apos; to generated the command to run.
RUN-FUNC must be a function such as `compile&apos; that takes a string
and executes it.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;funcall&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-rails--rspec-at-point-command&lt;/span&gt;
                 &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line-number-at-pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;run-spec-at-point&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Run RSpec test at point from the Rails root directory. If SEED is 1,
$RANDOM will be used.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;run-spec-at-point-using&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;run-spec-at-point-in-iterm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Run RSpec test at point in iTerm. If SEED is 1, $RANDOM will
be used.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;run-spec-at-point-using&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;send-to-iterm&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interactive&lt;/code&gt; is a special form&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; that tells the function what types the
args are and how to retrieve or prompt for them. Since the seed argument for
all of these functions is numeric and since I usually want to use the
default value, we tell the functions to use their
&lt;a href=&quot;http://www.gnu.org/software/emacs/manual/html_node/emacs/Arguments.html&quot;&gt;&lt;em&gt;numeric&lt;/em&gt; or &lt;em&gt;prefix argument&lt;/em&gt;&lt;/a&gt;
for the seed parameter.&lt;/p&gt;

&lt;p&gt;The final missing piece is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send-to-iterm&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-emacs-lisp&quot; data-lang=&quot;emacs-lisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;send-to-iterm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Send STR to the front window/session in iTerm. STR may contain
multiple lines separated by `\n&apos;.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;siTerm input: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;split-string&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;replace-regexp-in-string&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\&quot;&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\\&quot;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;do-applescript&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt;
                     &lt;span class=&quot;s&quot;&gt;&quot;tell application \&quot;iTerm\&quot;\n&quot;&lt;/span&gt;
                     &lt;span class=&quot;s&quot;&gt;&quot;	tell the current terminal\n&quot;&lt;/span&gt;
                     &lt;span class=&quot;s&quot;&gt;&quot;    tell the current session\n&quot;&lt;/span&gt;
                     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;mapconcat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;write text \&quot;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;lines&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                     &lt;span class=&quot;s&quot;&gt;&quot;    end tell\n&quot;&lt;/span&gt;
                     &lt;span class=&quot;s&quot;&gt;&quot;	end tell\n&quot;&lt;/span&gt;
                     &lt;span class=&quot;s&quot;&gt;&quot;end tell\n&quot;&lt;/span&gt;
                     &lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Writing this article caused me to review the code and improve it by
refactoring it a bit. I even found a glaring error in the design: I assumed
that the “current line” was to be found by asking the current buffer, even
when calling a function with a completely different file.&lt;/p&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h3&gt;

&lt;p&gt;I’m writing the book
&lt;a href=&quot;https://leanpub.com/emacs-mastery&quot;&gt;Emacs Mastery: Attaining Coding Supremacy&lt;/a&gt;,
and hope to be finished later this year. (Writing a book takes a long time.
Who knew?) In the mean time, enjoy
&lt;a href=&quot;https://www.masteringemacs.org/&quot;&gt;Mastering Emacs&lt;/a&gt; by Mickey Petersen.&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Of course, with an editor that’s around forty years old almost
everything is relatively new. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;That’s cool-kid shorthand for Emacs Lisp. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;“Point” is Emacs-speak for the location of the cursor in a buffer. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;&lt;em&gt;i.e.&lt;/em&gt;, magic. &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Fri, 17 Jul 2015 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2015/07/17/emacs-and-rails-testing.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2015/07/17/emacs-and-rails-testing.html</guid>
        
        <category>emacs</category>
        
        <category>testing</category>
        
        <category>programming</category>
        
        <category>ruby</category>
        
        <category>rails</category>
        
        
      </item>
    
      <item>
        <title>Emptying Full Client-Side Browser Storage</title>
        <description>&lt;p&gt;This morning I started seeing the following error on all preview pages
generated by an app running on my development machine:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;QUOTA_EXCEEDED_ERR: DOM EXCEPTION 22
in http://localhost:8080/sites/1341373150.915000/public_html/layout.less&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This error message isn’t caused by the application, it’s because the
generated HTML uses client-side LESS to render its CSS. The LESS processing
had filled up local storage, because each preview generates new storage keys
due to the ever-changing timestamp part of the URL.&lt;/p&gt;

&lt;p&gt;The solution: here’s how to manually clear your browser’s local storage of
any data whose keys match a particular value:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Clears local storage of keys that match a regular expression.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clear_local_storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;keep_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key_regexp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;keep_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;removeItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A sample of its use:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nf&quot;&gt;clear_local_storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;^http://localhost:8080/sites.*layout.less(:timestamp)?$&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Since I wrote the app that’s publishing the pages that generate this error,
I’m considering making the app inject this JavaScript code into the pages to
make sure that end users never see this error message.&lt;/p&gt;
</description>
        <pubDate>Wed, 04 Jul 2012 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2012/07/04/emptying-full-client-side-browser-storage.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2012/07/04/emptying-full-client-side-browser-storage.html</guid>
        
        
      </item>
    
      <item>
        <title>Identity Recall</title>
        <description>&lt;p&gt;&lt;a href=&quot;http://www.io.com&quot;&gt;io.com&lt;/a&gt; is going away. It’s funny how upsetting this is
to me. I’ve been &lt;a href=&quot;mailto:jimm@io.com&quot;&gt;jimm@io.com&lt;/a&gt; since 1994 or so—maybe
a year or two earlier than that. You know what I’m worried about most? All
those open source projects, emails, and other digital resources that point
to jimm@io.com are going to be pointing nowhere in a month. It feels like my
online identity is being stolen. Except it’s not being stolen, of
course—merely recalled.&lt;/p&gt;

&lt;p&gt;A Slashdot commenter also pointed out the potential security risk: “Think
about it, among other things whoever owns that domain now will be able to
intercept all mail to io.com accounts, and with the quickness and suddenness
of the transfer not everyone’s who uses those addresses is going to be able
to completely transition off them before the transfer happens”&lt;/p&gt;

&lt;p&gt;io.com was the ISP run by Steve Jackson Games—the company raided by the
Secret Service because they were writing a role playing game called
“Hacker”. SJ Games fought the Secret Service and won. I &lt;em&gt;think&lt;/em&gt; the
EFF grew out of that case.&lt;/p&gt;

&lt;p&gt;io.com was bought by &lt;a href=&quot;http://www.prismnet.com&quot;&gt;prismnet.com&lt;/a&gt; years ago.
PrismNet changed hands a few times. The last guy who sold it to the current
owner (for $20) didn’t sell the io.com domain. He kept it but let them use
it until July 1, 2011. My guess is he wants to sell it to I/O Digital for a
skillion dollars.&lt;/p&gt;

&lt;p&gt;From now on, I’m &lt;a href=&quot;mailto:jim@jimmenard.com&quot;&gt;jim@jimmenard.com&lt;/a&gt;. Also
&lt;a href=&quot;mailto:jim.menard@gmail.com&quot;&gt;jim.menard@gmail.com&lt;/a&gt;, or
&lt;a href=&quot;mailto:jimm@prismnet.com&quot;&gt;jimm@prismnet.com&lt;/a&gt;. Time to start wrapping my
head around being jim@jimmenard.com instead of jimm@io.com. Bye,
jimm@io.com.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.prismnet.com/html/iocomchange&quot;&gt;http://www.prismnet.com/html/iocomchange&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://games.slashdot.org/story/11/06/01/0219211/A-Piece-of-Internet-History-Lost-IOcom-Sold-Services-To-Shut-Down&quot;&gt;Slashdot: A Piece of Internet History Lost: IO.com Sold, Services To Shut Down&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 04 Jun 2011 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2011/06/04/identity-recall.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2011/06/04/identity-recall.html</guid>
        
        
      </item>
    
      <item>
        <title>Play Framework Configuration</title>
        <description>&lt;p&gt;I’ve been using the &lt;a href=&quot;http://www.playframework.org/&quot;&gt;Play framework&lt;/a&gt; for a
big project at work. It’s fantastic. I almost hesitate to say it, but it’s
like Ruby on Rails for Java. Watch the video that’s on the home page; it’s a
good quick introduction.&lt;/p&gt;

&lt;p&gt;The Tech Ops team at work wants control over configuration of the app. This
makes sense for a few reasons. First, they’re responsible for running it so
they should be able to tweak things like memory settings. Second, they need
to tell the app where the database lives, what the password is, etc. Tech
Ops also wants the app to run as a WAR inside an app server. That’s fine:
Play comes with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;war&lt;/code&gt; command that bundles your app into either an
exploded WAR or a WAR file.&lt;/p&gt;

&lt;p&gt;The problem is, Play doesn’t know how to read any configuration file other
than the one that’s in its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conf&lt;/code&gt; directory. At least, that’s what I
thought. It turns out there is an undocumented feature of Play configuration
files: If any configuration key is named&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@include.FOO&lt;/code&gt;, then the
value is used as a path and that file is read, too.&lt;/p&gt;

&lt;p&gt;A few caveats.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The path is relative to the application directory within the WAR. So, for
example, to find a file in Tomcat’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conf&lt;/code&gt; directory, the path will have
to be something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;../../../../../conf/play.conf&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The file that gets included isn’t treated exactly like the normal
configuration file. Magic values like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${play.path}&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${application.path}&lt;/code&gt; are not interpreted.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The current Play framework id (similar to Rails’ environment) is ignored.
However, since the include code is run after everything else is loaded,
this isn’t really a problem since the external configuration file you’re
loading is presumably for the environment you’re running.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also a plugin architecture, and a hook that runs after the
configuration file is read. This means that, even if
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@include.FOO&lt;/code&gt; didn’t exist, you can write a plugin to do
anything you want to alter the app’s configuration.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: Core Play developer Guillaume Bort wrote on the email list, “This
feature is not yet documented because it is not ready: as you observed it
the placeholders and the framework id stuff are not handled properly.”
Caveat emptor.&lt;/p&gt;
</description>
        <pubDate>Sun, 25 Jul 2010 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2010/07/25/play-framework-configuration.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2010/07/25/play-framework-configuration.html</guid>
        
        <category>rails</category>
        
        <category>programming</category>
        
        <category>opensource</category>
        
        <category>frameworks</category>
        
        <category>video</category>
        
        <category>java</category>
        
        
      </item>
    
      <item>
        <title>Up, Up, and Away</title>
        <description>&lt;p&gt;Let’s say you are in a subdirectory, and there’s a Makefile in a parent
directory. How do you run make? You climb back up to the parent directory
and type “make”, or use “make -C parent_dir”. In either case, you have to
know what the parent dir is. Here are a pair of scripts that do that for
you.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findup&lt;/code&gt; script’s job is to find a file in a parent directory. It prints
the directory name if found, else it exits with status 1.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#! /bin/sh&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# usage: findup file [file...]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Finds any of the files in the current directory or any directory above and&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# prints the directory name. If no such directory is found (we hit the root&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# directory), exit 1. Note: will not find files in the &apos;/&apos; directory.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;start_dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
    for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
        if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
            &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0
        &lt;span class=&quot;k&quot;&gt;fi
    done&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Keep swimming...keep swimming...swimming, swimming, swimming...&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# No parent directory&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;makeup&lt;/code&gt; script uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findup&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;c&quot;&gt;#! /bin/sh&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# usage: makeup [args...]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Finds the first makefile in the current directory or any directory above and&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# then runs make, passing on any args given to this script.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Relies on the &quot;findup&quot; command, which is found in this directory.&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;mfdir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;findup makefile Makefile&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$mfdir&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;no makefile found
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# We&apos;ve found a makefile. Use the -C flag to tell make to run from the&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# directory where we found the makefile. We do this so that error messages&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# produced during the make process are relative to the current directory. I&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# think.&lt;/span&gt;
make &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$mfdir&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$*&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Sat, 12 Sep 2009 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2009/09/12/up-up-and-away.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2009/09/12/up-up-and-away.html</guid>
        
        <category>programming</category>
        
        <category>filesystem</category>
        
        <category>unix</category>
        
        <category>shell</category>
        
        
      </item>
    
      <item>
        <title>ChucK Hack Fork</title>
        <description>&lt;p&gt;I’ve created a GitHub fork of &lt;a href=&quot;http://chuck.cs.princeton.edu/&quot;&gt;ChucK&lt;/a&gt;, the
“strongly-timed, concurrent, and on-the-fly audio programming language.” In
&lt;a href=&quot;https://github.com/jimm/chuck&quot;&gt;my fork&lt;/a&gt; I’ve started to add missing features
like string methods, and hope to add many more such as raw file I/O and
perhaps some form of MIDI file I/O.&lt;/p&gt;

&lt;p&gt;So far, I’ve added string.ch which is a getter and setter for string
characters (really one-character length substrings) and string.substr. This
allowed me to write a function in ChucK that converts strings like “c#4” to
MIDI note numbers.&lt;/p&gt;

&lt;p&gt;It looks like ChucK’s code was last updated in 2006, so I don’t feel to bad
about forking it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: I just read in the chuck email list archive that the team is
planning to start active development again, and they will be adding file
I/O. If they get it done before I do, that would be very nice.&lt;/p&gt;
</description>
        <pubDate>Mon, 03 Aug 2009 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2009/08/03/chuck-hack-fork.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2009/08/03/chuck-hack-fork.html</guid>
        
        <category>audio</category>
        
        <category>c++</category>
        
        <category>music</category>
        
        <category>programming</category>
        
        <category>midi</category>
        
        
      </item>
    
      <item>
        <title>A Weak Man Begs Forgiveness</title>
        <description>&lt;p&gt;&lt;em&gt;This attempt at humor was written while recovering from the Swine Flu.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dearest Colleagues,&lt;/p&gt;

&lt;p&gt;I write this electronically-delivered letter to you in the hopes that you
will forgive me. A terrible fate has befallen my lovely daughter
V_______, and I am afrid that I now share in her misfortune. To
protect you all from similarly horrible destinies, I must eschew your
company this day.&lt;/p&gt;

&lt;p&gt;It all started innocently enough, with a persistent cough. My eldest
daughter would delicately hack to “clear her throat”. The coughs became more
frequent and urgent. She developed a fever which burned her pure, innocent
forehead. As time passed, she begain to writhe in agony, delirious with
dreams of God knows not what Heavenly retributions and Hellish tortures.&lt;/p&gt;

&lt;p&gt;My dearest wife E____ conveyed her to the pediatrician’s office on
Sunday. Her doctor (a woman, by God! Ah, well. I know not what muse guides
Science, but she must hold in favor the fairer sex) diagnosed
V_______ with Swine Flu. The woman practiced her Art with confidence,
perscribing the most modern treatments available.&lt;/p&gt;

&lt;p&gt;We repaired to our home, powders and poultices in hand. The cure had barely
taken hold of my dear daughter when I myself began to cough. At first, I
denied the obvious. What strong man would not? Having fought in The War, my
thoughts were not for my own safety or bodily condition but for that of my
child.&lt;/p&gt;

&lt;p&gt;I tell you now: Mr. Poe never wrote of horrors so small. The tiny demons
that ravage my body even now defy all description, and to burden you with
reports of the agony they cause does not merit further thought.&lt;/p&gt;

&lt;p&gt;The submarine-like conditions of the Juicyorange office being the breeding
grounds for animalcules it is, it is best that I remain far, far away. This
is why I have decided to remain in an entirely different State of the Union.
My sorrow at not joining you is only mitigated by the knowledge that you
will not be exposed to the fiends that wrack my body.&lt;/p&gt;

&lt;p&gt;In the fervent hope that this missive is not rerouted to your Spam folder,&lt;/p&gt;

&lt;p&gt;With Deepest Respect,&lt;br /&gt;
James J. Menard&lt;br /&gt;
Babbage Engine Servitor&lt;/p&gt;
</description>
        <pubDate>Mon, 22 Jun 2009 00:00:00 -0400</pubDate>
        <link>http://jimmenard.com/2009/06/22/a-weak-man-begs-forgiveness.html</link>
        <guid isPermaLink="true">http://jimmenard.com/2009/06/22/a-weak-man-begs-forgiveness.html</guid>
        
        <category>humor</category>
        
        
      </item>
    
  </channel>
</rss>
