<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="https://www.w3.org/2005/Atom">

 <title>Mat Schaffer's blog</title>
 <link href="https://matschaffer.com/atom.xml" rel="self"/>
 <link href="https://matschaffer.com/"/>
 <updated>2026-03-21T01:52:26+00:00</updated>
 <id>https://matschaffer.com/</id>
 <author>
   <name>Mat Schaffer</name>
   <email>mat@schaffer.me</email>
 </author>

 

<entry>
  <title>Monitoring my home internet with Elastic</title>
  <link href="https://matschaffer.com/2021/10/04/monitor-home-internet-with-elastic.html"/>
  <updated>2021-10-04T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2021/10/04/monitor-home-internet-with-elastic</id>
  <content type="html">&lt;p&gt;Since the start of COVID19, my home internet has gotten noticeably slow between the hours of 8p and midnight (JST, local).&lt;/p&gt;

&lt;p&gt;I suspect this is a widespread issue as more people are staying home and doing things that exercise both local and global internet connections.&lt;/p&gt;

&lt;p&gt;My ISP seems to do a decent job keeping things like video streaming prioritized, but work can be a big pain. Operations like apt update/install on a local VMs, npm install, browsing company websites can get &lt;em&gt;very&lt;/em&gt; slow. It’s even worse when I’m on a full-tunnel VPN as I suspect in this case my ISP can’t make any sort of traffic priority decisions.&lt;/p&gt;

&lt;p&gt;For the last year, I mostly ignored it. With so much of the world suffering and dying, it’s difficult to complain about slow internet.&lt;/p&gt;

&lt;p&gt;But it’s gone on so long now I decided to at least get scientific about it.&lt;/p&gt;

&lt;h2 id=&quot;the-approach&quot;&gt;The approach&lt;/h2&gt;

&lt;p&gt;I figured if I could have something like &lt;a href=&quot;https://www.speedtest.net/&quot;&gt;Ookla Speedtest&lt;/a&gt; running continually and storing data, I could demonstrate and quantify the issue.&lt;/p&gt;

&lt;p&gt;But what to use?&lt;/p&gt;

&lt;h3 id=&quot;internet-speed-logger&quot;&gt;Internet Speed Logger&lt;/h3&gt;

&lt;p&gt;Having recently helped out &lt;a href=&quot;https://www.elastic.co/uptime-monitoring&quot;&gt;Elastic Uptime&lt;/a&gt; I first reached out there.&lt;/p&gt;

&lt;p&gt;It was notably outside their intended scope, but the project’s PM mentioned he’d done similar home network testing with &lt;a href=&quot;https://github.com/brennentsmith/internet-speed-logger&quot;&gt;Brennen Smith’s Internet Speed Logger&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This got me some quick results:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/internet-speed-logger.png&quot; alt=&quot;screenshot of internet speed logger showing bumpy traffic graphs&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But it left something to be desired. It was hard to see what time the dips were happening and I didn’t see any options to change aggregations or do other processing. I’d probably have to hack on the code directly to draw useful conclusions.&lt;/p&gt;

&lt;p&gt;Additionally this was a mongodb+node setup on docker compose. Easy to do on my MacBook, but a little trickier to run from my Raspberry Pi 4 which was the handiest stable computer on my network.&lt;/p&gt;

&lt;h3 id=&quot;using-the-elastic-stack&quot;&gt;Using the Elastic Stack&lt;/h3&gt;

&lt;p&gt;I mentioned my testing in passing to one of my coworkers, &lt;a href=&quot;https://github.com/tobio&quot;&gt;Toby Brain&lt;/a&gt;, who said he did something similar with the Ookla CLI shipping data to Elasticsearch.&lt;/p&gt;

&lt;p&gt;This sounded great for the following reasons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I could host Elasticsearch &amp;amp; Kibana on &lt;a href=&quot;https://cloud.elastic.co/&quot;&gt;cloud.elastic.co&lt;/a&gt; and not worry about data storage availability&lt;/li&gt;
  &lt;li&gt;Filebeat and the Ookla CLI should be light enough to run on my Raspberry Pi&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;setting-up-the-cluster&quot;&gt;Setting up the cluster&lt;/h4&gt;

&lt;p&gt;This was easy! Just go to &lt;a href=&quot;https://cloud.elastic.co/&quot;&gt;cloud.elastic.co&lt;/a&gt;, sign up and make a cluster.&lt;/p&gt;

&lt;p&gt;If you don’t happen to work at Elastic, you’ll get at 14 day free trial. If you want more, you could start a &lt;a href=&quot;https://www.elastic.co/pricing/&quot;&gt;paying plan&lt;/a&gt;, or &lt;a href=&quot;https://www.elastic.co/about/careers/&quot;&gt;consider getting a job at Elastic&lt;/a&gt; ;). Or of course you could also host your own elasticsearch and kibana on a spare machine with enough resources (but probably not a Raspberry Pi). The data set is quite small. Only about 600kb for 3 days so far.&lt;/p&gt;

&lt;h4 id=&quot;setting-up-the-ookla-cli&quot;&gt;Setting up the Ookla CLI&lt;/h4&gt;

&lt;p&gt;Installation here is easy. Just grab a copy from &lt;a href=&quot;https://www.speedtest.net/apps/cli&quot;&gt;the speedtest cli download page&lt;/a&gt; for your process (arm in my case) and untar it. Move the binary somewhere handy (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;One small “gotcha” is that each user has to run it once with no args to accept the EULA which will write into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME/.config/ookla&lt;/code&gt;). No big deal for my setup, but if you have users without home directories or something, watch out for it.&lt;/p&gt;

&lt;p&gt;To run the speed test and get json output, you’ll need a server ID. You can get one like this:&lt;/p&gt;

&lt;!-- /* cSpell:disable */ --&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ speedtest -L
Closest servers:

    ID  Name                           Location             Country
==============================================================================
 45080  ド田舎ネットワーク！By mino7r86. Kawagoe              Japan
  6087  Allied Telesis Capital Corporation Fussa-shi            Japan
  6492  denpa893                       Tokyo                Japan
 41592  NEVERLOSS LLC.                 Tokyo                Japan
 42083  LiyingNetwork                  Tokyo                Japan
 42297  Netprotect                     Tokyo                Japan
 43063  k-kohei.jp                     Tokyo                Japan
 43744  SERVG.NET MG-Network           Tokyo                Japan
 45012  NEROCLOUD INC.                 Tokyo                Japan
 44988  Misaka Network, Inc.           Tokyo                Japan
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- /* cSpell:enable */ --&gt;

&lt;p&gt;I was also interested in non-local test server. This was trickier, but I found an &lt;a href=&quot;https://c.speedtest.net/speedtest-servers-static.php&quot;&gt;Ookla XML feed&lt;/a&gt; that returns different lists based on your location. So I checked that while on a US VPN to get a second server ID.&lt;/p&gt;

&lt;p&gt;With two IDs handy, I set up a cron job and that was it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat /etc/cron.d/speedtest
10 * * * * root (/usr/local/bin/speedtest -s 42297 -f json; /usr/local/bin/speedtest -s 10979 -f json) &amp;gt;&amp;gt; /var/log/speedtests
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;setting-up-filebeat&quot;&gt;Setting up filebeat&lt;/h4&gt;

&lt;p&gt;The basic configuration if filebeat was simple. Just this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat /etc/filebeat/filebeat.yml
cloud.id: &quot;(your deployment&apos;s cloud ID)&quot;
cloud.auth: &quot;(your cluster&apos;s user):(your cluster&apos;s password)&quot;

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/speedtests*
    scan_frequency: 1m
    json.keys_under_root: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Getting filebeat set up on the Raspberry Pi was much more complicated. Elastic doesn’t have official binaries on the download page, so I ended up compiling from source. This required golang &amp;gt;= 1.16 which also isn’t available from Raspbian’s apt repositories. So I did this the old fashioned way.&lt;/p&gt;

&lt;p&gt;First grab the armv6 version of &lt;a href=&quot;https://golang.org/dl/&quot;&gt;golang&lt;/a&gt;, unpack that into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Export the go path to where you extracted it and go binaries onto your shell path:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ export GOPATH=$HOME/go
$ export PATH=&quot;$PATH:$HOME/go/bin&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Grab the beats source and get into the filebeat directory and run make:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git clone https://github.com/elastic/beats ${GOPATH}/src/github.com/elastic/beats
$ cd ${GOPATH}/src/github.com/elastic/beats/filebeat
$ make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally put the resulting binary into your path:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo mv filebeat /usr/local/bin/filebeat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you ran into build trouble, feel free to post a note on the &lt;a href=&quot;https://discuss.elastic.co/tags/c/elastic-stack/beats/28/filebeat&quot;&gt;filebeat forum&lt;/a&gt;. You can even mention me, though I suspect other engineers will jump in before I even notice.&lt;/p&gt;

&lt;p&gt;And finally I set up a systemd service to keep it running. I originally extracted this definition from the filebeat deb package and modified the executable path.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat /etc/systemd/system/filebeat.service
[Unit]
Description=Filebeat sends log files to Logstash or directly to Elasticsearch.
Documentation=https://www.elastic.co/beats/filebeat
Wants=network-online.target
After=network-online.target

[Service]

Environment=&quot;GODEBUG=&apos;madvdontneed=1&apos;&quot;
Environment=&quot;BEAT_LOG_OPTS=&quot;
Environment=&quot;BEAT_CONFIG_OPTS=-c /etc/filebeat/filebeat.yml&quot;
Environment=&quot;BEAT_PATH_OPTS=--path.home /usr/share/filebeat --path.config /etc/filebeat --path.data /var/lib/filebeat --path.logs /var/log/filebeat&quot;
ExecStart=/usr/local/bin/filebeat --environment systemd $BEAT_LOG_OPTS $BEAT_CONFIG_OPTS $BEAT_PATH_OPTS
Restart=always

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And enable the service.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo systemctl enable filebeat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now if you have any tests in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log/speedtest&lt;/code&gt;, they should end up in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filebeat-*&lt;/code&gt; in your Elasticsearch cluster.&lt;/p&gt;

&lt;p&gt;A little more involved than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker compose up&lt;/code&gt;, for sure. But now we have a much more stable setup plus all the facilities of kibana at our disposal for analysis.&lt;/p&gt;

&lt;h4 id=&quot;graphing&quot;&gt;Graphing&lt;/h4&gt;

&lt;p&gt;There’s a bunch of data in the speedtest CLI JSON payload, but I was mainly interested in looking and downstream and upstream speeds over time.&lt;/p&gt;

&lt;p&gt;The speeds recorded were a bit confusing at first. The main parts of the data look like this:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2021-10-12T05:10:16Z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ping&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;jitter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.218&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;latency&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;8.1850000000000005&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;download&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;bandwidth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3457698&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;bytes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15579024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;elapsed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4404&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;upload&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;bandwidth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5292178&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;bytes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;23912512&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;elapsed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4502&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;isp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ARTERIA Networks Corporation&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;download.bandwidth&lt;/code&gt; number is listed in bytes per second, but ISPs usually sell in megabits per second (Mbps). Thankfully Kibana lens now has formulas, so the conversion is easy. Just divide by 8, then 1000000. Or here I’ve used multiply to keep the formula short and simple.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multiply(median(download.bandwidth), 0.000008)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Same for downstream and we’re in business!&lt;/p&gt;

&lt;h2 id=&quot;results-and-conclusion&quot;&gt;Results and Conclusion&lt;/h2&gt;

&lt;p&gt;With data in hand I can now see when my internet speeds get slow (mostly noon and 9p) and plan accordingly. Also interesting is that my upstream stays consistently high.&lt;/p&gt;

&lt;p&gt;Once the covid situation calms down I may contact my ISP about other options, but I suspect issue extends beyond just my local ISP.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/kibana-speed-data.png&quot; alt=&quot;screenshot showing internet speeds with a clear downstream dip in the evenings&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to use the same dashboard, I’ve exported it here for import into your own kibana: &lt;a href=&quot;/files/internet-speeds-dashboard.ndjson&quot;&gt;internet-speeds-dashboard.ndjson&lt;/a&gt;&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Dealing with MacBook CPU throttling</title>
  <link href="https://matschaffer.com/2021/09/03/macbook-throttling.html"/>
  <updated>2021-09-03T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2021/09/03/macbook-throttling</id>
  <content type="html">&lt;p&gt;About two months ago, I switched from the &lt;a href=&quot;https://cloud.elastic.co/&quot;&gt;Elastic Cloud&lt;/a&gt; SRE team to one of the &lt;a href=&quot;https://www.elastic.co/kibana/&quot;&gt;Kibana&lt;/a&gt; software teams at Elastic. This has had me using a lot fewer cloud instances and a lot more of my local laptop CPU.&lt;/p&gt;

&lt;p&gt;Elastic gave me a fairly new MacBook Pro (16-inch, 2019), with a 2.4 GHz 8-Core Intel Core i9 and a whopping 64 GB 2667 MHz DDR4. By all means this computer should be crazy fast. But when I ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn kbn bootstrap&lt;/code&gt; for the first time, my computer was &lt;em&gt;terribly&lt;/em&gt; slow. Even typing in my local slack app was lagged like I was on a bad SSH connection.&lt;/p&gt;

&lt;p&gt;So I started trying to figure out why.&lt;/p&gt;

&lt;p&gt;Googling &lt;a href=&quot;https://www.google.com/search?q=macbook+pro+throttling&quot;&gt;macbook pro throttling&lt;/a&gt; led me quickly to &lt;a href=&quot;https://appletoolbox.com/check-if-mac-is-thermal-throttling/&quot;&gt;this apple toolbox post&lt;/a&gt; which recommended checking like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ pmset -g thermlog
Note: No thermal warning level has been recorded
Note: No performance warning level has been recorded
2021-09-03 13:22:05 +0900 CPU Power notify
	CPU_Scheduler_Limit 	= 100
	CPU_Available_CPUs 	= 16
	CPU_Speed_Limit 	= 34
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ouch. 34 looked pretty low.&lt;/p&gt;

&lt;p&gt;Next I tried the &lt;a href=&quot;https://github.com/macmade/Hot&quot;&gt;Hot&lt;/a&gt; app, which at least told me &lt;em&gt;when&lt;/em&gt; I was throttling, but not much about &lt;em&gt;why&lt;/em&gt;. Also the temperature was quite a bit lower than I expected.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/hot-30-percent.png&quot; alt=&quot;Hot app showing 30% CPU&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So I tried &lt;a href=&quot;https://software.intel.com/content/www/us/en/develop/articles/intel-power-gadget.html&quot;&gt;Intel Power Gadget&lt;/a&gt; next. This had a lot more detail, I still couldn’t work out the reason for the throttling. I’d read about possible external display problems so I unplugged it and saw my CPUs jump back.&lt;/p&gt;

&lt;p&gt;I &lt;a href=&quot;https://twitter.com/matschaffer/status/1430393627579093000&quot;&gt;tweeted this out&lt;/a&gt; and it started a nice thread that led me to consider my discreet GPU.&lt;/p&gt;

&lt;p&gt;After a bit more digging, I found &lt;a href=&quot;https://gaucho.software/Products/XRG/&quot;&gt;XRG&lt;/a&gt; which included configurable temperature sensor graphs, including the dGPU.&lt;/p&gt;

&lt;p&gt;With some new tools in hand, I started working and sure enough I could see that the throttling kicked in when my dGPU got up to around 80C.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/dgpu-heat-vs-cpu-speed.jpeg&quot; alt=&quot;Two metric tools showing correlation between dGPU temp and CPU speed&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;About this time, I also needed to do some work on VirtualBox which conflicted with the Intel Power Gadget. So I found this as a replacement to watch my core speeds, which XRG couldn’t.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ sudo powermetrics --samplers cpu_power | grep &apos;CPU Average frequency&apos;
CPU Average frequency as fraction of nominal: 105.85% (2540.51 Mhz)
CPU Average frequency as fraction of nominal: 97.23% (2333.44 Mhz)
CPU Average frequency as fraction of nominal: 105.58% (2533.88 Mhz)
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The throttling is actually easier to spot this way because all the cores will sync to the same reduced clock speed even when processes are putting demand on the CPU.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CPU Average frequency as fraction of nominal: 75.00% (1800.00 Mhz)
CPU Average frequency as fraction of nominal: 75.00% (1800.00 Mhz)
CPU Average frequency as fraction of nominal: 75.00% (1800.00 Mhz)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I’m far from a hardware heat-management expert here. But from various posts I can see that the CPU and GPU share a heat sink. And the dGPU is required to drive the external display. So what seems to be happening is that when display is plugged in, the dGPU starts heating up. If the system decides the GPU is too hot for the CPU to cool itself, it slows down the CPU to avoid overheating and potential damage.&lt;/p&gt;

&lt;p&gt;Of course, I’m glad I still have a working laptop, but I’d also like to have a fast laptop :)&lt;/p&gt;

&lt;p&gt;So far the best workaround I’ve found is the combination of a cooling stand (with fans) and keeping the air conditioning a little uncomfortably cold.&lt;/p&gt;

&lt;p&gt;Or if I can live without my external display for a bit, I’ll unplug it and work on the laptop display. Usually until whatever big CPU job (often kibana bootstrap or optimizer work) is done then I can plug it back in.&lt;/p&gt;

&lt;p&gt;I’d love to take it to the Apple Store to check if the fans are okay. It could be they’re dusty as Sergey Stadnik found in &lt;a href=&quot;https://ozmoroz.com/2020/07/macos-kernel-tasks/&quot;&gt;this post&lt;/a&gt;. But my location, COVID19 and vaccine shortages eliminate that possibility.&lt;/p&gt;

&lt;p&gt;Maybe next year 🤞&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Uninstalling Docker Desktop for macOS</title>
  <link href="https://matschaffer.com/2021/09/01/uninstalling-docker-desktop.html"/>
  <updated>2021-09-01T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2021/09/01/uninstalling-docker-desktop</id>
  <content type="html">&lt;p&gt;This morning, like many developers, I woke up to an email telling me that &lt;a href=&quot;https://www.docker.com/blog/updating-product-subscriptions/&quot;&gt;Docker Desktop&lt;/a&gt; would now require a subscription for me to use at work.&lt;/p&gt;

&lt;p&gt;While I’m sure my company could handle the expense, I wasn’t happy to about the likely overhead and wanted to explore my options.&lt;/p&gt;

&lt;p&gt;So I took a deep breath and hit the uninstall button.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/docker-desktop-uninstall.png&quot; alt=&quot;Docker desktop preferences dialog showing the uninstall button&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This part was easy. But how should we get docker functionality back?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/machine/&quot;&gt;Docker Machine&lt;/a&gt; is an option I’ve used before Docker Desktop and it doesn’t seem to be covered under the new licensing requirements. It’s also available via homebrew, so it’s an easy option to try out.&lt;/p&gt;

&lt;h2 id=&quot;the-basics&quot;&gt;The basics&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install it&lt;/span&gt;
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;docker-machine

&lt;span class=&quot;c&quot;&gt;# Provision a default machine&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# My docker desktop was set to 12GB of memory, and I&apos;m setting disk to 100GB to accommodate the many images I have to work with.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Please adjust for your machine.&lt;/span&gt;
docker-machine create &lt;span class=&quot;nt&quot;&gt;--virtualbox-cpu-count&lt;/span&gt; 4  &lt;span class=&quot;nt&quot;&gt;--virtualbox-memory&lt;/span&gt; 12288 &lt;span class=&quot;nt&quot;&gt;--virtualbox-disk-size&lt;/span&gt; 102400 default

&lt;span class=&quot;c&quot;&gt;# Add this to my zshrc to export the env if docker-machine is present&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; docker-machine &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;docker-machine &lt;span class=&quot;nb&quot;&gt;env &lt;/span&gt;default&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Docker Machine doesn’t come with a docker CLI client so brew that as well.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;docker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And seems like we’re in business.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ docker ps

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;running-docker-compose&quot;&gt;Running docker compose&lt;/h2&gt;

&lt;p&gt;Next I’ll try a docker compose test tool I often use.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ docker compose -f create-certs.yml run --rm create_certs
unknown shorthand flag: &apos;f&apos; in -f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Interesting. The brew installed docker CLI doesn’t include the fancy new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compose&lt;/code&gt; subcommand that docker desktop does.&lt;/p&gt;

&lt;p&gt;Well, that’s just a brew away:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;docker-compose
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And my previous docker compose workflows seem to work just fine.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ docker-compose -f create-certs.yml run --rm create_certs
# Good stuff happening

❯ ELASTIC_VERSION=7.15.0-SNAPSHOT docker-compose up
# Mostly good stuff, but then this
node01        | ERROR: [1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch.
node01        | bootstrap check failure [1] of [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html&quot;&gt;elasticsearch docs&lt;/a&gt; cover the sysctl setting I’ll need to correct this. But how do I get that into docker machine?&lt;/p&gt;

&lt;p&gt;Thankfully I came across &lt;a href=&quot;https://github.com/boot2docker/boot2docker/issues/1216&quot;&gt;github.com/boot2docker/boot2docker#1216&lt;/a&gt; which has the info I need. Amusingly for the same Elasticsearch memory requirement.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ docker-machine ssh default
...
docker@default:~$ sudo -i
# Set it for the current runtime
root@default:~# sysctl -w vm.max_map_count=262144
vm.max_map_count = 262144
# Put it in the profile for next boot too
root@default:~# echo &apos;sysctl -w vm.max_map_count=262144&apos; &amp;gt;&amp;gt; /var/lib/boot2docker/profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I restarted the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose&lt;/code&gt; command and things are looking better.&lt;/p&gt;

&lt;p&gt;Finally, let’s load up the kibana inside the docker-compose stack I just started.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/localhost-kibana.png&quot; alt=&quot;Browser showing no site at localhost:5601&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;No love. Since docker machine runs a VirtualBox VM, the network space isn’t shared with my OS.&lt;/p&gt;

&lt;p&gt;But docker machine can tell me the IP I need easily enough.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ docker-machine ls
NAME      ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER      ERRORS
default   *        virtualbox   Running   tcp://192.168.99.100:2376           v19.03.12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/docker-machine-kibana.png&quot; alt=&quot;Browser showing kibana running on 192.168.99.100:5601&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-about-kubernetes&quot;&gt;What about kubernetes?&lt;/h2&gt;

&lt;p&gt;I also had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind&lt;/code&gt; based tool I needed to run. So let’s try that:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ brew install kind kubectl
...
❯ kind create cluster
Creating cluster &quot;kind&quot; ...
 ✓ Ensuring node image (kindest/node:v1.21.1) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✗ Starting control-plane 🕹️
ERROR: failed to create cluster: failed to init node with kubeadm: command &quot;docker exec --privileged kind-control-plane kubeadm init --skip-phases=preflight --config=/kind/kubeadm.conf --skip-token-print --v=6&quot; failed with error: exit status 1
Command Output: I0901 23:58:29.674676     210 initconfiguration.go:246] loading configuration from &quot;/kind/kubeadm.conf&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;More bumps.&lt;/p&gt;

&lt;p&gt;For this one I managed to google my way to &lt;a href=&quot;https://github.com/microsoft/WSL/issues/4189#issuecomment-851919194&quot;&gt;github.com/microsoft/WSL#4189&lt;/a&gt; which mentioned that missing systemd could be an issue. So I’ll try that fix.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ docker-machine ssh default
   ( &apos;&amp;gt;&apos;)
  /) TC (\   Core is distributed with ABSOLUTELY NO WARRANTY.
 (/-_--_-\)           www.tinycorelinux.net

docker@default:~$ sudo -i
root@default:~# mkdir /sys/fs/cgroup/systemd
root@default:~# echo &apos;mkdir /sys/fs/cgroup/systemd&apos; &amp;gt;&amp;gt; /var/lib/boot2docker/profile
root@default:~# mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd
root@default:~# echo &apos;mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd&apos; &amp;gt;&amp;gt; /var/lib/boot2docker/profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This did the trick to get kind booted normally. But of course, more trouble:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;❯ kubectl cluster-info --context kind-kind

To further debug and diagnose cluster problems, use &apos;kubectl cluster-info dump&apos;.
The connection to the server 127.0.0.1:57999 was refused - did you specify the right host or port?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To fix this up we’ll need to create kind with the right server address. So we’ll create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-yaml 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;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Cluster&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kind.x-k8s.io/v1alpha4&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;networking&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;apiServerAddress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;192.168.99.101&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;apiServerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;57999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The IP address here is from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-machine ip&lt;/code&gt; and the port number is whatever the last attempt’s error used.&lt;/p&gt;

&lt;p&gt;Strangely enough if you don’t specify a port, it will fail to assign a random one:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ERROR: failed to create cluster: failed to get random host port for port mapping: listen tcp 192.168.99.101:0: bind: can&apos;t assign requested address
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the config in place I was able to get kind booted with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind create cluster --config kind.yaml&lt;/code&gt; and finally run a successful &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl apply&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap up&lt;/h2&gt;

&lt;p&gt;It’s not perfect. A few clear cons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’m missing the fancy new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker compose&lt;/code&gt; subcommand.&lt;/li&gt;
  &lt;li&gt;I have to keep VirtualBox installed functional. This can be a pain especially since last I checked it conflicts with the &lt;a href=&quot;https://software.intel.com/content/www/us/en/develop/articles/intel-power-gadget.html&quot;&gt;Intel Power Gadget&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;I can’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; which may cause link confusion between me and co-workers.&lt;/li&gt;
  &lt;li&gt;The docker-machine VM needed a some tweaks for some software packages (elasticsearch, kind). I suspect there’ll be more as time goes on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it should do the job at least.&lt;/p&gt;

&lt;p&gt;If my company ends up buying Docker Desktop licenses tomorrow, I’ll surely go back. But I’m glad I have this post handy to refer back to should I need it.&lt;/p&gt;

&lt;p&gt;I’ll post updates as I find out more. Thanks for following along with me!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2021/09/02&lt;/strong&gt;: Added section on kind &amp;amp; kubernetes&lt;/p&gt;
</content>
</entry>

<entry>
  <title>A Startup Weekend retrospective</title>
  <link href="https://matschaffer.com/2015/10/20/startup-weekend.html"/>
  <updated>2015-10-20T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2015/10/20/startup-weekend</id>
  <content type="html">&lt;p&gt;The &lt;a href=&quot;https://nagoya.startupweekend.org/&quot;&gt;Nagoya Startup Weekend&lt;/a&gt; this year was exhausting yet rewarding.&lt;/p&gt;

&lt;p&gt;To cap off the effort, I thought I’d write up a quick retro.&lt;/p&gt;

&lt;h3 id=&quot;things-that-worked&quot;&gt;Things that worked&lt;/h3&gt;

&lt;p&gt;First, hats off to the organizers. Fantastic event, very well organized.&lt;/p&gt;

&lt;p&gt;If you’re organizing a similar event, I’d recommend paying similar attention the the details that ours did so well, namely:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;snacks, plenty of them&lt;/li&gt;
  &lt;li&gt;music, easy to listen to and a wide selection&lt;/li&gt;
  &lt;li&gt;breaks, for facilitator talks and games&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The talk breaks were especially good for helping everyone keep synced and giving some perspective about where we were in the weekend.&lt;/p&gt;

&lt;p&gt;They also provided good advice about how to approach the weekend in general.&lt;/p&gt;

&lt;p&gt;One that really stuck with me is that “this is not a hackathon” - which I think the organizers of &lt;a href=&quot;https://medium.com/@rboyd/our-team-won-startup-weekend-and-all-we-got-was-a-shitty-new-boss-35f1d1f1f267#.3guwaabc8&quot;&gt;Bobby Boyd’s hackathon&lt;/a&gt; probably missed.&lt;/p&gt;

&lt;p&gt;Teams seemed to take this to heart and had a great showing without working code. Instead they leaned on deeper market analysis, clickable wireframes and more details around potential business and revenue models.&lt;/p&gt;

&lt;p&gt;Another thing that worked really well were my friends and family. I got a lot of response and participation from friends and family via facebook. I can’t thank everyone enough for that.&lt;/p&gt;

&lt;h3 id=&quot;things-that-didnt-work-so-well&quot;&gt;Things that didn’t work so well&lt;/h3&gt;

&lt;p&gt;First here, following in that “not a hackathon” vein: I don’t think I worked very well.&lt;/p&gt;

&lt;p&gt;Granted, I got a lot done. I focused mostly on coding and we had a fully functioning prototype by day 2. This was great fun for me but I think this was actually a detriment to the team.&lt;/p&gt;

&lt;p&gt;While focusing on the prototype I couldn’t offer much input on strategy. I think also having such a complete prototype may have anchored the team at a time when changes were needed.&lt;/p&gt;

&lt;p&gt;The next thing I could see changing was the coach involvement. We spent some time with the coaches in organized sessions, but it felt like it came a bit late.&lt;/p&gt;

&lt;p&gt;I would have loved to get some of their feedback earlier, perhaps even continually in the form of challenging interjected questions during early planning and discussion.&lt;/p&gt;

&lt;h3 id=&quot;wrap-up&quot;&gt;Wrap up&lt;/h3&gt;

&lt;p&gt;It was a great weekend over all. I met a ton of people, learned some new techniques and learned a lot about my own interests in developing products.&lt;/p&gt;

&lt;p&gt;The discovery I was especially proud of is that I like coding &lt;em&gt;way&lt;/em&gt; better than market analysis &amp;amp; experimentation. While it’s probably no surprise to those who know me, I think knowing this empirically will really shift my approach to startup ideas going forward.&lt;/p&gt;

&lt;p&gt;If you’re planning on attending one yourself, I’d recommend coming with an idea of your own, plenty of business cards and a grab bag of tactics for gathering early customer feedback.&lt;/p&gt;

&lt;p&gt;I suspect these will serve you and your team better than the freshly updated set of rails gems I showed up with.&lt;/p&gt;

&lt;p&gt;If you have any thoughts or experiences from your own start up weekend, or questions about mine please leave a comment!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>First look at Otto and Nomad</title>
  <link href="https://matschaffer.com/2015/09/29/otto-nomad-first-look.html"/>
  <updated>2015-09-29T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2015/09/29/otto-nomad-first-look</id>
  <content type="html">&lt;h2 id=&quot;the-big-one-otto&quot;&gt;The big one, Otto&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://ottoproject.io/&quot;&gt;Otto&lt;/a&gt; is a tool that wraps up Vagrant, Packer, Terraform and Consul and provides clean layer of abstraction on top.&lt;/p&gt;

&lt;p&gt;This provides a seamless way to transition from your local environment, into a dev VM on VirtualBox, then on to an instance on AWS. The same Appfile is used on both environments then fed through either Vagrant locally or Packer &amp;amp; Terraform on AWS.&lt;/p&gt;

&lt;p&gt;In addition Hashicorp has built in &lt;em&gt;infrastructure&lt;/em&gt; and &lt;em&gt;foundation&lt;/em&gt; concepts that configure VPC and supporting services (right now just Consul) automatically as well.&lt;/p&gt;

&lt;p&gt;Finally it comes with a nice app detection suite that’s capable of building a default Appfile for a good range of application frameworks.&lt;/p&gt;

&lt;h3 id=&quot;a-few-surprises&quot;&gt;A few surprises&lt;/h3&gt;

&lt;p&gt;The getting started guide was really straight forward but I did hit a few surprises that I’m sure will get worked out over time:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;The VM that got selected in the tutorial was Ubuntu 12.04. It worked fine but I was pretty surprised to see such an old release used here.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Otto doesn’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt; for AWS credentials. Instead it prompts you for credentials and a password to encrypt them. Not a deal breaker, but was a bit curious given how common the AWS credentials file is across tools these days.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Packer chooses a c3.large to build the AMI by default. The build should be less than an hour so it won’t cost much but it won’t qualify for the free tier if you’re trying to keep your AWS bill at zero.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Otto, quite frankly, looks &lt;strong&gt;awesome&lt;/strong&gt;. I can’t wait to see where it goes from here.&lt;/p&gt;

&lt;p&gt;I’ve already got the code checked out and am exploring what it’d take to build some sort of monitoring foundation like Influx or Netflix’s Atlas.&lt;/p&gt;

&lt;p&gt;I’d also love to see some evolution in deployment strategies, specifically in the direction of rolling ASGs similar to how Netflix’s Asgard tool does it.&lt;/p&gt;

&lt;p&gt;To wrap it up I did a quick screencast of the high-level steps that the getting started guide goes through.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-16by9 text-center&quot; style=&quot;margin: 20px;&quot;&gt;
&lt;iframe width=&quot;420&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/WHt4xhX7XJc&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;also-nomad&quot;&gt;Also, Nomad&lt;/h2&gt;

&lt;p&gt;I also took a peek at &lt;a href=&quot;https://www.nomadproject.io/&quot;&gt;Nomad&lt;/a&gt; which was the other HashiCorp release yesterday.&lt;/p&gt;

&lt;p&gt;This is a scheduler project that aims to make running containers across a fleet of docker hosts straight forward.&lt;/p&gt;

&lt;p&gt;While Otto seemed to be a very new piece of software, Nomad felt similar to other container management tools such as Kubernetes or CoreOS Fleet.&lt;/p&gt;

&lt;p&gt;The getting started guide again was fairly solid though I did run into a few cases where I had no contianers running and couldn’t figure out why Nomad wasn’t scheduling them.&lt;/p&gt;

&lt;p&gt;Nomad appears to still be a bit more beta than Otto, but there are hints that Nomad will provide a platform for Otto in the future.&lt;/p&gt;

&lt;p&gt;This would be a welcome addition since it should offer a way to deploy containzerized apps much more quickly than the current AMI-baking default.&lt;/p&gt;

&lt;p&gt;Congrats to the folks at HashiCorp for a very big and well-executed release day!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>What should I monitor?</title>
  <link href="https://matschaffer.com/2015/01/14/what-to-monitor.html"/>
  <updated>2015-01-14T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2015/01/14/what-to-monitor</id>
  <content type="html">&lt;p&gt;It’s an old question.
And one that can vary quite a bit from domain to domain.&lt;/p&gt;

&lt;p&gt;But for Web Operations
there are some definite patterns I’ve come across.
Most of these come from the last two years I spent working as a Reliability Engineer at Netflix.
Even though we had hundreds of discrete applications
when something broke you often found yourself looking at similar sets of metrics.&lt;/p&gt;

&lt;p&gt;So without further ado, here they are,
broken down per category and in a roughly-prioritized order.&lt;/p&gt;

&lt;h1 id=&quot;the-basics&quot;&gt;The basics&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Successful requests per second&lt;/li&gt;
  &lt;li&gt;Failed requests per second&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Successful requests in the HTTP world take the form of something in the 100s-300s. Failed HTTP requests would usually be anything 400 and above.
Most services have some way of designating “yes it worked” vs “no it didn’t”.
Count those.&lt;/p&gt;

&lt;p&gt;Ideally count them from both the client and server side.
For middle-tier services the client-side counting can often go in a common client library,
edge services may be able to count at the load balancer.
If counting at the client isn’t feasible
you can count at the server side only,
but in that case you run the risk of missing unseen failures
due to networking trouble.&lt;/p&gt;

&lt;p&gt;You can then make a ratio out of these &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(failures / successes) * 100&lt;/code&gt;
and start tracking your availability against an &lt;a href=&quot;http://www.site-reliability-engineering.info/&quot;&gt;error budget&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If possible, break these down on a per-resource basis (in the REST-resource sense).
Not all monitoring tools can provide this sort of tagging
but being able to say which resource is very valuable when trying to deduce what’s wrong.
This will often take the form of the first part of the HTTP path
or the controller object that’s servicing the request.&lt;/p&gt;

&lt;h1 id=&quot;deeper-errors&quot;&gt;Deeper errors&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Errors when calling back end services&lt;/li&gt;
  &lt;li&gt;Unhandled exceptions&lt;/li&gt;
  &lt;li&gt;Known application-specific error conditions (e.g., locked account)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not all error conditions manifest as a failed request.
Typically a service has circuit breakers, fallbacks,
or other mechanisms to deal with internal failures
without causing a user-visible error.
But you’ll want to count them even if the user doesn’t see them.&lt;/p&gt;

&lt;p&gt;Watching for changes in deeper error trends can tell you
when something is about to go wrong
or when something is going wrong in a way that you can’t see from your basic metrics.&lt;/p&gt;

&lt;p&gt;For example, you could have a failed remote call which causes key data to be omitted from the returned data.
This would still be a 200 but the client may not be able to use the response.&lt;/p&gt;

&lt;p&gt;Count these on a per-second basis with some low-cardinality break down.
Many systems have a set of internal error codes
which will likely be reusable as a way to break down deeper error counts.
Or in the case of circuit breakers,
the name or class of the circuit is typically a good way to segment these errors.&lt;/p&gt;

&lt;h1 id=&quot;tracking-performance&quot;&gt;Tracking performance&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Latency distributions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you may have a system which is always returning 200s,
but if it goes from 150ms response times to 4000ms response times,
you have a problem.&lt;/p&gt;

&lt;p&gt;In addition to a simple max, min and average per second,
percentile distributions are common here.
You can start by looking at the 50th, 95th and 99.5th percentile
which many tools (e.g., statsd) support.
Bucketing by time
(e.g., &amp;lt; 10ms, 100-500ms, &amp;gt; 500ms)
is also useful since bucketed counters can be averaged across many servers and still retain some accuracy.
Averaging the 95th percentile across a hundred machines is still useful,
but doesn’t tell you your actual 95th percentile for the whole fleet.
Unfortunately this bucketed approach is a bit less common
probably since it requires you to have a sense of how your service will perform ahead of time.&lt;/p&gt;

&lt;p&gt;Much like the success counters,
you’ll want to break these down by resource if possible.
Knowing where the latency is coming from can point you in the right direction a lot faster.
If all resources are effected equally,
investigate the system showing the latency.
If it’s a single resource,
investigate the systems used to build that resource’s response data.&lt;/p&gt;

&lt;h1 id=&quot;os--runtime-level-metrics---the-use-method&quot;&gt;OS &amp;amp; Runtime level metrics - the USE method&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;CPU&lt;/li&gt;
  &lt;li&gt;Memory &amp;amp; Garbage Collection&lt;/li&gt;
  &lt;li&gt;Disk&lt;/li&gt;
  &lt;li&gt;Network&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, these are important to keep an eye on as well.
But how their used will vary quite a bit between services.&lt;/p&gt;

&lt;p&gt;On the plus side there are many good tools and resources for monitoring this stuff
since most any environment will be capable of providing metrics on it.&lt;/p&gt;

&lt;p&gt;Rather that get into details here,
I’ll refer you to &lt;a href=&quot;http://www.brendangregg.com/usemethod.html&quot;&gt;the USE&lt;/a&gt; method from Brendan Gregg
which is a really solid place to start when examining OS level resources.&lt;/p&gt;

&lt;p&gt;In some respects
the metrics discussed above
are Brendan’s USE method re-applied to
distributed systems.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Utilization - how many requests are coming in?&lt;/li&gt;
  &lt;li&gt;Saturation - are the requests backing up and becoming latent?&lt;/li&gt;
  &lt;li&gt;Errors - what errors were encountered servicing requests?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you should find yourself in an unknown domain wondering what to monitor,
try the USE method there too.&lt;/p&gt;

&lt;h1 id=&quot;next-steps---tooling&quot;&gt;Next steps - tooling&lt;/h1&gt;

&lt;p&gt;There are a number of software packages that can help you track the sort of metrics covered here
so I won’t try to cover them in detail.
But if you’re really just getting started here are a few options I’ve had experience with:&lt;/p&gt;

&lt;h2 id=&quot;statsd---graphite&quot;&gt;&lt;a href=&quot;https://github.com/etsy/statsd/&quot;&gt;Statsd&lt;/a&gt; -&amp;gt; &lt;a href=&quot;https://graphite.wikidot.com/&quot;&gt;Graphite&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;I did a &lt;a href=&quot;https://vimeo.com/ondemand/monitoringwithgraphite/84747550&quot;&gt;screencast&lt;/a&gt; on this approach almost a year ago.
It’s a fully open-source approach that’s quite common and has a large ecosystem.&lt;/p&gt;

&lt;h2 id=&quot;zabbix&quot;&gt;&lt;a href=&quot;https://www.zabbix.com/&quot;&gt;Zabbix&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;The Stellar setup is mainly using this right now.
It’s also open source and has a rich feature set,
but the surrouding ecosystem a a bit limited.&lt;/p&gt;

&lt;h2 id=&quot;mrtg&quot;&gt;&lt;a href=&quot;https://oss.oetiker.ch/mrtg&quot;&gt;MRTG&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;I’m not as familiar with MRTG,
but it’s been around a very long time
and is still cited as one of the easier monitoring setups to get started with.&lt;/p&gt;

&lt;h2 id=&quot;atlas-from-netflix&quot;&gt;&lt;a href=&quot;https://github.com/Netflix/atlas&quot;&gt;Atlas from Netflix&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;The system I’m most familiar with.
I’ve seen it cover all of the above items while working at Netflix,
but the open source offering is still new and somewhat raw.
I look forward to more experimentation and blogging around Atlas in the near future.&lt;/p&gt;

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

&lt;p&gt;As stated above,
metrics and monitoring can be a very application-specific thing
so consider everything in the context of your application and infrastructure.
My hope is that for those of you still defining your operational metrics,
this will provide a good basis for growing your own distributed system.&lt;/p&gt;

&lt;p&gt;If you have any comments or examples from your own system,
I’d love to hear them.
Feel free to leave a comment below
or come find &lt;a href=&quot;irc://irc.freenode.net/#stellar-dev&quot;&gt;matschaffer in #stellar-dev on freenode&lt;/a&gt;.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>It&apos;s war out there. Get yourself a sub.</title>
  <link href="https://matschaffer.com/2014/12/28/sub-command.html"/>
  <updated>2014-12-28T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2014/12/28/sub-command</id>
  <content type="html">&lt;p&gt;When I started at Netflix almost two years ago, one of the first things that occured to me was “We could really use a general helper command.” Other developers even agreed that it could be handy. I recalled Nick Quaranto’s handy &lt;a href=&quot;https://github.com/basecamp/sub&quot;&gt;sub&lt;/a&gt; repository but with Netflix being a Java shop, I wasn’t sure about introducing something that required a bash environment.&lt;/p&gt;

&lt;p&gt;A few months passed while I tinkered with doing something in Groovy. Then during one of our &lt;a href=&quot;https://techblog.netflix.com/2014/02/netflix-hack-day.html&quot;&gt;hack days&lt;/a&gt; I gave in and decided to use sub to make an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nf&lt;/code&gt; command. With only a few commands in place, it quickly became part of my daily workflow. When I started at the &lt;a href=&quot;https://stellar.org&quot;&gt;Stellar Development Foundation&lt;/a&gt; I decided to start a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; command on day one. Even with just two commands I already use it daily.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;So what’s this “sub” thing about?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/basecamp/sub&quot;&gt;Sub&lt;/a&gt; is a project from &lt;a href=&quot;http://quaran.to/&quot;&gt;Nick Quaranto&lt;/a&gt; that lets you build collections of scripts and access them via git-like subcommands. If you use &lt;a href=&quot;https://github.com/sstephenson/rbenv&quot;&gt;rbenv&lt;/a&gt; you’re actually already using sub. The really cool part is that you can also make your own.&lt;/p&gt;

&lt;p&gt;Need a helper to start up an ssh tunnel? It’d look like this:&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;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Usage: mysub tunnel&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Summary: Sets up an ssh tunnel&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Help: Requires ssh and access to jumpbox, etc, etc...&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt;

ssh &lt;span class=&quot;nt&quot;&gt;-NL&lt;/span&gt; 8888:somehost:80 me@jumpbox&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You drop this script in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysub/libexec/mysub-tunnel&lt;/code&gt; and sub provides access to it via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysub tunnel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Just providing access to the script via your custom command is handy. But sub takes it a step further with tab completion (for both bash and zsh), command listing, and documentation access for free. Sub parses everything it needs from the comments at the top of your script.&lt;/p&gt;

&lt;p&gt;While I could go into more detail about all of sub’s capabilities, the &lt;a href=&quot;https://github.com/basecamp/sub/blob/master/README.md&quot;&gt;readme&lt;/a&gt; is pretty solid so go check it out. If you have any sort of repetitive commands you use, give it a try. Commit it to your company repo and your coworkers will thank you for it.&lt;/p&gt;

&lt;p&gt;As a final note, don’t let the age of the last commit cause hesitation. I’d of course love to see more activity on the project, but it’s a simple enough concept and I haven’t run into any problems using it as a daily tool.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Rethinking My Kanban Board</title>
  <link href="https://matschaffer.com/2014/06/16/rethinking-kanban.html"/>
  <updated>2014-06-16T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2014/06/16/rethinking-kanban</id>
  <content type="html">&lt;p&gt;If you’ve ever used Trello, Jira, or other kanban board tools you may be used to seeing a board that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/kanban3column.png&quot; width=&quot;799&quot; height=&quot;523&quot; alt=&quot;A 3 column kanban board&quot; class=&quot;img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The ticket names have been changed to protect the innocent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here we have a typical 3-column layout; To Do, Doing, and Done. Our three available “dimensions” represent the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Vertical: &lt;strong&gt;Priority&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Horizontal: &lt;strong&gt;Status&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Color: &lt;strong&gt;Logical track of work&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The use of color may vary a bit depending on project, but there are number of systems that work this way and I’ve grown really used to it. Though it can have some problems, namely:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There are only a few colors to work with, so you can run out on complex projects&lt;/li&gt;
  &lt;li&gt;There’s only one “top” of the list, so if you have many people working the list may thrash a bit&lt;/li&gt;
  &lt;li&gt;If groups are fairly separate, people may need to skip down the list to find the next ticket they should work on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course these cases are somewhat rare and manageable so I happily kept working in this model. Jira also makes this easier since I can build filters to just see the parts of the board I was actively working on.&lt;/p&gt;

&lt;p&gt;But when I showed up at the Trello board for a project I joined recently, I saw this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/kanbanmulticolumn.png&quot; width=&quot;969&quot; height=&quot;322&quot; alt=&quot;A multi-column kanban board&quot; class=&quot;img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oh, man.&lt;/p&gt;

&lt;p&gt;How would I know what the status of a ticket was? How do I separate my backlog from my completed work?&lt;/p&gt;

&lt;p&gt;Here the horizontal dimension typically used for status has been replaced with the track of work breakdown. My coworker said he laid it out this way because he’s how he thought about the project. I considered “pivoting” it back to what I was used to, but the project already had more tracks of work than Trello had available colors.&lt;/p&gt;

&lt;p&gt;Then it dawned on me: the only status I was really concerned about was “Doing”. This was easily represented by a color. And with all those other colors available I decided to use two to represent my typical use of story points: 0 for trivial, 2 for easy, 4 for hard.&lt;/p&gt;

&lt;p&gt;Now the board looked like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/kanbanmulticolumncolored.png&quot; width=&quot;873&quot; height=&quot;479&quot; alt=&quot;A multi-column kanban board with colors&quot; class=&quot;img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now our dimensions look like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Vertical: &lt;strong&gt;Priority&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Horizontal: &lt;strong&gt;Logical track of work&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Color: &lt;strong&gt;Status &amp;amp; Difficulty&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that we’ve added a little extra information and now have the option to introduce more than 6 tracks of work.&lt;/p&gt;

&lt;p&gt;We also added an “Ideas” column on the left for higher level ideas that still needs to get split out into the different tracks of work. And a “Done” column far off to the right to get the completed tickets out of the way.&lt;/p&gt;

&lt;p&gt;We’re still iterating on this a bit, like ya do. But I’m pretty happy with how it turned out and really glad I didn’t just try to force my preconceptions onto the existing board.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Thoughts on Scalability</title>
  <link href="https://matschaffer.com/2013/09/30/scalability.html"/>
  <updated>2013-09-30T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2013/09/30/scalability</id>
  <content type="html">&lt;p&gt;I spent the last two years doing deployment automation work and helping startups build Rails applications at Mashion. It was great fun but rarely put me in the path of high-scale customer traffic. Getting close to that traffic was a big factor in my decision to move west and help run one of the largest installations on the internet.&lt;/p&gt;

&lt;p&gt;Since I joined Netflix last April there have been a number of interesting things I’ve learned about how we operate at high scale. Some of these seem obvious but were never quite at the forefront when working on 3-tier startup applications.&lt;/p&gt;

&lt;h3 id=&quot;backward-compatibility-on-all-fronts&quot;&gt;Backward compatibility on all fronts&lt;/h3&gt;

&lt;p&gt;In a start up, you can take usually the app off-line for a few minutes overnight. No-one will notice or be upset. When you’re working on a full-scale product the traffic never stops so you have to rethink how you do your deployments.&lt;/p&gt;

&lt;p&gt;You will usually have two (or more) versions of your application code running at any time. You will usually have application code running against older client code or database schema. Most changes have to be done gradually. It pains me to think how difficult this would be with a typical Rails application. The facility to run and test against multiple schemas just isn’t there.&lt;/p&gt;

&lt;p&gt;I hope rich-client applications push the needle on this one since you need to address the possibility of an unrefreshed browser session hanging out there. Granted, most of the rich client frameworks don’t ship with an answer to this yet. But I suspect it’s only a matter of time until they do.&lt;/p&gt;

&lt;h3 id=&quot;runtime-controls&quot;&gt;Runtime controls&lt;/h3&gt;

&lt;p&gt;In an average Rails app, if something isn’t right with a new feature, you redeploy. When you have a few hundred or thousand machines a full redeploy can take awhile. So instead you need a way to control your app at runtime. Usually this takes the form of feature toggles.&lt;/p&gt;

&lt;p&gt;At Netflix we use &lt;a href=&quot;https://github.com/Netflix/archaius&quot;&gt;archaius&lt;/a&gt; for this but there are many projects that can help provide this functionality. But it’s critical that you have a quick way to turn off broken features or slowly roll out new features.&lt;/p&gt;

&lt;h3 id=&quot;app-level-focus&quot;&gt;App-level focus&lt;/h3&gt;

&lt;p&gt;When I ran servers in the past I would often obsess about the operating system, the file system structure, which HTTP server or daemon tool we used. Now that I work with a few hundred machines at a time, most of that has fallen away. I find myself mentally equating servers and processes.&lt;/p&gt;

&lt;p&gt;There are a lot of processes running on each machine but the main one I care about is the one handling customer requests.&lt;/p&gt;

&lt;p&gt;It has taken me some time to learn to relax and love my base image. But I’m happy that I have. It may not be exactly how I’d do it but at our scale that really doesn’t matter.&lt;/p&gt;

&lt;h3 id=&quot;auto-scale-everything&quot;&gt;Auto-scale everything&lt;/h3&gt;

&lt;p&gt;I’ve been using AWS for a long time. But this usually meant creating one-off EC2 instances and provisioning them over SSH. Each one then stayed online as long as it could as I upgraded, re-deployed and otherwise tweaked the server.&lt;/p&gt;

&lt;p&gt;This is data center mentality.&lt;/p&gt;

&lt;p&gt;To really leverage the cloud you need to be in an auto-scaling group. This will ensure that servers are easily replaced as they come and go (and they will). It also forces you make your application resilient against failures. Failures can happen anywhere but only the cloud approach offers a way to fully recover from failure automatically.&lt;/p&gt;

&lt;p&gt;These points are really just a sample of the things I’ve been learning recently. Hopefully they provide some food for thought. The &lt;a href=&quot;https://netflix.github.io/&quot;&gt;Netflix stack&lt;/a&gt; certainly has a number of components that can help with these items but there’s plenty of room for improvement. Especially when it comes to bridging the gap between high scalability and rapid application development.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Rebooting</title>
  <link href="https://matschaffer.com/2013/09/08/rebooting.html"/>
  <updated>2013-09-08T00:00:00+00:00</updated>
  <id>https://matschaffer.com/2013/09/08/rebooting</id>
  <content type="html">&lt;p&gt;So what happened last year?&lt;/p&gt;

&lt;h2 id=&quot;i-taught-rails-at-upenn&quot;&gt;I taught Rails at UPenn&lt;/h2&gt;

&lt;p&gt;I taught a full year of CIS196 at UPenn. Being an adjunct teacher was &lt;em&gt;great&lt;/em&gt; and highly recommended to anyone interested. The skills I learned on presenting, course organization and mentoring keep coming in handy again and again.&lt;/p&gt;

&lt;h2 id=&quot;i-took-a-full-time-job-at-netflix&quot;&gt;I took a full-time job at Netflix&lt;/h2&gt;

&lt;p&gt;This was a tough move but consulting just wasn’t teaching me much about technology. I learned tons about business operations and project management. I’m sure I’ll make good use of all this at some point but for now I really wanted to double down on my tech education. There were definitely a lot of takeaways from the business side, but I’ll save those for later posts.&lt;/p&gt;

&lt;h2 id=&quot;i-moved-to-san-jose&quot;&gt;I moved to San Jose&lt;/h2&gt;

&lt;p&gt;This was a prerequisite for the new job. But honestly it’s like Hollywood for programmers out here and the weather is jaw-droppingly amazing.&lt;/p&gt;

&lt;p&gt;Also working at Netflix has been perfect. I can’t think of a better place to learn about high-scale software development.&lt;/p&gt;

&lt;h2 id=&quot;i-rebooted-this-blog&quot;&gt;I rebooted this blog&lt;/h2&gt;

&lt;p&gt;The above points got me wanting to write again. One thing that I think we lost sight of at Mashion is how important writing is to a personal brand. The traffic on posts themselves is great but the writing process also helped me solidify a number of ideas both technical and non.&lt;/p&gt;

&lt;p&gt;So here goes. I’m going to try to write a bit more regularly on some of my findings now that I’m back in the high-scale world in the heart of Silicon Valley. Wish me luck!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Mashion&apos;s Pairing Rig</title>
  <link href="https://matschaffer.com/tech/2012/07/20/mashion-pairing-rig.html"/>
  <updated>2012-07-20T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2012/07/20/mashion-pairing-rig</id>
  <content type="html">&lt;p&gt;Here at Mashion, we pair program a lot. We’ve been iterating through a handful of pairing setups and have finally settled on what I’m happy to call a masterpiece. But first, a little bit about how we got there.&lt;/p&gt;

&lt;p&gt;We decided that having a dedicated pairing machine wasn’t practical, partly for price but also because we’d have to maintain another machine and the data on it. Instead, we opted to use our own laptops to power all of our pairing rig iterations. This also makes for a smooth transition from pairing to soloing since you’re able to plug and unplug and the only thing that changes is your screen size.&lt;/p&gt;

&lt;p&gt;Originally, we paired side-by-side using a shared single monitor. This worked reasonably well. It’s simple to get started and offers a balanced environment between the two partners. But being off to the side of the monitor is bad ergonomics and having a face-to-face conversation doesn’t work very well. Noticing that your partner is trying to type is also harder and you bump chairs a lot more often than you’d think because both partners tend to lean toward the shared display.&lt;/p&gt;

&lt;p&gt;Then we switched to face-to-face pairing on a single desk. This worked better, but since we still only used one display, one coder would be on their laptop. This mismatch was awkward and again not great ergonomics. In exchange, we got good face-to-face discussion, easier non-verbal cues and no chair bumping. Although we did get occasional &lt;a href=&quot;http://www.flickr.com/photos/schapht/7640998898/in/set-72157630737977714&quot;&gt;games of footsie&lt;/a&gt;. If you’re setting up pair programming with limited time or resources, I’d definitely go this route first. The desk here is an &lt;a href=&quot;http://www.ikea.com/us/en/catalog/products/S29806818/#/S19852113&quot;&gt;Ikea Galant&lt;/a&gt;, which I’ve seen at a number of offices in the past couple years.&lt;/p&gt;

&lt;p&gt;At this point we really wanted to address the environment parity problem. We wanted two monitors, but how could we mirror two displays off of one MacBook? Most solutions to this were expensive and bulky. Thankfully &lt;a href=&quot;http://jasongarber.com/&quot;&gt;Jason Garber&lt;/a&gt; joined the team around this time and had a stroke of genius: Use HDMI splitters which were small, cheap and easy to attach to a laptop. This was the key that let us drive two identical environments off of one standard laptop.&lt;/p&gt;

&lt;p&gt;To really put the icing on the cake, we took a cue from Pivotal Labs’ &lt;a href=&quot;http://pivotallabs.com/users/jsusser/blog/articles/1505-pairing-tete-a-tete&quot;&gt;tete-a-tete pairing article&lt;/a&gt; and started working on ways to offset the pair to get better communication. The solution that &lt;a href=&quot;http://gregsterndale.com/&quot;&gt;Greg Sterndale&lt;/a&gt; came up with was also genius. He noticed out that you can use a Galant extension as a single desk. Put two of these together and you have two individual desks that take up a little less space than the single larger Galant desk. Total win! Also &lt;a href=&quot;http://www.flickr.com/photos/schapht/7641008704/in/set-72157630737977714/&quot;&gt;a lot less footsie&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally we finished it off with easily-movable monitor mounts, wireless input devices and some cable clips to make a pairing rig that’s there when you need it and gets out of the way when you don’t.&lt;/p&gt;

&lt;p&gt;This setup is by far our best setup yet. The closer seating makes for easy conversation even when the floor gets busy. Each coder gets &lt;a href=&quot;http://www.flickr.com/photos/schapht/7641013494/in/set-72157630737977714/&quot;&gt;their own space&lt;/a&gt; which is just big enough to get the job done. Having the monitors mounted on the outside also makes them &lt;a href=&quot;http://www.flickr.com/photos/schapht/7641005226/in/set-72157630737977714/&quot;&gt;easy to move aside&lt;/a&gt; when splitting off to do some research or other solo task. And we get some &lt;a href=&quot;http://www.flickr.com/photos/schapht/7641012396/in/set-72157630737977714/&quot;&gt;epic high-five action&lt;/a&gt; when the test suite goes green.&lt;/p&gt;

&lt;p&gt;If you like the idea, check out our &lt;a href=&quot;http://www.amazon.com/gp/registry/wishlist/3QE883URUTEEE/?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;linkCode=ur2&amp;amp;tag=mall0a-20&quot;&gt;Amazon Wishlist&lt;/a&gt; to get all the pieces. If you make one, be sure to &lt;a href=&quot;http://twitter.com/mashionllc&quot;&gt;send us a note&lt;/a&gt; and tell us about your thoughts or improvements!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.assoc-amazon.com/e/ir?t=mall0a-20&amp;amp;l=ur2&amp;amp;o=1&quot; width=&quot;1&quot; height=&quot;1&quot; border=&quot;0&quot; alt=&quot;&quot; style=&quot;border:none !important; margin:0px !important;&quot; /&gt;&lt;/p&gt;
</content>
</entry>

<entry>
  <title>A paragraph on sexism in computer science</title>
  <link href="https://matschaffer.com/tech/2012/04/24/sexism-in-computer-science.html"/>
  <updated>2012-04-24T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2012/04/24/sexism-in-computer-science</id>
  <content type="html">&lt;p&gt;&lt;a href=&quot;http://trevmex.com/post/21644536045/a-paragraph-on-sexism-in-computer-science&quot;&gt;Trevor&lt;/a&gt; encouraged me to write a paragraph on sexism in computer science and how we can change it. I encourage you to do the same!&lt;/p&gt;

&lt;p&gt;Here’s mine:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;http://angelaharms.com/&quot;&gt;Angela Harms&lt;/a&gt; had a great quote on her recent
appearance on the &lt;a href=&quot;http://rubyrogues.com/049-rr-agile-communication-with-angela-harms/&quot;&gt;Ruby
Rogues&lt;/a&gt;
podcast. It’s been ringing in my head all month: Stay present, stay
vulnerable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;I think it applies in a lot of situations and sexism in the work place is one
of them. The news I’ve seen on the topic lately tends to be a situation that
could have been avoided if people weren’t trying to be cool or save face.
We already build the most complex things that humans make. No need to act
cool or demean your co-workers. We’re all smart here. Stay present, stay
vulnerable.&lt;/p&gt;
&lt;/blockquote&gt;

</content>
</entry>

<entry>
  <title>Run CI locally on Jenkins</title>
  <link href="https://matschaffer.com/tech/2012/04/06/local-jenkins-server.html"/>
  <updated>2012-04-06T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2012/04/06/local-jenkins-server</id>
  <content type="html">&lt;p&gt;At &lt;a href=&quot;http://mashion.net&quot;&gt;Mashion&lt;/a&gt; our usual pairing setup means we have one laptop that’s not doing much work. It’s handy to have for email, or breaking off to google something but it’s idle most of the time.&lt;/p&gt;

&lt;p&gt;When looking for a CI solution on a recent project, it made sense to me to use that second machine rather than spend money on a VPS. So I set out to make a simple local Jenkins server. Here’s what I came up with.&lt;/p&gt;

&lt;p&gt;First install the jenkins.rb gem and start the server:&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;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;jenkins jenkins-war
jenkins server &lt;span class=&quot;nt&quot;&gt;--daemon&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Once the server is up and running (check &lt;a href=&quot;http://localhost:3001&quot;&gt;localhost:3001&lt;/a&gt;) use the Java Jenkins CLI to install the git plugin or whatever plugins you need for your setup.&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;nv&quot;&gt;CLI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.jenkins/server/war/WEB-INF/jenkins-cli.jar
java &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$CLI&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; http://localhost:3001 install-plugin git
java &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$CLI&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; http://localhost:3001 safe-restart&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Once Jenkins restarts you’ll probably want to set up outbound email. I did this using my gmail account and &lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/GMail&quot;&gt;these instructions&lt;/a&gt; on the Jenkins wiki. Do this on the &lt;a href=&quot;http://localhost:3001/configure&quot;&gt;Jenkins config page&lt;/a&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;jenkins configure &lt;span class=&quot;nt&quot;&gt;--host&lt;/span&gt; localhost &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt; 3001
jenkins create path/to/project &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; none&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here I opted not to use a project template. The ones that come with jenkins.rb don’t poll Git or email on failures. I also prefer keeping the build steps in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script/ci&lt;/code&gt; in the project’s git repo since it’s easier to update and port across languages and frameworks.&lt;/p&gt;

&lt;p&gt;So head to &lt;a href=&quot;http://localhost:3001&quot;&gt;your Jenkins server&lt;/a&gt; and configure your job to poll git every 5 minutes with this cron expression:&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;k&quot;&gt;*&lt;/span&gt;/5 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt; build step with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script/ci&lt;/code&gt;. And add an E-mail Notification to yourself or whoever should know about failures (space separated). Then save your job config.&lt;/p&gt;

&lt;p&gt;You’re all set. Commit some new code or click the “Build Now” link to run a test build. To stop the Jenkins server run:&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;jenkins server &lt;span class=&quot;nt&quot;&gt;-k&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hope that helps some, feel free to leave a comment or &lt;a href=&quot;mailto:mat@schaffer.me&quot;&gt;email me&lt;/a&gt; if you have any questions.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Creating a minimal Ubuntu VirtualBox image</title>
  <link href="https://matschaffer.com/tech/2011/06/29/minimal-ubuntu-virtualbox.html"/>
  <updated>2011-06-29T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2011/06/29/minimal-ubuntu-virtualbox</id>
  <content type="html">&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Update 2015/01/14:&lt;/strong&gt;
This post is from 2011!
If you’re looking for a minimal ubuntu running on VirtualBox
I highly recommend you check out &lt;a href=&quot;https://www.vagrantup.com/&quot;&gt;Vagrant&lt;/a&gt;.
Once you have it installed you can get a minimal Ubuntu box running
with two easy commands&lt;/p&gt;
&lt;/blockquote&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;nv&quot;&gt;$ &lt;/span&gt;vagrant init ubuntu/trusty64
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;vagrant up&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;The instructions below still might work, but have only been tested on 10.04 LTS which is quite old at this point.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;In my last post, I showed you how to build a CloudFoundry mini-cloud on an Ubuntu VirtualBox. Building that base image took some work and sorting through old blog posts, so I thought I’d post the instructions I ended up using.&lt;/p&gt;

&lt;p&gt;First, download a copy of the latest Ubuntu LTS (Long-Term-Support) image. This tutorial follows the 10.04 installation, later versions may vary.&lt;/p&gt;

&lt;p&gt;At the boot screen, press F4 which will let you select the type of installation to perform. Select “Minimal Virtual Machine”, then hit enter to start the Install.&lt;/p&gt;

&lt;p&gt;Keyboard selection won’t matter much, so I just chose USA/USA.&lt;/p&gt;

&lt;p&gt;For the system clock, I recommend UTC. This models most of the cloud provider images you’re likely to come across.&lt;/p&gt;

&lt;p&gt;Have it use the whole disk and auto-partition.&lt;/p&gt;

&lt;p&gt;Use ‘ubuntu’ for the username and password for now. Once the system is set up, you can install your ssh keys and remove the password with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo passwd -d ubuntu&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Disk encryption and HTTP proxies is your choice, but I didn’t need to use either.&lt;/p&gt;

&lt;p&gt;When you get to select packages, do manual package selection and it will put you into the aptitude program. This is a little tricky to navigate, but these commands should be plenty to get the job done:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;use /, enter and n to search similar to less or vi&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; to install the selected package&lt;/li&gt;
  &lt;li&gt;search and install as needed&lt;/li&gt;
  &lt;li&gt;gg to install packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll need these packages in order to get the VirtualBox guest tools setup. I recommend installing these since it will enable you to detect the IP of the VM and shut it down without having to SSH into it:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;build-essential&lt;/li&gt;
  &lt;li&gt;linux-headers-virtual&lt;/li&gt;
  &lt;li&gt;openssh-server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once it reboots, select “Install Guest Additions” from the Devices menu to mount the VirtuaBox guest additions install image. Then log in and run these commands:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo mount /dev/cdrom /mnt
sudo sh /mnt/VBoxLinuxAdditions.run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last important fix is to remove the MAC address cache that this version of Ubuntu has:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo rm /etc/udev/rules.d/70-persistent-net.rules
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that when you import the image using VBoxManage you should also reset it’s MAC address. You can do this using VBoxManage like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage modifyvm MyVMName --macaddress1 auto
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may also want to be able to shutdown the machine without logging in, you’ll also need to install ACPI support:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-get update
sudo apt-get install acpi-support
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will allow you to use the VBoxManage command to shut down the machine cleanly without using SSH:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage controlvm MyVMName acpipowerbutton
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want passwordless sudo access (also common on cloud images) run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo su -c &quot;echo &apos;ubuntu ALL=(ALL) NOPASSWD:ALL&apos; &amp;gt;&amp;gt; /etc/sudoers&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, disconnect the guest additions and shut down the VM. You should now have a super-minimal Ubuntu image that’s confiured similarly to the Ubuntu images you’ll see on cloud services like EC2.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Running CloudFoundry with VirtualBox on OS X</title>
  <link href="https://matschaffer.com/tech/2011/04/15/cloudfoundry-virtualbox.html"/>
  <updated>2011-04-15T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2011/04/15/cloudfoundry-virtualbox</id>
  <content type="html">&lt;p&gt;So you might have seen the &lt;a href=&quot;http://www.trottercashion.com/2011/04/14/automating-the-cloudfoundry-install.html&quot;&gt;“one click” installer&lt;/a&gt; for CloudFoundry that my esteemed colleague Trotter Cashion posted yesterday. This is very cool. If you have an Ubuntu server handy you can run that script and you have a cloud. But I like to keep things local so I started working to get it installed on VirtualBox. And here’s the solution, packaged as a script.&lt;/p&gt;

&lt;p&gt;To use this script you’ll need:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A Mac (Linux or Cygwin would need some tweaks, pull requests welcome!)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.virtualbox.org/wiki/Downloads&quot;&gt;VirtualBox&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then run this:&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;curl &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; https://github.com/matschaffer/vcap/raw/vbox-install/setup/vbox_install
curl &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; https://github.com/matschaffer/vcap/raw/vbox-install/setup/install
sh vbox_install&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Accept the host key (should be 5f:f4:1f:14:4c:b8:5c:ad:11:b1:85:f3:f1:0d:a5:2c) and enter the password “ubuntu” once.&lt;/p&gt;

&lt;p&gt;That’s it! This script will import a minimal Ubuntu VM from our S3 bucket, set up bridged networking to your default interface, install your SSH key and kick off Trotter’s install script.&lt;/p&gt;

&lt;p&gt;It’ll take an hour or so depending on your internet connection. When it’s done, you’ll get some instructions on updating your hosts file, registering with your new cloud and accessing the VirtualBox image. Follow those and enjoy your new personal cloud!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>How to hire a Rails developer</title>
  <link href="https://matschaffer.com/tech/2011/04/12/how-to-hire-rails-developer.html"/>
  <updated>2011-04-12T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2011/04/12/how-to-hire-rails-developer</id>
  <content type="html">&lt;p&gt;As the co-founder of &lt;a href=&quot;http://mashion.net&quot;&gt;Mashion&lt;/a&gt; and organizer of &lt;a href=&quot;http://phillyrb.org&quot;&gt;Philly.rb&lt;/a&gt; I get a lot of emails from people looking for Rails developers. To help spread the word and maybe do a little less typing, I decided it was time for a blog post.&lt;/p&gt;

&lt;p&gt;Before we get into it you should know that finding a good developer in any technology is difficult. Even in larger developer pools like Java or .Net, isolating the top talent is difficult. The pool of Rails developers is of course smaller, but also contains some extremely high quality developers. Here are some of the key points I’ve settled on when helping people find the right developers for their company.&lt;/p&gt;

&lt;h2 id=&quot;1-get-involved&quot;&gt;1. Get involved&lt;/h2&gt;

&lt;p&gt;There are probably some user groups near your area. Maybe even a Ruby user group. Google them, go to a meeting. This is easier when you know a little programming, since the conversation will be mostly technical. If you have even one developer now, send them. If you’re the only person in your company, just show up anyway. Establishing a presence in the community goes a long way. The more often people see you, the more likely your company will come to mind when people think about changing jobs.&lt;/p&gt;

&lt;h2 id=&quot;2-be-a-presenter-or-a-sponsor&quot;&gt;2. Be a Presenter or a Sponsor&lt;/h2&gt;

&lt;p&gt;This is a continuation of the first point. Being at the meeting is a great start. Being at the podium is even better. And it’s a bonus if you’re talking about cool technologies or business ideas. If no-one in your company can present on a technical topic, sponsor a meeting. Most groups accept sponsors for food, space, or giveaways. So you can probably find an option that works even if your budget is pretty tight. In exchange you’ll usually get mentions in the meeting promotions or a chance to talk about your company while everyone’s paying attention. This varies between groups, so contact the organizer for info.&lt;/p&gt;

&lt;h2 id=&quot;3-dont-talk-about-your-degree&quot;&gt;3. Don’t talk about your degree&lt;/h2&gt;

&lt;p&gt;Whether it’s technical or business-related, most of what interests Rails developers doesn’t come from a classroom. People are much more interested in hearing about your experiences in real companies, delivering real products. If you don’t have any experience, that’s okay too. Just be honest about it. Rails developers are very aware that good business ideas can come from anywhere.&lt;/p&gt;

&lt;h2 id=&quot;4-have-a-business-plan&quot;&gt;4. Have a business plan&lt;/h2&gt;

&lt;p&gt;How are you going to make money? Knowing your target market and having a marketing plan is best. Having a good shot at a buy out is passable. Selling advertising is unlikely to get anyone’s attention. If you’re profitable today, pitch that. If you’re not profitable yet, emphasize what your company has to offer in technology or lifestyle interest. Having VC backing is fine, but it’s not a big selling point. Same goes for equity offers. Most of us experienced what happened in the dot-com bubble and are not eager to repeat it.&lt;/p&gt;

&lt;h2 id=&quot;5-dont-hire-a-rails-developer&quot;&gt;5. Don’t hire a “Rails” developer&lt;/h2&gt;

&lt;p&gt;The best developers I know don’t classify themselves as “Rails” developers. They might do most of their business in Rails, but they also do JavaScript, Clojure, Erlang, Objective-C and a multitude of other languages. They have side projects using technologies even I haven’t heard of yet. The web development landscape changes rapidly, so you’ll need people who are constantly challenging themselves to learn more.&lt;/p&gt;

&lt;p&gt;As with any blog post, these are just some ideas that have helped me. If you have some points of your own, please comment. I welcome the feedback.&lt;/p&gt;

&lt;p&gt;Good hunting.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Net::HTTP Mocking during Cucumber Tests</title>
  <link href="https://matschaffer.com/tech/2011/04/03/net-http-mock-cucumber.html"/>
  <updated>2011-04-03T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2011/04/03/net-http-mock-cucumber</id>
  <content type="html">&lt;p&gt;I just finished up an awesome week by speaking at &lt;a href=&quot;http://www.rubynation.org/&quot;&gt;RubyNation&lt;/a&gt;. Hats off to the organizers and the sponsors for throwing a great conference! During &lt;a href=&quot;http://www.slideshare.net/matschaffer/ruby-on-the-phone&quot;&gt;my twilio talk&lt;/a&gt; I mentioned how we had problems running &lt;a href=&quot;https://github.com/wycats/artifice&quot;&gt;Artifice&lt;/a&gt; during our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@javascript&lt;/code&gt; Cucumber features. Here’s how that goes down.&lt;/p&gt;

&lt;p&gt;If you’re trying to use any sort of global Net::HTTP mocking tool during your Cucumber tests, you might encounter something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-gherkin&quot; data-lang=&quot;gherkin&quot;&gt;  &lt;span class=&quot;kn&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Given &lt;/span&gt;I am on the new user registration page
      &lt;span class=&quot;err&quot;&gt;Capybara&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;TimeoutError (Capybara&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;TimeoutError)&lt;/span&gt;
      &lt;span class=&quot;err&quot;&gt;./features/step_definitions/web_steps.rb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;in `/^(?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:|&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;)am&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;(.+)$/&apos;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;features/user_registration.feature:7:in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;`Given&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;am&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;...&apos;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Then &lt;/span&gt;I should see &lt;span class=&quot;s&quot;&gt;&quot;Email&quot;&lt;/span&gt;
      &lt;span class=&quot;err&quot;&gt;Capybara&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;TimeoutError (Capybara&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;TimeoutError)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This happens because Selenium and Capybara use Net::HTTP to know when your Rails app is available. Sadly, you can’t even put the mock activation in a Before block because Capybara won’t start the separate Rails app until the first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@javascript&lt;/code&gt; test. To get around this we set up our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env.rb&lt;/code&gt; like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;artifice&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&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;s1&quot;&gt;&apos;test&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;support&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;fake_twilio&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Artifice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;activate_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FakeTwilio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;capybara/rails&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;capybara/cucumber&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;capybara/session&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Selenium&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Net&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Net&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;HTTP&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Artifice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NET_HTTP&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Capybara::Server&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Net&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Net&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;HTTP&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Artifice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NET_HTTP&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;/figure&gt;

&lt;p&gt;In the case of Artifice, it holds on to the original Net::HTTP as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Artifice::NET_HTTP&lt;/code&gt;. This block of code puts the original Net::HTTP back but only in the namespaces where it’s needed (Selenium and Capybara). Yet another lovely example of how powerful Ruby is. If you have a cleaner way to do this without resorting to two HTTP libraries, I’d love to hear it.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Making your own Ruby gem</title>
  <link href="https://matschaffer.com/tech/2010/12/21/your-own-gem.html"/>
  <updated>2010-12-21T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/12/21/your-own-gem</id>
  <content type="html">&lt;p&gt;It’s easy to make your own Ruby gem. Even without a library to help you. If you have RubyGems installed you’re already good to go.&lt;/p&gt;

&lt;p&gt;First, make a directory for your project. In that directory make gemspec file named after the gem. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_awesome_gem.gemspec&lt;/code&gt;. The gemspec file is just Ruby. Here’s a basic example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;my_awesome_gem&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0.0.1&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;summary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;This bit will show up under your gem on rubygems.org&apos;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;author&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Mat Schaffer&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;mat@schaffer.me&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;homepage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;https://github.com/matschaffer/my_awesome_gem&apos;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# These dependencies are only for people who work on this gem&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_development_dependency&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rspec&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_development_dependency&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;mocha&apos;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Include everything in the lib folder&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;lib/**/*&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Supress the warning about no rubyforge project&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rubyforge_project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;nowarning&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now just put your code in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/my_awesome_gem.rb&lt;/code&gt;. RubyGems will automatically put your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib&lt;/code&gt; directory into the load path when the gem is installed. To test your gem before it’s installed just run your test script using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruby -Ilib my_test_file.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now build your gem with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem build my_awesome_gem.gemspec&lt;/code&gt; and push it to &lt;a href=&quot;http://rubygems.org&quot;&gt;RubyGems.org&lt;/a&gt; by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem push my_awesome_gem-0.0.1.gem&lt;/code&gt;. You’ll need to make a RubyGems.org account if you don’t have one. The gem push command will ask you for your account info the first time you push a gem.&lt;/p&gt;

&lt;p&gt;As a bonus, if you want to use rake to build and push your gem, just make a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rakefile&lt;/code&gt; like this (run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake -T&lt;/code&gt; to see what this gives you):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rake/gempackagetask&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;*.gemspec&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GemPackageTask&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;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Push gem to rubygems.org&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:push&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:gem&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;gem push &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;package_dir&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;gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gem_file&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;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And if you want to manage your development gems with bundler, just put this in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:rubygems&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gemspec&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
</entry>

<entry>
  <title>SEO for Developers: DOs and DON&apos;Ts</title>
  <link href="https://matschaffer.com/tech/2010/11/26/seo-for-developers.html"/>
  <updated>2010-11-26T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/11/26/seo-for-developers</id>
  <content type="html">&lt;p&gt;Normally I’m not one to repost, but I was cleaning my office and came across an old handout from &lt;a href=&quot;http://www.clickequations.com&quot;&gt;Click Equations&lt;/a&gt; and wanted to share it. It’s a good summation of most of what I know about SEO so maybe some others can learn from it too. Hopefully no-one from Click Equations minds, but I’ll be happy make modifications if they do.&lt;/p&gt;

&lt;h2 id=&quot;do&quot;&gt;DO&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Consider the needs of search engines when setting up Web sites and coding pages&lt;/li&gt;
  &lt;li&gt;Validate HTML, CSS, Links and 508standards before publishing content&lt;/li&gt;
  &lt;li&gt;Remember that HTML content can be crawled and indexed - but not JavaScript&lt;/li&gt;
  &lt;li&gt;Use XML Sitemaps to feed to the search engines every URL that you want crawled and indexed&lt;/li&gt;
  &lt;li&gt;Use Robots.txt to block any files or sub-directories that you do not want the search engines to crawl and index&lt;/li&gt;
  &lt;li&gt;Ensure that every image has a keyword-relevant filename, Alt Tag, and text description close to where the image is displayed on the page&lt;/li&gt;
  &lt;li&gt;Use descriptive, keyword-rich URLs for all pages (rather than dynamically-generated alphanumeric URLs)&lt;/li&gt;
  &lt;li&gt;Make sure that you’re most important pages are linked to in the site’s browsable interface (and with keyword-rich anchor text); the more difficult it is to “find” a page, the less likely it is to rank well&lt;/li&gt;
  &lt;li&gt;Render in the HTML as much information (i.e., Meta data) as you have about the media files&lt;/li&gt;
  &lt;li&gt;Include a “Link to this Page” capability as a quick-and-easy means of attracting backlinks from your user community&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;dont&quot;&gt;DON’T&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Place heading tags around hyperlinks or snippets of text used on more than one page&lt;/li&gt;
  &lt;li&gt;Implement URLs that neither a user nor a search engine can understand&lt;/li&gt;
  &lt;li&gt;Include “stop words” in URLs (and, to, in, etc.)&lt;/li&gt;
  &lt;li&gt;Have more than one URL resolve to the same page&lt;/li&gt;
  &lt;li&gt;Let any page return a 404 error; use temporary (302) or permenant (301) redirects depending on whether the original page will [sic]&lt;/li&gt;
  &lt;li&gt;Hide text to users that you want the engines to see; deliver a consistent experience to both&lt;/li&gt;
  &lt;li&gt;Use different sub-domains unless your content covers very diverse topic and/or the hosting platform is too inflexible&lt;/li&gt;
  &lt;li&gt;Use iFrames&lt;/li&gt;
  &lt;li&gt;Use JavaScript or AJAX extensively; while Google processes some JavaScript, the other engines do not&lt;/li&gt;
  &lt;li&gt;Do anything solely because of its SEO benefit (think of your users first)&lt;/li&gt;
&lt;/ol&gt;

</content>
</entry>

<entry>
  <title>Driving Pure Data with Ruby</title>
  <link href="https://matschaffer.com/tech/2010/11/24/ruby-midi-pure-data.html"/>
  <updated>2010-11-24T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/11/24/ruby-midi-pure-data</id>
  <content type="html">&lt;p&gt;So I’ve been playing around with &lt;a href=&quot;http://puredata.info/&quot;&gt;Pure Data&lt;/a&gt; a lot lately. I’m finally getting to a point where I think I “get” it. But one thing Pure Data doesn’t seem to do very well is sequencing, or building melodies and patterns to play back. Unable to find an open MIDI sequencer for OS X, I turned to Ruby… Full. Double. Rainbow.&lt;/p&gt;

&lt;p&gt;First, a little background incase you haven’t played with Pure Data. Pure Data is visual programming language for working with audio signals. A simple sine wave tone at 440Hz would look like this:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center&quot; src=&quot;https://matschaffer.com/images/simple.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To connect that sine wave to a MIDI control you just add a couple boxes:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center&quot; src=&quot;https://matschaffer.com/images/simple-midi.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Of course, you can get a lot more complex. Pure Data has tools for mixing signals, storing data into tables, filtering, manipulating lists of data, etc. There’s also a library called &lt;a href=&quot;http://wiki.dataflow.ws/PdMtlAbstractions&quot;&gt;PdMtlAbstractions&lt;/a&gt; that has a lot of good tools for handling note off events and &lt;a href=&quot;http://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope&quot;&gt;ADSR&lt;/a&gt; more like a real synth. But even with these tools I was unable to build a sequencer that I was excited to use. Enter Ruby.&lt;/p&gt;

&lt;p&gt;Now I’ll probably get this working on MRI soon, but my googling pointed out that there was a &lt;a href=&quot;http://download.oracle.com/javase/6/docs/api/javax/sound/midi/package-summary.html&quot;&gt;MIDI system built into Java&lt;/a&gt;. So at the moment this is JRuby only. And turns out that the Java API to trigger notes wasn’t complicated. Here’s a simple scale:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Java&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;MidiSystem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;javax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;midi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MidiSystem&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ShortMessage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;javax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;midi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ShortMessage&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Find the &quot;Bus 1&quot; receiving device&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MidiSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_midi_device_info&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Bus 1&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MidiSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_midi_device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_max_receivers&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;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;receiver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_receiver&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Make a melody&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;notes&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;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;62&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&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;2&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;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;67&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;67&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;2&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;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Send the notes   &lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;noteon&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ShortMessage&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;n&quot;&gt;noteon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ShortMessage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NOTE_ON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;noteon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The first section of the code is just to find the right output device. In this case I have Pure Data configured to listen to “Bus 1” on my IAC Driver. On a Mac you set this up by opening Audio MIDI Setup and pulling up info on the IAC Driver and adding a port. The IAC Driver is basically a virtual MIDI bus that lets you route MIDI signals between software on your computer. For more info on setting that up, check out &lt;a href=&quot;http://www.youtube.com/watch?v=10SdP_gviHY&quot;&gt;this tutorial&lt;/a&gt;. If you wanted to have this script control a hardware synth, you’d just have to change the name to match what you’re looking for.&lt;/p&gt;

&lt;p&gt;The second section just sends some notes. Astute readers may notice that I’m not sending any note off messages. On a proper instrument or synth you’ll need those too, but this simple synth can only play one note, so I’ve left them off for now.&lt;/p&gt;

&lt;p&gt;And if you’re curious, &lt;a href=&quot;https://matschaffer.com/files/simple-scale.mp3&quot;&gt;here’s what is sounds like&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For my next trick, I’m hoping to build a ruby gem that will help me build songs. I’ve found a few out there, but haven’t been really excited about them yet. Here’s the syntax I have so far.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Melody&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Voice&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;step_down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;play_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;instance_eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:channel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:notes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w(G# B C#5 D5)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&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;nf&quot;&gt;play_with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step_down&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;play_with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&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;nf&quot;&gt;play_with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which sounds &lt;a href=&quot;https://matschaffer.com/files/zelda.mp3&quot;&gt;like this&lt;/a&gt;. Maybe I’ll make something closer to real music with this some day. But for now it’s a lot of fun. Hope you think so too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: The latest version of collavoce has a much nicer syntax for defining voices. Be sure to check out &lt;a href=&quot;https://github.com/matschaffer/collavoce/blob/master/examples/zelda.rb&quot;&gt;this example&lt;/a&gt; for the latest rendition.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Notes From October Philly.rb</title>
  <link href="https://matschaffer.com/tech/2010/10/13/october-phillyrb-notes.html"/>
  <updated>2010-10-13T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/10/13/october-phillyrb-notes</id>
  <content type="html">&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;So usually &lt;a href=&quot;http://trevmex.com/&quot; title=&quot;trevmex&apos;s tumblings&quot;&gt;Trev&lt;/a&gt; does notes, but he was presenting, so I tried to take some instead. Here they are, essentially unabridged so excuse any typos. Feel free to comment if you have errata or questions. If you need a little context or want to rate the talk, check out the [SpeakerRate page](http://spkr8.com/e/626 “philly.rb 10/12/2010&lt;/td&gt;
      &lt;td&gt;SpeakerRate”).&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;p&gt;Why VCR? Mocks are hard :)
So Trev wrote a big ruby lib with no tests, but didn’t have mocks.&lt;/p&gt;

&lt;p&gt;This had lots of HTTP calls. Led to very slow tests. Changing data sets caused lots of breakage.&lt;/p&gt;

&lt;p&gt;Twitter example, took 4 seconds to run one test. Also there are a lot of things that can break the test (deleted accounts, etc).&lt;/p&gt;

&lt;p&gt;Getting started:&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;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;vcr
gem &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;fakeweb|webmock&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# VCR wraps one or the other&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;“Cassettes” that record HTTP stuff&lt;/p&gt;

&lt;p&gt;To use:&lt;/p&gt;

&lt;p&gt;make a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vcr_setup.rb&lt;/code&gt;, that points to your vcr/cassettes (or other folder) and sets your stubbing lib (fakeweb or webmock).&lt;/p&gt;

&lt;p&gt;They are yml, but don’t put them in fixtures in rails 3 or it’ll try to load them like they’re db data.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use_cassette&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mycassettename&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:new_episodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;your_test&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Run it once, and it’s slow. Run it again and it’s fast (case it’ll replay the API responses).&lt;/p&gt;

&lt;p&gt;Can pass in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:erb =&amp;gt; { vars... }&lt;/code&gt; to give a context for ERB substitution in the file.&lt;/p&gt;

&lt;p&gt;Can also use&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert_cassette&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;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# in before&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eject_cassette&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#in after&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then you don’t even need the block in the test code. You can also use rspec’s “around” to do use_cassette.&lt;/p&gt;

&lt;p&gt;Which http stub lib to use?&lt;/p&gt;

&lt;p&gt;FakeWeb is 3 times faster than WebMock, but it only mocks Net::HTTP. WebMock is slower but it works for libcurl’s Patron. Curb coming soon. libwww-perl, etc…&lt;/p&gt;

&lt;p&gt;Request Matching
By default it’ll match requests on the method and the URI. But you can also match on body/headers if necessary by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:match_requests_on =&amp;gt; [:headers, :uri]&lt;/code&gt;, etc…&lt;/p&gt;

&lt;p&gt;You can use different cassettes for the same call to handle edge cases.&lt;/p&gt;

&lt;p&gt;Best Practices
Set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:record =&amp;gt; :none&lt;/code&gt; in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vcr_setup.rb&lt;/code&gt; so that get errors if you make accidental unrecorded HTTP calls.&lt;/p&gt;

&lt;p&gt;For ETAGs, putting match =&amp;gt; :headers in your config might be a good idea.&lt;/p&gt;

&lt;p&gt;Q: what about only doing certain requests to certain services? can you put a per-host filter on it?
A: Not sure.&lt;/p&gt;

&lt;p&gt;Version 2.0 is gonna have automatic re-recording of cassettes based on date. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use_vcr_cassette&lt;/code&gt; macro in rspec to do the around stuff for you.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Docs with Maruku, Jekyll and Prawn</title>
  <link href="https://matschaffer.com/tech/2010/09/27/markdown-jekyll-prawn.html"/>
  <updated>2010-09-27T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/09/27/markdown-jekyll-prawn</id>
  <content type="html">&lt;p&gt;If you’ve been reading my blog at all you’ve probably noticed that I am not a lawyer. Sadly when &lt;a href=&quot;http://mashion.net&quot;&gt;starting your own business&lt;/a&gt;, there are legal documents involved.&lt;/p&gt;

&lt;p&gt;Now I don’t know about you, but doing find and replace on my agreements for every new client sounded tedious and not fun. So I’m working on a way to make it more programmer-ish. Basically I’m taking the &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; approach to legal documentation. Markdown with a YAML header goes in, custom-tailored and nicely formatted PDF comes out.&lt;/p&gt;

&lt;p&gt;With just under 130 lines of ruby and some help from &lt;a href=&quot;http://maruku.rubyforge.org/&quot;&gt;Maruku&lt;/a&gt;, &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; and &lt;a href=&quot;http://prawn.majesticseacreature.com/&quot;&gt;Prawn&lt;/a&gt;, I was able to turn this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;consultant&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Mashion, LLC&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;s&quot;&gt;Awesome Client&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;s&quot;&gt;September 23, &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2010&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;CONSULTING AGREEMENT&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;====================&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;This Consulting Agreement (this “**Agreement**”) is made and entered into&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;as of {{ date }} (the “**Effective Date**”) by and between {{ client }}&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;(the “**Company**”), and {{ consultant }} (“**Consultant**”) (each herein&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;referred to individually as a “**Party**,” or collectively as the&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;“**Parties**”).&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;(legalese continues...)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Into this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://img.skitch.com/20100927-dq43axfs5k75s64dk8ryjbn73x.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Neat, right?&lt;/p&gt;

&lt;p&gt;I’ll definitely be releasing the code properly sometime soon. But in the mean time, &lt;a href=&quot;http://gist.github.com/598566&quot;&gt;here’s a gist&lt;/a&gt; to whet your appetite. If you have any ideas, or if you’re a lawyer who might want in on this hotness, please do &lt;a href=&quot;mailto:mat@schaffer.me&quot;&gt;get in touch&lt;/a&gt;. Thanks!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Using Runnables in the Redcar Editor</title>
  <link href="https://matschaffer.com/tech/2010/08/12/redcar-runnables.html"/>
  <updated>2010-08-12T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/08/12/redcar-runnables</id>
  <content type="html">&lt;p&gt;So it’s been awhile since I’ve posted, and no I haven’t decided to take up
&lt;a href=&quot;https://matschaffer.com/2010/06/curried-chicken/&quot;&gt;cooking&lt;/a&gt;. Although I do use 
&lt;a href=&quot;http://wiki.opscode.com/display/chef/Home&quot;&gt;chef&lt;/a&gt; a lot at work.&lt;/p&gt;

&lt;p&gt;I’ve been spending a lot of time lately working on the &lt;a href=&quot;http://redcareditor.com&quot;&gt;Redcar Text Editor&lt;/a&gt;.
It’s become my favorite way to spend my personal hacking time. My most recent contribution
has been some work on “Runnables” which is the Redcar plugin that lets you run commands
from inside the editor. Even one of the collaborators on the project didn’t really know how to use it
so I thought I’d write an explaination.&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;Like I said Runnables lets you run commands inside Redcar. It let’s you set up two types of commands:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Commands: These become list items that you can click on to run. You can view by selecting Project -&amp;gt; Runnables from the menu.
They look like this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img class=&quot;center&quot; src=&quot;https://cl.ly/2599033874449b819500/content&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;File Runners: These are commands that get run when you hit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+R&lt;/code&gt; if your current file matches a regex.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You define these runners in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.redcar/runnables/mycommands.json&lt;/code&gt;. Runnables will read any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.json&lt;/code&gt; file in that directory.
The file also gets processed whenever you hit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+R&lt;/code&gt; so you can edit the file runners as much as you like. At the
moment, it looks like you have to re-open your project for Commands to get refreshed. I might fix that soon though.&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;So here’s my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;features.json&lt;/code&gt; file which allows me to run a full test suite as well as use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+R&lt;/code&gt; to run
individual Cucumber and RSpec tests:&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;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;commands&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;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;name&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Full suite&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;command&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;jruby -S rake&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;description&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Run the full test suite&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;task/ruby/rake&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;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;file_runners&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;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;regex&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.*&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.feature&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Run as feature&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;command&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;jruby -J-XstartOnFirstThread bin/cucumber __PATH__&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;test/ruby/feature&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;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;regex&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.*_spec&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.rb&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Run a spec&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;command&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;jruby -J-XstartOnFirstThread -S spec __PATH__&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;test/ruby/spec&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;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;So as you can see, the command is just the command that gets run if the file’s path matches &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regex&lt;/code&gt; and the file path gets
substituted for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__PATH__&lt;/code&gt; in the command. I still haven’t really figured out what the type and description fields do
for you yet, but I expect I will soon.&lt;/p&gt;

&lt;h2 id=&quot;bonus&quot;&gt;Bonus&lt;/h2&gt;

&lt;p&gt;If you use my &lt;a href=&quot;http://github.com/matschaffer/redcar/tree/color-and-scrolling-runnables&quot;&gt;color-and-scrolling-runnables&lt;/a&gt; branch
you’ll also get support for ANSI color output and automatic scrolling (courtesy of &lt;a href=&quot;http://github.com/kattrali&quot;&gt;Delisa Mason&lt;/a&gt;).
Hopefully both of these features will be in &lt;a href=&quot;http://github.com/danlucraft/redcar&quot;&gt;master&lt;/a&gt; soon. Here are the commands
to force color if you want to give it a try:&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;# Force color in Cucumber&lt;/span&gt;
jruby &lt;span class=&quot;nt&quot;&gt;-J-XstartOnFirstThread&lt;/span&gt; bin/cucumber &lt;span class=&quot;nt&quot;&gt;--color&lt;/span&gt; __PATH__

&lt;span class=&quot;c&quot;&gt;# Force color in RSpec&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;AUTOTEST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true &lt;/span&gt;jruby &lt;span class=&quot;nt&quot;&gt;-J-XstartOnFirstThread&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; spec &lt;span class=&quot;nt&quot;&gt;--color&lt;/span&gt; __PATH__&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;future-enhancements&quot;&gt;Future Enhancements&lt;/h2&gt;

&lt;p&gt;I’ve been told that this plugin only works on Unix systems right now. So Windows users can’t partake in the
awesomeness just yet. I’ll see how I can help there.&lt;/p&gt;

&lt;p&gt;I haven’t found a way to make a general &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.rb&lt;/code&gt; matcher without having my specs run twice. Seems like
the plugin needs support for a “last” flag to make sure that we don’t keep moving down the list and running more
commands. Perhaps the type field has something to do with it.&lt;/p&gt;

&lt;p&gt;I’d like to have a way to reuse the “Run File” tabs that get created so I can just keep re-running my specs without
having to clean up old output. I haven’t decided the best workflow for that yet. Sometimes you want to be able to see
old output and so I don’t want to just clobber that in all cases.&lt;/p&gt;

&lt;p&gt;If you have any other ideas feel free to come chat about it in the &lt;a href=&quot;irc://irc.freenode.net/#redcar&quot;&gt;Redcar IRC channel&lt;/a&gt; or
on the &lt;a href=&quot;http://groups.google.com/group/redcar-editor&quot;&gt;Redcar mailing list&lt;/a&gt;.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>How to mock out jQuery and Raphael</title>
  <link href="https://matschaffer.com/tech/2010/04/29/mock-jquery-javascript-libraries.html"/>
  <updated>2010-04-29T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/04/29/mock-jquery-javascript-libraries</id>
  <content type="html">&lt;p&gt;Most people are in agreement now that testing is a good thing. But most any JavaScript developer out there will tell you that testing browser-dependent code (jQuery, Raphael, etc.) can be a pain. You usually have to concede to running your tests in the browser, or spend lots time of pounding out &lt;a href=&quot;http://github.com/thatcher/env-js&quot;&gt;env.js&lt;/a&gt; bugs to get something headless running.&lt;/p&gt;

&lt;p&gt;But here at Hoopla, &lt;a href=&quot;http://www.trottercashion.com/2010/04/27/headless-raphael-testing.html&quot; title=&quot;Headless Raphael Testing&quot;&gt;Trotter&lt;/a&gt; and I have found a very happy middle ground. As outlined in his post, it’s trivial to make a simple mock Raphael that satisfies your code and allows you to test what got called. But in practice, maintaining these mock classes gets repetitive quickly.&lt;/p&gt;

&lt;p&gt;So I wrote &lt;a href=&quot;http://github.com/matschaffer/recorderMock.js&quot; title=&quot;matschaffer&apos;s recorderMock.js at master - GitHub&quot;&gt;recorderMock.js&lt;/a&gt;, which makes it easy to create full-featured mock objects for things like jQuery, Raphael and any other library that doesn’t run outside of the browser.&lt;/p&gt;

&lt;p&gt;Say you wanted to test some code that used jQuery to resize a div:&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;$&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;#display&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;span class=&quot;nf&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;someComputedWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;someComputedHeight&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;I think I just heard your inner tester say &lt;em&gt;“oh shit.”&lt;/em&gt; Worry no longer my friend. Building a mock for this with recorderMock is super simple. Just put this in your setup code.&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;nx&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;recorderMock&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;width&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;height&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;You can even check that they got called with the right values:&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;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;__calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;eql&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;mi&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// or &lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;__calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;eql&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;mi&quot;&gt;250&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;And specifying return values is only slightly more complicated. Say you want to return mock html for different DOM elements:&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;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startDate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;$&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;#start-date&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;span class=&quot;nf&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;endDate&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;$&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;#end-date&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;span class=&quot;nf&quot;&gt;html&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;Mock it out like this:&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;nx&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;recorderMock&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;html&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;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;__returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&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;selector&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;previous&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&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;#start-date&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;January 1st, 2010&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#end-date&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;January 31st, 2010&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;span class=&quot;nx&quot;&gt;selector&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;Need a basic mock of Raphael? Just use this:&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;nx&quot;&gt;Raphael&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;recorderMock&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;path&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;rect&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;text&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;attr&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;init&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;push&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;set&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hide&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;show&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;animate&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;animateWith&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;rotate&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;translate&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;setSize&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;So there you have it. You can indeed test browser-related code outside a browser. And it’s pretty easy.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/matschaffer/recorderMock.js&quot; title=&quot;matschaffer&apos;s recorderMock.js at master - GitHub&quot;&gt;recorderMock&lt;/a&gt; has a handful of other tricks, and I’ll probably be adding more soon, so keep an eye on &lt;a href=&quot;http://github.com/matschaffer/recorderMock.js/blob/master/spec/unit/spec.recorderMock.js&quot; title=&quot;spec/unit/spec.recorderMock.js at master from matschaffer&apos;s recorderMock.js - GitHub&quot;&gt;the specs&lt;/a&gt; for examples on how to use it more fully.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update 2010-05-06:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I just released version 0.3.0 which includes better support for inspecting the call chain. The above examples have been updated to reflect the API changes and enhancements.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Things I Love About Bundler</title>
  <link href="https://matschaffer.com/tech/2010/04/20/things-i-love-about-bundler.html"/>
  <updated>2010-04-20T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/04/20/things-i-love-about-bundler</id>
  <content type="html">&lt;p&gt;So I’ve been working with working with &lt;a href=&quot;http://guides.rails.info/3_0_release_notes.html&quot; title=&quot;Ruby on Rails Guides: Ruby on Rails 3.0 Release Notes&quot;&gt;Rails 3&lt;/a&gt; and &lt;a href=&quot;http://github.com/carlhuda/bundler&quot; title=&quot;carlhudas&apos;s bundler at master - GitHub&quot;&gt;bundler&lt;/a&gt; full time for a month and a half now and it’s going swimmingly.&lt;/p&gt;

&lt;p&gt;Here are some things that bundler does that I think are awesome.&lt;/p&gt;

&lt;h2 id=&quot;gems-from-git-repositories&quot;&gt;Gems from git repositories&lt;/h2&gt;

&lt;p&gt;We use custom forks of &lt;a href=&quot;http://github.com/rails/arel&quot; title=&quot;rails&apos;s arel at master - GitHub&quot;&gt;arel&lt;/a&gt; and &lt;a href=&quot;http://github.com/rubiii/savon&quot; title=&quot;rubiii&apos;s savon at master - GitHub&quot;&gt;savon&lt;/a&gt; in our project. Before bundler, managing this would have been a mix of submodules/braid/piston and custom gem files that could easily become a mess. With bundler we just throw this in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt; and we’re done.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;savon&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:git&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;git://github.com/hoopla/savon.git&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;arel&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;ss&quot;&gt;:git&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;git@github.com:hoopla/arel.git&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;gems-from-the-local-file-system&quot;&gt;Gems from the local file system&lt;/h2&gt;

&lt;p&gt;Much like loading from git, bundler also handles loading gems from a local path. This is great if you want to try out some ideas on a gem, but you’re not yet sure if gem is worth forking.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;mygem&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/path/to/my/gem&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;What’s especially awesome is that bundler wires things up so that source modifications get picked up without any sort of repackaging or reinstalling. This beats the pants off of the &lt;em&gt;change source &amp;gt; repackage gem &amp;gt; reinstall gem &amp;gt; test&lt;/em&gt; cycle that was so easy to fall into before bundler.&lt;/p&gt;

&lt;h2 id=&quot;it-actually-works&quot;&gt;It actually works&lt;/h2&gt;

&lt;p&gt;I don’t know about you, but I don’t think I’ve ever had a real Rails 2 project on which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rake gems:install&lt;/code&gt; really worked. There were always a couple of gems (rspec) that you needed to install separately. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt; on the other hand works pretty much every time.&lt;/p&gt;

&lt;p&gt;Now of course it’s not all rainbows and unicorns. Bundler seems to take up some extra startup time beyond what I was used to on Rails 2. And I often wish there were a way to make it check git repositories for updates a little less aggressively. But for 0.9.22, I’m impressed. Hats off to &lt;a href=&quot;http://github.com/carlhuda&quot; title=&quot;carlhuda&apos;s Profile - GitHub&quot;&gt;carlhuda&lt;/a&gt;, &lt;a href=&quot;http://github.com/indirect&quot; title=&quot;indirect&apos;s Profile - GitHub&quot;&gt;indirect&lt;/a&gt; and any others that appear beyond page 3 of the commit log for all their hard work on it.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Managing Unix packages on OS X with Homebrew</title>
  <link href="https://matschaffer.com/tech/2010/03/27/homebrew-package-manager.html"/>
  <updated>2010-03-27T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/03/27/homebrew-package-manager</id>
  <content type="html">&lt;p&gt;This month I started a &lt;a href=&quot;http://hoopla.net&quot;&gt;new gig&lt;/a&gt; which landed me with a new MacBook
so I thought it seemed like a good time to try out the &lt;a href=&quot;http://github.com/mxcl/homebrew&quot;&gt;Homebrew&lt;/a&gt;
package manager. It’s almost a month later and I am loving it. The beer-related jargon doesn’t hurt
either.&lt;/p&gt;

&lt;p&gt;Have you ever gotten annoyed at the time it takes for a package to get updated on MacPorts or Fink? Or
tried to install a MacPorts package only to have it reinstall your ssh client or other core system
component? I got fed up with this sort of thing over a year ago and have been &lt;a href=&quot;/2009/05/installing-imagemagick-on-os-x-leopard/&quot;&gt;compiling packages by
hand&lt;/a&gt; ever since. Then I found Homebrew.&lt;/p&gt;

&lt;p&gt;Installation is straightforward. You’ll need XCode of course, then you can just do this:&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;sudo chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt; /usr/local
curl &lt;span class=&quot;nt&quot;&gt;-Lsf&lt;/span&gt; http://github.com/mxcl/homebrew/tarball/master | &lt;span class=&quot;nb&quot;&gt;tar &lt;/span&gt;xvz &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt;/usr/local &lt;span class=&quot;nt&quot;&gt;--strip&lt;/span&gt; 1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Or they have more automated approaches in their README if you’re into that sort of thing.&lt;/p&gt;

&lt;p&gt;The real win of Homebrew is how packages are defined: in Ruby. Have a look at this hotness for 
describing how to install redis:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;formula&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Redis&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Formula&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;http://redis.googlecode.com/files/redis-1.2.5.tar.gz&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;homepage&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;http://code.google.com/p/redis/&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sha1&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;f28d840d8100586796cab02ccd8e91545a92179d&apos;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;install&lt;/span&gt;
    &lt;span class=&quot;sx&quot;&gt;%w( run db/redis log )&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mkpath&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gcc_4_2&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;make&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w( redis-benchmark redis-cli redis-server )&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Fix up default conf file to match our paths&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inreplace&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redis.conf&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub!&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/run/redis.pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/run/redis.pid&quot;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub!&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dir ./&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dir &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/db/redis/&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;etc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redis.conf&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;caveats&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;To start redis: $ redis-server &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;etc&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/redis.conf&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;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hot right? What’s more is that if you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew update&lt;/code&gt; it’ll turn your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local&lt;/code&gt; into a working git
repository where you can tweak these scripts, push to your own fork and contribute back. Compare that to
the MacPorts &lt;a href=&quot;http://trac.macports.org/browser/trunk/dports/databases/redis/Portfile&quot;&gt;Portfile&lt;/a&gt; for redis
which took me 5 minutes just to find, let alone tweak or contribute. Homebrew keeps all those files in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/Library/Formula&lt;/code&gt;, so have at it!&lt;/p&gt;

&lt;p&gt;For more info on Homebrew check out &lt;a href=&quot;http://wiki.github.com/mxcl/homebrew/&quot;&gt;their wiki&lt;/a&gt; or their
&lt;a href=&quot;http://wiki.github.com/mxcl/homebrew/press&quot;&gt;collection of blog links&lt;/a&gt; as I’m sure they’re more accurate
than anything I’d produce.&lt;/p&gt;

&lt;p&gt;So go grab a beer and toast your package management woes farewell. Hope you enjoy Homebrew as much as
I do!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Learning to Draw with Screencasts</title>
  <link href="https://matschaffer.com/tech/2010/02/05/i-drew-a-popsicle.html"/>
  <updated>2010-02-05T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/02/05/i-drew-a-popsicle</id>
  <content type="html">&lt;p&gt;I’ve been having so much fun with &lt;a href=&quot;http://www.inkscape.org/&quot; title=&quot;Inkscape. Draw Freely.&quot;&gt;Inkscape&lt;/a&gt; lately that I thought I should share. I basically got started with it when my wife and I ended up with no other option for our Christmas cards but to decorate our photos ourselves. Apparently most photo places don’t do Christmas templates in a 4x6 format. So I downloaded Inkscape, bought a set of Christmas-styled stencils from &lt;a href=&quot;http://www.istockphoto.com/&quot; title=&quot;Stock Photography: Search Royalty Free Images &amp;amp; Photos&quot;&gt;iStockPhoto.com&lt;/a&gt; and we went to town.&lt;/p&gt;

&lt;p&gt;Then she wanted sparkles. Sparkles… Our stencil set was sadly lacking sparkles. So I did what I always do when I need to learn something: I googled it. And found &lt;a href=&quot;https://screencasters.heathenx.org/blog/2008/01/23/episode-050-sparkle-effect/&quot; title=&quot;screencasters.heathenx.org/blog  &amp;raquo; Blog Archive   &amp;raquo; Episode 050 &amp;#8211; Sparkle Effect&quot;&gt;this lovely tutorial&lt;/a&gt; which showed me how to make a sparkle. It worked out great and the Christmas cards looked awesome. I enjoyed the process so much that I subscribed to heathenx’s RSS feed of Inkscape screencasts.&lt;/p&gt;

&lt;p&gt;If you’re a coder interested in learning how to create some basic visual elements, I highly recommend checking out &lt;a href=&quot;http://screencasters.heathenx.org/&quot; title=&quot;screencasters.heathenx.org&quot;&gt;screencasters.heathenx.org&lt;/a&gt;. It’s basically &lt;a href=&quot;https://railscasts.com/&quot; title=&quot;Railscasts - Free Ruby on Rails Screencasts&quot;&gt;RailsCasts&lt;/a&gt; for vector drawing. So far I’ve only watched the two screencasts, but I feel like I’ve learned a lot already. It also has a similar relaxation appeal to painting happy little trees.&lt;/p&gt;

&lt;p&gt;Who knows maybe one day I can even make the jump &lt;a href=&quot;https://www.arpitonline.com&quot; title=&quot;arpitonline.com&quot;&gt;from engineering to design&lt;/a&gt;…&lt;/p&gt;

&lt;p&gt;or not.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Migrating from Subversion to ... Subversion?</title>
  <link href="https://matschaffer.com/tech/2010/01/25/migrate-from-svn-to-svn.html"/>
  <updated>2010-01-25T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2010/01/25/migrate-from-svn-to-svn</id>
  <content type="html">&lt;p&gt;As an update to &lt;a href=&quot;/2009/11/migrate-from-perforce-to-svn/&quot;&gt;my previous post&lt;/a&gt;, one of my coworkers found that this technique was also applicable to migration between Subversion servers and that using git as intermediary was actually a bit more graceful than the traditional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;svnadmin dump/load&lt;/code&gt; approach. Git wins once again!&lt;/p&gt;

&lt;p&gt;The one thing he noted is that this ends up with occasional 0-length patch files, I’m guessing from commits that only modified svn properties. These files will cause &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am&lt;/code&gt; to abort, but can just be deleted for the re-import.&lt;/p&gt;

&lt;p&gt;Here’s the script he used:&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;# Source URL&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SRC_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Destination URL&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DST_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Source and destination repo types (svn,p4)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SRC_TYPE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;svn
&lt;span class=&quot;nv&quot;&gt;DST_TYPE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_TYPE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;PROJECT&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;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_URL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Clone SRC repo&lt;/span&gt;
git &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_TYPE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; clone &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_URL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_src.git&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_TYPE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Julienne SRC clone into patch files&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.patches&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_src.git&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_TYPE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
git format-patch &lt;span class=&quot;nt&quot;&gt;--root&lt;/span&gt; HEAD &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;../&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.patches&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..

&lt;span class=&quot;c&quot;&gt;# Clone DST repo with empty target PROJECT dir&lt;/span&gt;
svn &lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DST_URL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Initial repository layout.&quot;&lt;/span&gt;
git &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DST_TYPE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; clone &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DST_URL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_dst.git&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DST_TYPE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Merge SRC patches into DST clone&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_dst.git&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DST_TYPE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
git am &lt;span class=&quot;s2&quot;&gt;&quot;../&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.patches/&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Commit changes DST repo&lt;/span&gt;
git &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DST_TYPE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; dcommit &lt;span class=&quot;nt&quot;&gt;--add-author-from&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
</content>
</entry>

<entry>
  <title>Migrating from Perforce to Subversion</title>
  <link href="https://matschaffer.com/tech/2009/11/27/migrate-from-perforce-to-svn.html"/>
  <updated>2009-11-27T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/11/27/migrate-from-perforce-to-svn</id>
  <content type="html">&lt;p&gt;So my new favorite hack of the week was created while trying to find a good way to import code from our partner’s &lt;a href=&quot;http://www.perforce.com/&quot; title=&quot;Perforce Software - The Fast Software Configuration Management System&quot;&gt;Perforce&lt;/a&gt; repository to our &lt;a href=&quot;http://subversion.tigris.org/&quot; title=&quot;subversion.tigris.org&quot;&gt;Subversion&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;I wanted to do this while retaining history but the &lt;a href=&quot;http://p42svn.tigris.org/&quot; title=&quot;p42svn.tigris.org&quot;&gt;p4svn script&lt;/a&gt; that I found depended on the deprecated &lt;a href=&quot;http://www.perforce.com/perforce/loadsupp.html#api&quot; title=&quot;Perforce-Related Software: Conversions, Sample Depot, APIs, Python, Perl, etc.&quot;&gt;Perforce Perl API&lt;/a&gt; which made me nervous about trying to get it working.&lt;/p&gt;

&lt;p&gt;So instead I decided to use &lt;a href=&quot;http://git-scm.com/&quot; title=&quot;Git - Fast Version Control System&quot;&gt;Git&lt;/a&gt; as an intermediary. My initial attempts to do this by way of a git merge or rebase didn’t work out. The best I could get was the full source as one commit in subversion. But since git has really solid patch file support, I decided to try using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git format-patch&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am&lt;/code&gt; to get the changes from one repository to the other.&lt;/p&gt;

&lt;p&gt;The end result was about as good as I could have hoped for. Including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--add-author-from&lt;/code&gt; even put the original Perforce information into the subversion commit logs which was awesome. The only downside I’ve seen so far today is that folders that should be deleted aren’t always deleted in the final import. I’m guessing this has to do with how git ignores empty directories but svn doesn’t. But cleaning them up wasn’t too bad for me since the projects had a consistent structure. If the project had some sort of major reorganization happen, you might want to dig into that and try to figure out a better solution.&lt;/p&gt;

&lt;p&gt;So here’s the script, also checked into my &lt;a href=&quot;http://github.com/matschaffer/randomscripts&quot;&gt;randomscripts&lt;/a&gt; repository. Feel free to ask any questions or school me on a better way to do this!&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;# Set these before you start&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# export P4PORT=myperforceserver:1666&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# export P4USER=myusername&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# export P4PASSWD=mypassword&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Source Perforce depot path (without @all)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;P4PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Destination SVN url (without trunk, we create that)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SVNPATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;PROJECT&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;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SVNPATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# git p4 clone &quot;${P4PATH}@all&quot; &quot;${PROJECT}.gitp4&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.patches&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.gitp4&quot;&lt;/span&gt;
git format-patch &lt;span class=&quot;nt&quot;&gt;--root&lt;/span&gt; HEAD &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;../&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.patches&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..

svn &lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--parents&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SVNPATH&lt;/span&gt;&lt;span class=&quot;k&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;trunk,branches,tags&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Initial repository layout.&quot;&lt;/span&gt;
git svn clone &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SVNPATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/trunk&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.gitsvn&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.gitsvn&quot;&lt;/span&gt;
git am &lt;span class=&quot;s2&quot;&gt;&quot;../&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.patches/&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
git svn dcommit &lt;span class=&quot;nt&quot;&gt;--add-author-from&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
</content>
</entry>

<entry>
  <title>Barcamp Philly, in Japanese</title>
  <link href="https://matschaffer.com/tech/2009/11/26/barcamp-in-japanese.html"/>
  <updated>2009-11-26T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/11/26/barcamp-in-japanese</id>
  <content type="html">&lt;p&gt;A little belated, but not too long ago I went to &lt;a href=&quot;http://www.barcampphilly.org&quot;&gt;Barcamp Philly 2009&lt;/a&gt; with my wife and son. It was a great experience not only to be a part of the blossoming Philly tech scene, but also having my family there encouraged me to branch out a little and head to some really enjoyable talks that I probably wouldn’t have otherwise.&lt;/p&gt;

&lt;p&gt;Shortly after the event, Kaori posted a writeup in her &lt;a href=&quot;http://mixi.jp/view_diary.pl?id=1338958355&amp;amp;owner_id=11017782&quot;&gt;Mixi blog&lt;/a&gt;. I thought this was really cool and wanted to share it with other folks that might be interested. So I’ve reposted it here with her permission.&lt;/p&gt;

&lt;p&gt;相方の付き添いでBar campなるものに行って来ました。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4136572654/&quot; title=&quot;IMG_4752 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2493/4136572654_db23e01582.jpg&quot; width=&quot;375&quot; height=&quot;500&quot; alt=&quot;IMG_4752&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4135814807/&quot; title=&quot;IMG_4760 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2550/4135814807_5703e02d72.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;IMG_4760&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IT系やプログラマー系のコンフェランス（協議会？？）みたいな感じかな。&lt;/p&gt;

&lt;p&gt;でも堅苦しくなくて、かな～りカジュアル。&lt;/p&gt;

&lt;p&gt;大学を貸し切って、いろんな部屋でいろんな人がプレゼンテーションをしてるので、興味があるタイトルならその部屋に行き、自由に聞けたりディスカッション出来るというもの。&lt;/p&gt;

&lt;p&gt;テクノロジーにうとい私にはちんぷんかんぷん。。。。と思いきや、意外におもしろい内容のプレゼンテーションもあったり。&lt;/p&gt;

&lt;p&gt;カイも参加！&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4136573114/&quot; title=&quot;IMG_4753 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2595/4136573114_376944670f.jpg&quot; width=&quot;375&quot; height=&quot;500&quot; alt=&quot;IMG_4753&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4136573626/&quot; title=&quot;IMG_4754 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2728/4136573626_dd1a79fb0f.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;IMG_4754&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4135813175/&quot; title=&quot;IMG_4755 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2636/4135813175_6567c0e49f.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;IMG_4755&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4135817335/&quot; title=&quot;??-1 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2551/4135817335_a6f19ca347.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;??-1&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;見てておもしろいなと思ったのは、こういうプレゼンテーションとかの途中でも質問とかあったら、みんなどしどし聞いたり、聞いてないのに自分の意見言ったりつっこみ入れたりして積極的なこと。&lt;/p&gt;

&lt;p&gt;日本だと、しーーーんとしちゃうよね。&lt;/p&gt;

&lt;p&gt;にしても、内容が内容なだけに人々も濃かった。&lt;/p&gt;

&lt;p&gt;どれくらいかというと&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4136575344/&quot; title=&quot;IMG_4758 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2511/4136575344_59c35be67e.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;IMG_4758&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;こんなマニアックなTシャツを着てる人がいるくらい。（何の映画か分かるかな？）&lt;/p&gt;

&lt;p&gt;４ヶ月のカイ坊、、、既にこんなところに連れて来られて一体将来どれほどマニアックになるのか。。。。&lt;/p&gt;

&lt;p&gt;講義中のカイ。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4135815845/&quot; title=&quot;IMG_4763 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2691/4135815845_1e6e20e9e7.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;IMG_4763&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ちゃんとネーム入りパスをもらった。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;//www.flickr.com/photos/schapht/4136576252/&quot; title=&quot;IMG_4761 by matschaffer, on Flickr&quot;&gt;&lt;img class=&quot;center&quot; src=&quot;//farm3.static.flickr.com/2630/4136576252_55f9c489a1.jpg&quot; width=&quot;375&quot; height=&quot;500&quot; alt=&quot;IMG_4761&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Hello, Jekyll.</title>
  <link href="https://matschaffer.com/tech/2009/11/11/hello-jekyll.html"/>
  <updated>2009-11-11T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/11/11/hello-jekyll</id>
  <content type="html">&lt;p&gt;So I just ported my blog from WordPress to &lt;a href=&quot;http://jekyllrb.com&quot; title=&quot;jekyll&quot;&gt;Jekyll&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why? Because once again while posting my latest &lt;a href=&quot;/tech/2009/10/call-jruby-jython-from-maven/&quot;&gt;maven trick&lt;/a&gt; I got frustrated trying to post &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;code&lt;/code&gt; in my blog and also get the formatting to work the way I wanted it.&lt;/p&gt;

&lt;p&gt;I think I just prefer &lt;a href=&quot;http://en.wikipedia.org/wiki/WYSIWYM&quot; title=&quot;WYSIWYM - Wikipedia, the free encyclopedia&quot;&gt;WYSIWYM&lt;/a&gt; to &lt;a href=&quot;http://en.wikipedia.org/wiki/WYSIWYG&quot; title=&quot;WYSIWYG - Wikipedia, the free encyclopedia&quot;&gt;WYSIWYG&lt;/a&gt;. But that’s just me.&lt;/p&gt;

&lt;p&gt;The transition wasn’t bad, but was more manual than I had hoped, mostly because of all the garbage formatting in my WordPress posts. Some were good, but others were a mess due to my own indecision on how to get the article into WordPress (edit in browser, edit as HTML, write markdown then post HTML, etc…).&lt;/p&gt;

&lt;p&gt;So we’ll see how this goes. One thing that I really like already is how easy it is to fix broken/deprecated links when your whole blog is just sitting on your file system: Just a simple find and replace. I’m also considering writing a rake task that validates the whole generated site and possibly some sort of job queuing set up to move the widget stuff onto the server and reduce my javascript dependency.&lt;/p&gt;

&lt;p&gt;Let me know what you think. There’s still some styling to be done, but I think it’s an improvement over the old site and &lt;a href=&quot;http://kaorippe.com&quot;&gt;the lady&lt;/a&gt; agrees.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Call JRuby, Jython or other JVM scripting language from Maven</title>
  <link href="https://matschaffer.com/tech/2009/10/23/call-jruby-jython-from-maven.html"/>
  <updated>2009-10-23T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/10/23/call-jruby-jython-from-maven</id>
  <content type="html">&lt;p&gt;Ever been frustrated trying to &lt;a href=&quot;http://matschaffer.com/2008/09/more-work-on-corundum/&quot;&gt;extend maven&lt;/a&gt;? Yeah me too. So here’s a trick I finally pounded out a couple weeks ago that I thought I’d share here.&lt;/p&gt;

&lt;p&gt;It’s pretty common place to use the &lt;a href=&quot;http://maven.apache.org/plugins/maven-antrun-plugin/&quot;&gt;maven antrun plugin&lt;/a&gt; to perform some extra build work that you couldn’t otherwise do without writing a full-blown maven plugin. You might even already use it to call scripts that move files around, compress CSS, or whatever. But wouldn’t it be nice if maven could download the script’s runtime for you? It can. Just throw these chunks into the plugins sections of your pom.xml file.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-antrun-plugin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jruby&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jruby-complete&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.4.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;my_script&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;compile&lt;span class=&quot;nt&quot;&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;tasks&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;java&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;classname=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.jruby.Main&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;failonerror=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;arg&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;${basedir}/src/main/ruby/myscript.rb&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/java&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tasks&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And of course the same technique should work for any language that you can run as an argument java class (jython, rhino, clojure).&lt;/p&gt;

&lt;p&gt;Now some of you might be asking “why not use the maven jruby plugin?”. Well because as best I can tell it still runs against JRuby 0.9.9. A bit too far behind the curve if you ask me. I expect you’ll find the same with Jython. So there.&lt;/p&gt;

&lt;p&gt;Hope that helps some folks. I know it made wrangling my builds a world easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (11/13)&lt;/strong&gt;: Thanks to Brian Fox (via &lt;a href=&quot;irc://irc.codehaus.org#maven&quot;&gt;irc.codehaus.org#maven&lt;/a&gt;) for guiding me toward a much more graceful method of doing this. And for bonus points, the updated method (above) also allows you to load in &lt;a href=&quot;http://blog.nicksieger.com/articles/2009/01/10/jruby-1-1-6-gems-in-a-jar&quot; title=&quot;JRuby 1.1.6: Gems-in-a-jar&quot;&gt;gems as jar&lt;/a&gt; dependencies. Just add them to your dependency list (and maven repository), then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;require &apos;rubygems&apos;; require &apos;#{gem_name}&apos;&lt;/code&gt; as usual. Hot!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>JavaScript templates, build-time lovin&apos;</title>
  <link href="https://matschaffer.com/tech/2009/10/03/javascript-templates.html"/>
  <updated>2009-10-03T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/10/03/javascript-templates</id>
  <content type="html">&lt;p&gt;(Reposted from &lt;a href=&quot;http://github.com/matschaffer/trimpath-templateRegistry&quot;&gt;http://github.com/matschaffer/trimpath-templateRegistry&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Ever wanted to render HTML templates from javascript? Yeah, so did we. And so do a lot of people apparently. There are &lt;a href=&quot;http://beebole.com/pure/&quot; title=&quot;PURE - JavaScript Template Engine&quot;&gt;a&lt;/a&gt; &lt;a href=&quot;http://embeddedjs.com/&quot; title=&quot;EJS - JavaScript Templates&quot;&gt;lot&lt;/a&gt; &lt;a href=&quot;http://www.kuwata-lab.com/tenjin/&quot; title=&quot;Tenjin - the fastest template engine in the world&quot;&gt;of&lt;/a&gt; &lt;a href=&quot;http://code.google.com/p/jssmarty/&quot; title=&quot;jssmarty - Project Hosting on Google Code&quot;&gt;toolkits&lt;/a&gt; &lt;a href=&quot;http://code.google.com/p/trimpath/wiki/JavaScriptTemplates&quot; title=&quot;JavaScriptTemplates - trimpath - Project Hosting on Google Code&quot;&gt;around&lt;/a&gt; for this.&lt;/p&gt;

&lt;p&gt;We at CIM were evaluating some of them and had the realization that since we’re already processing JavaScript (compression) and CSS (from &lt;a href=&quot;http://lesscss.org/&quot; title=&quot;LESS - Leaner CSS&quot;&gt;LESS&lt;/a&gt; and compression) at build time, why not process the templates at build time as well?&lt;/p&gt;

&lt;p&gt;So I give you &lt;a href=&quot;http://github.com/matschaffer/trimpath-templateRegistry&quot;&gt;TrimPath TemplateRegistry&lt;/a&gt;. After evaluating all of the above mentioned template toolkits, we settled on &lt;a href=&quot;http://code.google.com/p/trimpath/wiki/JavaScriptTemplates&quot; title=&quot;JavaScriptTemplates - trimpath - Project Hosting on Google Code&quot;&gt;TrimPath’s JST&lt;/a&gt; because we all could agree that the syntax was reasonable (&lt;a href=&quot;http://beebole.com/pure/&quot; title=&quot;PURE - JavaScript Template Engine&quot;&gt;Pure&lt;/a&gt; was a little hit or miss) and it was able to produce fairly clean JavaScript after processing a template. And when I say clean in this case I mean the generated JavaScript also has no dependency on any library. Since we’ve already processed the templates, it’d be a shame if we still required the browser to parse the template toolkit at run time.&lt;/p&gt;

&lt;p&gt;It seems to be working so far at least in theory. We haven’t applied this to production code yet, but hopefully we will soon. Check out client/demo.html for a rough sketch of the idea. The “client” folder is what’s intended for use at run-time. The “server” folder is what’s intended for use at build-time.&lt;/p&gt;

&lt;p&gt;There’s still a lot of work to be done. My biggest outstanding question is how to handle the the template registration function name such that we can be pretty flexible about how/where the templates get loaded. And of course, we’ll need some specs now that I actually have some rough idea of what the heck I’m doing :)&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Harsh opinions on JavaScript testing</title>
  <link href="https://matschaffer.com/tech/2009/08/26/harsh-opinions-on-javascript-testing.html"/>
  <updated>2009-08-26T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/08/26/harsh-opinions-on-javascript-testing</id>
  <content type="html">&lt;p&gt;JavaScript testing has been a hot topic at CIM lately. Enough that I’ve managed to form some opinions on the matter that &lt;a href=&quot;http://arpitonline.com&quot;&gt;Arpit&lt;/a&gt; thought I should share with the world. So here’s a rundown of all the javascript testing tools I’ve looked at and my opinions on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://github.com/nkallen/screw-unit&quot;&gt;Screw Unit&lt;/a&gt;&lt;/strong&gt;: More than just a funny name, this is so far the most enjoyable javascript testing tool I’ve used yet. BDD looks great in JavaScript. The jQuery-bias also works for us since jQuery is our framework of choice around here. And to top it off, it’s got a beautiful and functional runner which requires almost no boilerplate code allows focused testing with a single click. I wrote the test suite for &lt;a href=&quot;http://github.com/matschaffer/isepta-train-view.wdgt&quot;&gt;iSepta Train View&lt;/a&gt; in Screw Unit and have been using it at work too.&lt;/p&gt;

&lt;p&gt;The biggest downside to Screw Unit is the project momentum. The original authors don’t seem to be doing much with it these days and it’s difficult to find a source of truth. So in the mean time I look to &lt;a href=&quot;http://github.com/trotter/screw-unit&quot;&gt;Trotter Cashion’s fork&lt;/a&gt; for stability since I know he’s used it for testing a number of production apps. His version also has a better Stubbing/Mocking toolkit than what Screw Unit originally comes with.&lt;/p&gt;

&lt;p&gt;If you’re interested in a command line runner Screw Unit on Rails, check out &lt;a href=&quot;http://github.com/relevance/blue-ridge&quot;&gt;Blue Ridge&lt;/a&gt;. Or for off rails (and on Mac), try &lt;a href=&quot;http://github.com/nakajima/screw-driver&quot;&gt;Screw Driver&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://github.com/pivotal/jasmine&quot;&gt;Jasmine&lt;/a&gt;&lt;/strong&gt;: I do have one gripe with Screw Unit that I blame Trotter for (I might not have noticed if he didn’t mention it). It uses snake_casing rather than JavaScript’s own camelCaseConvention. Jasmine gets that right. On top of that, it appears to stay framework and DOM independent. I’m a little concerned about the runner though since they don’t show it off in the readme and the docs recommend find/replace as a method for running focused tests which is super lame.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://code.google.com/p/js-test-driver&quot;&gt;JsTestDriver&lt;/a&gt;&lt;/strong&gt;: Written by Java developers for Java developers. And as best I can tell offers no support for asynchronous tests. The browser capturing is cool, and I’ve seen some interesting advances in the &lt;a href=&quot;http://googletesting.blogspot.com/2009/08/super-fast-js-testing.html&quot;&gt;tooling&lt;/a&gt; for this framework, but it doesn’t seem worth the Java culture that comes along for the ride.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://jsunittest.com&quot;&gt;JsUnitTest&lt;/a&gt;&lt;/strong&gt;: This is a fork of prototype’s test suite but with the prototype dependency removed. I was excited about this early on, but had a lot of trouble getting it to run correctly across all browsers. The runner pages also require a fair bit of boilerplate which is a big turn-off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://www.jsunit.net&quot;&gt;JsUnit&lt;/a&gt;&lt;/strong&gt;: I’ll mention this purely for historical reasons. Don’t use it. It’s old, crufty and I’m pretty sure they spent more time on their logo than they did on the test runner. We started using this on our project originally and quickly grew to hate and ignore it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://visionmedia.github.com/jspec/&quot;&gt;JSpec&lt;/a&gt;&lt;/strong&gt;: I haven’t spent much time with JSpec but it does look fairly well thought out so I might give it another chance. What caused me to write it off initially was the introduction of a ruby-like test grammar rather than just straight JavaScript. Helpers for anonymous functions are cool and all but not at the cost of having to learn a new grammar which apparently even has issues running some operations correctly. As much as I love ruby, the JSpec grammar is an unnecessary complication. JavaScript is capable language, use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;: So that’s it. Hope this saves some other people the pain of wading through a number of different testing frameworks. The JavaScript culture is a funny one but I’ve seen a lot of promise and progress lately. If you have any tools you like that I haven’t covered here, please comment!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Installing ImageMagick on OS X Leopard</title>
  <link href="https://matschaffer.com/tech/2009/05/17/installing-imagemagick-on-os-x-leopard.html"/>
  <updated>2009-05-17T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/05/17/installing-imagemagick-on-os-x-leopard</id>
  <content type="html">&lt;p&gt;So last night I tried to install &lt;a href=&quot;http://www.imagemagick.org/&quot;&gt;ImageMagick&lt;/a&gt; on Leopard but couldn’t find the requisite blog article that detailed the installation. So I thought I’d write one. I managed to dig up &lt;a href=&quot;http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&amp;amp;t=9823&quot;&gt;some forum posts&lt;/a&gt;, so here’s the whole deal.&lt;/p&gt;

&lt;p&gt;ImageMagick itself compiles fairly easily on &lt;span class=&quot;caps&quot;&gt;OS X&lt;/span&gt;, but it’ll end up missing &lt;span class=&quot;caps&quot;&gt;JPEG&lt;/span&gt; support, which was kind of a bummer. Here’s how to fix it without using &lt;a href=&quot;http://www.macports.org/&quot;&gt;MacPorts&lt;/a&gt; (which I avoid these days since it tends to get updated too slowly for a lot of projects).&lt;/p&gt;

&lt;p&gt;First get a copy of &lt;a href=&quot;http://www.ijg.org/&quot;&gt;libjpeg&lt;/a&gt; and build it as a shared library:&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;curl &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; http://www.ijg.org/files/jpegsrc.v7.tar.gz
&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-zxf&lt;/span&gt; jpegsrc.v7.tar.gz
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;jpeg-7/
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;which glibtool&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; ./libtool
./configure &lt;span class=&quot;nt&quot;&gt;--enable-shared&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/local
make
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now get a copy of ImageMagick:&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;curl &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-zxf&lt;/span&gt; ImageMagick.tar.gz
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;ImageMagick-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;/
./configure &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/local
make
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now you should have convert, mogrify, montage or whatever other ImageMagick tools you were interested in. &lt;a href=&quot;http://rmagick.rubyforge.org/&quot;&gt;RMagick&lt;/a&gt; seems to install just fine after you do this, though I couldn’t get &lt;a href=&quot;http://nubyonrails.com/pages/gruff&quot;&gt;gruff&lt;/a&gt; working because of what looks like a dependency on &lt;a href=&quot;http://www.ghostscript.com/&quot;&gt;GhostScript&lt;/a&gt;. I’ll update this post once I get that worked out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (8/26)&lt;/strong&gt;: Tweaked some of the commands here to account for new versions of libjpeg and ImageMagick. Thanks for the heads up, Amed!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>What I learned about Cookies this week</title>
  <link href="https://matschaffer.com/tech/2009/05/09/what-i-learned-about-cookies-this-week.html"/>
  <updated>2009-05-09T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/05/09/what-i-learned-about-cookies-this-week</id>
  <content type="html">&lt;p&gt;A lot of people reading this are probably using &lt;a href=&quot;http://www.rubyonrails.org&quot;&gt;frameworks&lt;/a&gt; that have decent session storage and don’t often need to worry about cookies. But over at &lt;a href=&quot;http://www.comcast.net&quot;&gt;&lt;span class=&quot;caps&quot;&gt;CIM&lt;/span&gt;&lt;/a&gt; the thought of session state for many tens of millions of requests per day is daunting. As a result we use a lot of cookies. Probably too many, but that’s a story for another day.&lt;/p&gt;

&lt;p&gt;Ever read the &lt;a href=&quot;http://www.faqs.org/rfcs/rfc2965.html&quot;&gt;&lt;span class=&quot;caps&quot;&gt;RFC&lt;/span&gt; for cookies&lt;/a&gt;? Neither had I. I probably wouldn’t have either until our company decided to standardize on &lt;a href=&quot;http://tomcat.apache.org&quot;&gt;Apache Tomcat&lt;/a&gt; for our application containers. What we thought would be an easy conversion turned out to take a few days time because some of our important cookies apparently violate the &lt;span class=&quot;caps&quot;&gt;RFC&lt;/span&gt;, but no browser or web server had complained yet. What rules did we break?&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;The left hand side of a cookie definition can’t contain an ’@’ symbol (along with some others)&lt;/li&gt;
	&lt;li&gt;The right hand side of a cookie can’t contain ’=’ symbols, so no unescaped key=value pairs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;span class=&quot;caps&quot;&gt;RFC&lt;/span&gt; is a lot to wade through, but thanks to some information from &lt;a href=&quot;https://issues.apache.org/jira/browse/WICKET-1834&quot;&gt;Tomcat’s issue tracker&lt;/a&gt; it was made pretty clear. Basically a cookie definition boils down to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
   av-pairs    =     av-pair *(&quot;;&quot; av-pair)
   av-pair     =     attr [&quot;=&quot; value]              ; optional value
   attr        =     token
   value       =     token | quoted-string
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Where the definition for token and quoted-string are provided in the &lt;a href=&quot;http://www.faqs.org/rfcs/rfc2616.html&quot;&gt;&lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt;/1.1 spec.&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
    token          = 1*&amp;lt;any CHAR except CTLs or separators&amp;gt;
    separators     = &quot;(&quot; | &quot;)&quot; | &quot;&amp;lt;&quot; | &quot;&amp;gt;&quot; | &quot;@&quot;
                   | &quot;,&quot; | &quot;;&quot; | &quot;:&quot; | &quot;\&quot; | &amp;lt;&quot;&amp;gt;
                   | &quot;/&quot; | &quot;[&quot; | &quot;]&quot; | &quot;?&quot; | &quot;=&quot;
                   | &quot;{&quot; | &quot;}&quot; | SP | HT

    quoted-string  = ( &amp;lt;&quot;&amp;gt; *(qdtext | quoted-pair ) &amp;lt;&quot;&amp;gt; )
    qdtext         = &amp;lt;any TEXT except &amp;lt;&quot;&amp;gt;&amp;gt;
    quoted-pair    = &quot;\&quot; CHAR
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So there you have it. And most web servers and browsers don’t seem to complain. But if you ever find yourself looking at your server logs or a debugger wondering where you cookies went or why they’re truncated. Then it might be time to double check the &lt;span class=&quot;caps&quot;&gt;RFC&lt;/span&gt;.&lt;/p&gt;

&lt;p&gt;Oh, and the fix? Well ideally we’ll fix our cookies to comply with the &lt;span class=&quot;caps&quot;&gt;RFC&lt;/span&gt;, but that will take some time since we don’t control the creation of all the cookies in question. So the plan for now is to patch Tomcat’s Cookie class to be a bit less strict when it’s parsing the request.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>iSepta Train View, my time with Dashcode</title>
  <link href="https://matschaffer.com/tech/2009/03/29/isepta-train-view.html"/>
  <updated>2009-03-29T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/03/29/isepta-train-view</id>
  <content type="html">&lt;p&gt;&lt;img class=&quot;left&quot; title=&quot;iSepta Train View&quot; src=&quot;/files/iSeptaTrainView.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; New version 1.1! &lt;a href=&quot;http://cloud.github.com/downloads/matschaffer/isepta-train-view.wdgt/iSepta_Train_View-1.1.zip&quot;&gt;Download it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE 2:&lt;/strong&gt; Github now has issue tracking! So I’m dropping uservoice. If you have ideas, submit them &lt;a href=&quot;http://github.com/matschaffer/isepta-train-view.wdgt/issues&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So over the past month I’ve been working with &lt;a href=&quot;http://developer.apple.com/tools/dashcode/&quot;&gt;Dashcode&lt;/a&gt; trying to come up with a nice representation of &lt;a href=&quot;http://septa.org&quot;&gt;Septa&lt;/a&gt; train data. I’ve managed to come up with a simple widget that combines the data from &lt;a href=&quot;http://isepta.org&quot;&gt;iSepta&lt;/a&gt; with the ontime/late data available from Septa’s &lt;a href=&quot;http://trainview.septa.org&quot;&gt;Train View&lt;/a&gt; page. So far it seems to be working well. I’ve submitted the widget to &lt;a href=&quot;http://www.apple.com/downloads/dashboard/&quot;&gt;Apple downloads&lt;/a&gt; and &lt;a href=&quot;http://www.dashboardwidgets.com/showcase/details.php?wid=2413&quot;&gt;Dashboard Widgets&lt;/a&gt; so hopefully you’ll see it show up there soon too. So &lt;a href=&quot;http://cloud.github.com/downloads/matschaffer/isepta-train-view.wdgt/iSepta_Train_View-1.1.zip&quot;&gt;&lt;strong&gt;download&lt;/strong&gt; your copy&lt;/a&gt; and &lt;a href=&quot;http://github.com/matschaffer/isepta-train-view.wdgt/issues&quot;&gt;let me know what you think&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;As far as working with Dashcode goes, it’s been an interesting experience. I’m still a little lukewarm on it, but it definitely helped with the visual work. I’m not much of a designer, so having Dashcode’s gradient, rounded corner and “glass” effect helpers was perfect for me. It was also nice not having to hand-code the “i” button animation like I did with &lt;a href=&quot;http://matschaffer.com/projects/#dwc&quot;&gt;Digital World Clock&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the downside of things, I felt like the HTML and javascript generation over-complicated things in some cases. Dashcode provides “Parts” like labels, lists and indicators which are cool but the way you interact with them is a little odd. For example, to add items to a list part you’d do this:&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;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setDataArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;trainList&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;Which isn’t terrible, but felt like more work than just manipulating a plain old “ul” with jQuery. Granted this specialized interface does also offer streaming-friendly setters, which could be useful for some folks. My bigger gripe is with the labels that get generated like this in the HTML:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;apple-part=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.apple.Dashcode.part.text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apple-text apple-no-children&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;apple-default-image-visibility=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hidden&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;apple-text-overflow=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ellipsis&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nextTrainLabel&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;apple-style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;part-height-dependent: true;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note the lack of contents. They get wired at run time by javascript. My guess is that this makes localization more accessible, but I wonder how many widgets really get localized.&lt;/p&gt;

&lt;p&gt;These sort of things made me hesitant to muck around with the HTML and CSS which felt a little limiting when building the widget. But the graphics helpers and quick access to functions like “deploy to dashboard” were definitely nice. And the javascript debugging support felt even more solid than firebug. So all-in-all I’m a happy camper.&lt;/p&gt;

&lt;p&gt;One more thing to be careful about if you’re working with Dashcode: when saving your project, Dashcode will regenerate most of the files and directories and blow away files that haven’t been imported into the project. So be careful not to save things under the dcproj bundle or you might lose them. I would bet this applies to .svn folders as well, but all the cool kids are using &lt;a href=&quot;http://git-scm.com&quot;&gt;git&lt;/a&gt; nowadays anyway, right?&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Removing code is a feature</title>
  <link href="https://matschaffer.com/tech/2009/03/03/removing-code-is-a-feature.html"/>
  <updated>2009-03-03T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/03/03/removing-code-is-a-feature</id>
  <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.thinkgeek.com/homeoffice/gear/a639/images/&quot;&gt;&lt;img title=&quot;Delete&quot; src=&quot;https://www.thinkgeek.com/images/products/additional/large/delete_key_eraser_inhand.jpg&quot; alt=&quot;&quot; width=&quot;400&quot; height=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I saw a beautiful thing this evening in the &lt;a href=&quot;http://blog.zenspider.com/2009/03/zentest-version-400-has-been-r.html&quot;&gt;ZenTest 4.0.0 release notes&lt;/a&gt; (emphasis mine):&lt;/p&gt;

&lt;blockquote&gt;2 minor enhancements:
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Deleted&lt;/strong&gt; autotest/screen - releasing as a separate gem soon.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deleted&lt;/strong&gt; test-rails and rails&lt;em&gt;test&lt;/em&gt;audit.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Removing unnecessary code has always given me a &lt;em&gt;warm fuzzy&lt;/em&gt; feeling.&lt;/p&gt;

&lt;p&gt;In today’s world of &lt;a href=&quot;http://www.apple.com/macosx/features/timemachine.html&quot;&gt;backups&lt;/a&gt;, &lt;a href=&quot;http://git-scm.com/&quot;&gt;version control&lt;/a&gt; and &lt;a href=&quot;http://maven.apache.org&quot;&gt;dependency management&lt;/a&gt; we need to learn to be &lt;strong&gt;ruthless&lt;/strong&gt; with the delete key.&lt;/p&gt;

&lt;p&gt;Don’t comment it out. Don’t talk yourself into thinking you’ll use it later. &lt;strong&gt;Just delete it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is one of the same principals behind the &lt;em&gt;&lt;a href=&quot;http://www.43folders.com/2006/03/14/delete&quot;&gt;inbox zero&lt;/a&gt;&lt;/em&gt; concept and it is just as applicable to code. Nice work, &lt;a href=&quot;http://www.zenspider.com/ZSS/Products/ZenTest/&quot;&gt;ZenTest&lt;/a&gt;. We should get you a &lt;a href=&quot;http://nerdmeritbadges.com/&quot;&gt;merit badge&lt;/a&gt;.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>The Creep of Asynchronicity</title>
  <link href="https://matschaffer.com/tech/2009/02/19/the-creep-of-asynchronicity.html"/>
  <updated>2009-02-19T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2009/02/19/the-creep-of-asynchronicity</id>
  <content type="html">&lt;p&gt;I’ve been doing a fair bit of JavaScript at work lately and while I really enjoy the language there’s something that’s been nagging at me which I’ve come to call &lt;strong&gt;The Creep of Asynchronicity&lt;/strong&gt;. I’ll use some of the examples I’ve been working with to illustrate the idea.&lt;/p&gt;

&lt;p&gt;Let’s start with a function to get all the videos on a device that looks like this:&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;nx&quot;&gt;videos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getVideos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;doSomethingWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;videos&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;em&gt;Simple&lt;/em&gt;. Pretty much what you would expect in a “device” API, right? But now let’s assume that getVideos() requires a return trip to the web server to get the data. You have two options:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Fly in the face of AJAX, make the call synchronously and lock up your UI.&lt;/li&gt;
  &lt;li&gt;Change the API to look something like this:&lt;/li&gt;
&lt;/ol&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;nx&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getVideos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;videos&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;doSomethingWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;videos&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;Now that’s not so bad, but consider the case where you also need an asynchronous call to get the device object. Now we have:&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;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getDevice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;device&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;nx&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getVideos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;videos&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;doSomethingWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;videos&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;&lt;em&gt;Ew…&lt;/em&gt; getting kinda nasty there isn’t it? Imagine if we also had to make an asynchronous call to get some details on the videos, and so on.&lt;/p&gt;

&lt;p&gt;So the crux of my argument is that without some sort of &lt;strong&gt;synchronization mechanism&lt;/strong&gt;, any introduction of an asynchronous call requires that the whole application become asynchronous to support it. Even just a &lt;strong&gt;sleep&lt;/strong&gt; function would help, but JavaScript has nothing of the sort. The best I’ve come up with so far is recursion, which helps in the event that your asychronous calls are acting upon a list of items uniformly. I used this to load a series of JavaScript libraries like so:&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;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;loadScripts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;libraries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&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;libraries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;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;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lib&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;libraries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;jQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getScript&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&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;loadScripts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;libraries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&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;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;callback&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;span class=&quot;nf&quot;&gt;loadScripts&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;/user.js&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;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/device.js&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;span class=&quot;kd&quot;&gt;function&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;doStuffOnceScriptsHaveLoaded&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;This could conceivably be used to recurse down a list of asynchronous callbacks (which I did find on the web somewhere but seem to find it at the moment). But I think this might lead to an even nastier looking API.&lt;/p&gt;

&lt;p&gt;I’m sure on some level, this just requires a shift in thinking. But there’s a part of me that still longs for that two liner at the top of this post… &lt;em&gt;sigh&lt;/em&gt;&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Groovy Unit Tests on Maven &amp; Eclipse</title>
  <link href="https://matschaffer.com/tech/2008/12/27/groovy-unit-tests-on-maven-eclipse.html"/>
  <updated>2008-12-27T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/12/27/groovy-unit-tests-on-maven-eclipse</id>
  <content type="html">&lt;p&gt;During our last sprint at work I got a good chance to get &lt;a href=&quot;http://groovy.codehaus.org/&quot;&gt;Groovy&lt;/a&gt; working for our unit tests. It took a bit of googling and trial and error, so hopefully this post will benefit others trying to do the same thing. Here’s what I did:&lt;/p&gt;

&lt;p&gt;Set up your pom with the following dependency:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.groovy&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;groovy-all&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.6-beta-2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class=&quot;nt&quot;&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the gmaven plugin under the build section:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.groovy.maven&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;gmaven-plugin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0-rc-3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;extensions&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/extensions&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;testCompile&lt;span class=&quot;nt&quot;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;sources&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;fileset&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;${pom.basedir}/src/test/java&lt;span class=&quot;nt&quot;&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
              &lt;span class=&quot;nt&quot;&gt;&amp;lt;include&amp;gt;&lt;/span&gt;**/*.groovy&lt;span class=&quot;nt&quot;&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/fileset&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/sources&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note that I keep my tests under &lt;code&gt;src/test/java&lt;/code&gt;, this avoids needing extra configuration from inside eclipse. &lt;code&gt;src/test/groovy&lt;/code&gt; also works, but you’ll need to mark it as a source folder, and it seems to be a little flaky so I stopped that.&lt;/p&gt;

&lt;p&gt;At this point you should be able to run your groovy tests with &lt;code&gt;mvn clean install&lt;/code&gt;. Or use &lt;code&gt;-Dtest=MyGroovyTest&lt;/code&gt; to run a single test.&lt;/p&gt;

&lt;p&gt;For eclipse you’ll need the &lt;a href=&quot;http://groovy.codehaus.org/Eclipse+Plugin&quot;&gt;GroovyFeature&lt;/a&gt; installed. I currently have version 1.5.6 installed.&lt;/p&gt;

&lt;p&gt;Close your eclipse project and rebuild the configuration with &lt;code&gt;mvn eclipse:clean eclipse:eclipse&lt;/code&gt;. Open the project back up.  You may need to fix the JRE settings for the project in the java build properties. On my machine maven tries to hook the project to a JRE other than the default, compile errors occur, switching back to the default Mac OS JRE resolves that.&lt;/p&gt;

&lt;p&gt;Right click on the project root, go to “Groovy -&amp;gt; Add Groovy Nature”.  If you use the eclipse maven plugin, don’t turn it on for this project. Enabling maven dependency management doesn’t seem to jive with the eclipse groovy plugin. If I turn it on, I get lots of class not found type errors and have to do &lt;code&gt;eclipse:clean eclipse:eclipse&lt;/code&gt; and start over again.&lt;/p&gt;

&lt;p&gt;Right click your .groovy test (a class that inherits from groovy.util.GroovyTestCase) and you should have Run As -&amp;gt; JUnit test available.  I have my setUp and test methods as &lt;code&gt;void&lt;/code&gt;.  I used &lt;code&gt;def&lt;/code&gt;s originally, but this caused the methods to be ignored.&lt;/p&gt;

&lt;p&gt;If you don’t see JUnit available, try cleaning the project. I’ve even turned off the automatic builds and manually ran “Build All” on occasion. Also watch out for compile errors in the groovy files.  Eclipse doesn’t reliably warn about it and I’ve seen compilation just silently fail. When in doubt have maven try to run it. Eclipse doesn’t always reliably build the groovy classes, and I often have to clean the compiled classes especially when adding new groovy methods or test functions. I’d say about 75% of the time it just runs.&lt;/p&gt;

&lt;p&gt;Dan Schaffer on the groovy mailing list also mentioned noted that refactoring Java classes doesn’t seem to apply to Groovy files. There is &lt;a href=&quot;http://groovy.ifs.hsr.ch/trac/GroovyRefactoring/wiki&quot;&gt;a plugin&lt;/a&gt; for that, but I haven’t tried it yet.&lt;/p&gt;

&lt;p&gt;As usual, feel free to comment if you have any questions about my setup. Hope you find this useful.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>GTAC2008 Videos now available</title>
  <link href="https://matschaffer.com/tech/2008/12/12/gtac2008-videos-now-available.html"/>
  <updated>2008-12-12T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/12/12/gtac2008-videos-now-available</id>
  <content type="html">&lt;p&gt;Rather than continue my review/rant about GTAC2008, I think I’ll let you just decide for yourself since the videos are &lt;a href=&quot;http://googletesting.blogspot.com/2008/12/posted-by-lydia-ash-gtac-conference.html&quot;&gt;now available.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many of them are less-than-interesting, but the following are probably worth a look.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Boosting Your Testing Productivity with Groovy – Andres Almiray&lt;/strong&gt;&lt;br /&gt;
  I’ve been doing a lot of this stuff with Groovy lately, more detail to follow.
    &lt;ul&gt;
      &lt;li&gt;Video: &lt;a href=&quot;http://www.youtube.com/watch?v=UvWTfVCWKJY&quot;&gt;http://www.youtube.com/watch?v=UvWTfVCWKJY&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Slides: &lt;a href=&quot;http://www.slideshare.net/aalmiray/gtac-boosting-your-testing-productivity-with-groovy/&quot;&gt;http://www.slideshare.net/aalmiray/gtac-boosting-your-testing-productivity-with-groovy/&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The New Genomics: Software Development at Petabyte Scale – Matt Wood&lt;/strong&gt;&lt;br /&gt;It’s not about testing, but it’s a good talk.
    &lt;ul&gt;
      &lt;li&gt;Video: &lt;a href=&quot;http://www.youtube.com/watch?v=A64WKH9gNI8&quot;&gt;http://www.youtube.com/watch?v=A64WKH9gNI8&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Slides part 1: &lt;a href=&quot;http://docs.google.com/Presentation?id=dczwht9g_3318qqfb6f5&quot;&gt;http://docs.google.com/Presentation?id=dczwht9g_3318qqfb6f5&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Slides part 2: &lt;a href=&quot;http://docs.google.com/Presentation?id=dczwht9g_393d7zg4xcm&quot;&gt;http://docs.google.com/Presentation?id=dczwht9g_393d7zg4xcm&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Automated Model-Based Testing of Web Applications – Atif M. Memon and Oluwaseun Akinmade&lt;/strong&gt;&lt;br /&gt;Nothing here that you can go and download just yet, but very interesting research.
    &lt;ul&gt;
      &lt;li&gt;Video: &lt;a href=&quot;http://www.youtube.com/watch?v=6LdsIVvxISU&quot;&gt;http://www.youtube.com/watch?v=6LdsIVvxISU&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Slides: &lt;a href=&quot;http://www.cs.umd.edu/~atif/GTAC08/&quot;&gt;http://www.cs.umd.edu/~atif/GTAC08/&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Atom Publishing Protocol: Testing your Server Implementation – David Calavera&lt;/strong&gt;&lt;br /&gt;This gist of this one is really just: check out &lt;a href=&quot;http://www.tbray.org/ape/&quot;&gt;Ape&lt;/a&gt;, it’s cool. But I think David should also be given a lot of credit for giving a pretty solid talk despite it being his first talk in English. Nice work, David!
    &lt;ul&gt;
      &lt;li&gt;Video: &lt;a href=&quot;http://www.youtube.com/watch?v=uRmWTfT91uQ&quot;&gt;http://www.youtube.com/watch?v=uRmWTfT91uQ&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Slides: &lt;a href=&quot;http://thinkincode.net/gtac_atomPub_testing_your_server_implementation.pdf&quot;&gt;http://thinkincode.net/gtac_atomPub_testing_your_server_implementation.pdf&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;The Future of Testing&lt;/em&gt; by James A. Whittaker is also probably worth a look, but the video is still listed as “coming soon”, so I’ll link to that when it’s available.&lt;/p&gt;

&lt;p&gt;I’ll discontinue my GTAC2008 recap unless anyone is really dying to hear more of my ranting. More blog posts on my Groovy work to follow.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Dude your PubNite was totally epic!</title>
  <link href="https://matschaffer.com/tech/2008/11/18/pubnite-was-totally-epic.html"/>
  <updated>2008-11-18T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/11/18/pubnite-was-totally-epic</id>
  <content type="html">&lt;p&gt;I felt a blog post was in order to commemorate a totally epic 2 year anniversary for &lt;a href=&quot;http://phillyonrails.org&quot;&gt;PhillyOnRails PubNite&lt;/a&gt;.  We had quite the turnout last night, definitely upwards of 20 people!&lt;/p&gt;

&lt;p&gt;Colin took some photos, so I’ll lean on him to get them up on &lt;a href=&quot;http://www.flickr.com/photos/colinandangie/&quot;&gt;his flickr account&lt;/a&gt; soon.&lt;/p&gt;

&lt;p&gt;A big thank you to everyone that came out and I’m really looking forward to next month!&lt;/p&gt;
</content>
</entry>

<entry>
  <title>My new X-Plat strategy</title>
  <link href="https://matschaffer.com/tech/2008/11/05/my-new-x-plat-strategy.html"/>
  <updated>2008-11-05T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/11/05/my-new-x-plat-strategy</id>
  <content type="html">&lt;p&gt;Thanks to a strong collaborative effort between myself and one of the members of &lt;a href=&quot;http://twitter.com/mofro&quot; rel=&quot;friend coworker&quot;&gt;our front end team&lt;/a&gt;. I’m pleased to announce my new cross-platform strategy for glyph management.&lt;/p&gt;

&lt;p&gt;It’s capable of operating under a variety of modes including that of a 6, 9 or lower-case ‘e’. This new initiative will be the focus of my coming year.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center&quot; title=&quot;X-Plat&quot; src=&quot;//farm4.static.flickr.com/3172/3006575668_73dc65b020.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;(How’s that, &lt;a href=&quot;http://paradox1x.org&quot; rel=&quot;friend coworker&quot;&gt;Karl&lt;/a&gt;?)&lt;/p&gt;
</content>
</entry>

<entry>
  <title>GTAC 2008 Recap - Part 2</title>
  <link href="https://matschaffer.com/tech/2008/10/29/gtac-2008-recap-part-2.html"/>
  <updated>2008-10-29T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/10/29/gtac-2008-recap-part-2</id>
  <content type="html">&lt;h4&gt;Advances in Automated Software Technologies, Elfriede Dustin and Marcus Borch&lt;/h4&gt;

&lt;p&gt;This started with focus on applying hardware testing techniques to software testing. Namely with regards to reusable components (citing &lt;a href=&quot;http://koders.com&quot;&gt;koders.com&lt;/a&gt; and &lt;a href=&quot;http://krugle.com&quot;&gt;krugle.com&lt;/a&gt;). The components in question seemed to largely be toolkits that they use in testing including&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://staf.sourceforge.net&quot;&gt;&lt;span class=&quot;caps&quot;&gt;STAX&lt;/span&gt;/STAF&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Fire eye – for test data generation&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.vncrobot.com&quot;&gt;&lt;span class=&quot;caps&quot;&gt;VNC&lt;/span&gt; Robot&lt;/a&gt; – for noninvasive testing&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.logwatch.org&quot;&gt;logwatch&lt;/a&gt;, &lt;a href=&quot;http://sourceforge.net/projects/lofimo/&quot;&gt;LoFiMo&lt;/a&gt; – for log monitoring&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://wiki.eclipse.org/index.php/Rich_Client_Platform&quot;&gt;Rich client platform&lt;/a&gt; (in Eclipse) – for test management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beyond the tools mentioned, the bulk of the presentation on a system that they built for generating junit test cases for either C++ or Java logic from excel sheets (the logic translation was done with &lt;span class=&quot;caps&quot;&gt;ANTLR&lt;/span&gt;).  The test spreadsheet was pounded into &lt;span class=&quot;caps&quot;&gt;XML&lt;/span&gt; then into Java then run as tests. When asked why they don’t just interpret the spreadsheet directly they gave an answer regarding the DoD’s requirements for transparency.  The whole thing seemed horribly static to me, but I suppose if your client is our government you probably have to jump through a variety of hoops.&lt;/p&gt;

&lt;h4&gt;Boosting Your Testing Productivity with Groovy, Andres Almiray&lt;/h4&gt;

&lt;p&gt;This talk presented a few things about &lt;a href=&quot;http://groovy.codehaus.org&quot;&gt;Groovy&lt;/a&gt; that I hadn’t heard yet.  First that Groovy supports TestNG out of the box, and also that most Java code can run in the Groovy interpreter without modification. The only java construct which Groovy doesn’t support is inner classes (I’m guessing cause they look like closures).&lt;/p&gt;

&lt;p&gt;Andres showed a number of the tricks Groovy is capable of such as closures, mocking and stubbing. It seemed pretty useful that Groovy can also stub existing Java classes and also can masquerade simple hashes as java objects, providing a pretty flexible mocking situation.  The syntax was something like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;proxyString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;startsWith&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;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&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;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The proxyString object will now pass for a String so long as you don’t try to call any methods other than startsWith.&lt;/p&gt;

&lt;p&gt;According to Andres, this can also all be dropped into an existing JUnit or TestNG infrastructure by means of eclipse and maven plugins. So I’m thinking my next free-time project might be to rewrite some of comcast.net’s unit tests in Groovy to see just how well it will blend. He also discussed using Groovy with DbUnit for testing databases as well as using it with Easyb for behavior driven development. And also one of participants brought up &lt;a href=&quot;http://code.google.com/p/aost/wiki/Tutorial&quot;&gt;Tellurium&lt;/a&gt;, which apparently uses Groovy to drive selenium.&lt;/p&gt;

&lt;p&gt;This was definitely the best overall talk so far. I still don’t think I could support Groovy over Ruby for general purpose development, but it seems to have a solid home for testing Java applications.  I look forward to trying this out on my work projects in the next week or two.&lt;/p&gt;

&lt;h4&gt;Lunch talks&lt;/h4&gt;

&lt;p&gt;Google made the smart move of providing lunch at the conference so we could all stay together.  I sat with developers from a number of different companies and picked up on some cool technologies like &lt;a href=&quot;http://www.cs.waikato.ac.nz/~marku/mbt/modeljunit/&quot;&gt;ModelJUnit&lt;/a&gt; and also got a demo of a unit test correlation webapp that Todd Wells was working on. I even got an introduction to the micro-finance site, &lt;a href=&quot;http://kiva.org&quot;&gt;Kiva&lt;/a&gt; which I could definitely see throwing some money at.&lt;/p&gt;

&lt;p&gt;I hope you’re enjoying my &lt;span class=&quot;caps&quot;&gt;GTAC 2008&lt;/span&gt; overview so far. I expect it will take about 3-4 more posts to get it all up here. Feel free to comment if you’d like to see more or less of my own personal commentary, or for any other reason.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>GTAC 2008 Restrospective</title>
  <link href="https://matschaffer.com/tech/2008/10/26/gtac-2008-restrospective.html"/>
  <updated>2008-10-26T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/10/26/gtac-2008-restrospective</id>
  <content type="html">&lt;p&gt;I traveled to Seattle last week for this year’s &lt;a href=&quot;http://googletesting.blogspot.com/2008/04/gtac-2008-in-seattle.html&quot;&gt;Google Testing and Automation Conference&lt;/a&gt;. I tried to take some  notes for the benefit of my own memory and others, so I’ll be posting notes and reactions to my blog over the next few days starting with this post.&lt;/p&gt;

&lt;p&gt;As for a quick overview of this year’s conference, here it is: the attendees were great, but many of the presentations were really weak.&lt;/p&gt;

&lt;p&gt;The videos from previous conferences had me really excited for this conference, but about half of this year’s presentations were pretty lame. And the final presentation was outright appalling. So much so that I’m still having a hard time letting go of my frustration about it, but more on that later.&lt;/p&gt;

&lt;p&gt;I have no way to know just what went wrong, but I really hope Google can learn from these talks and try to come up with a better offering next year. Or perhaps we can all learn from this and step up to the plate with our own presentations for next year. Either way, I’m still glad I went. I just wish I had spent more time outside the presentation hall. So without further rant, here’s my review of the first talk:&lt;/p&gt;
&lt;h4&gt;The Future of Testing, James A Whittaker&lt;/h4&gt;
&lt;p&gt;James opened the conference with a strong message about the magic that software makes possible in the world, including references to &lt;a href=&quot;http://planetquest.jpl.nasa.gov&quot;&gt;the search for earth-like planets&lt;/a&gt; and &lt;a href=&quot;http://well.blogs.nytimes.com/2008/02/28/the-language-of-autism&quot;&gt;understanding autism&lt;/a&gt;. He then showed us a somewhat cheesy video of &lt;a href=&quot;http://www.youtube.com/watch?v=6F1u36Y-qlE&quot;&gt;Microsoft’s vision for the future&lt;/a&gt; of software and followed it up with the very real prospect of “but how will we test it?” He then outlined his vision for the future of testing which was drawn from some of his &lt;a href=&quot;http://blogs.msdn.com/james_whittaker/default.aspx&quot;&gt;previous blogs entries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first part he presented was a vision of thousands of virtualized environments and bundled tests such that tests for a given domain and be purchased along with the environments to run them on. I mostly agreed with the idea, but couldn’t help but wonder: Should environmental software bugs really be addressed with a brute-force testing effort across all possible environments? Or should we work in reducing the number of environments that can effect software through proper encapsulation. Linux and Unix systems reduce the number of environments by limiting communication between components such that crashes generally only effect one corner of the total system. Apple reduces the number of environments by only supporting a handful of custom hardware. I’m sure the right answer varies depending on the situation, but I doubt tens of thousands of commodity virtual machines will really cut it.&lt;/p&gt;

&lt;p&gt;Mr. Whittaker’s second point was visualization. In most other forms of engineering you can clearly look at the components that make up a system. But even fairly simple software systems are too complex to easily visualize. He proposed that we focus more on visualizing software for the purposes of testing and showed one of the tools that he had used at Microsoft. I can’t find a link to this tool anywhere but basically it showed a tree map of the Windows components with size indicating the size of the component and color intensity indicating code complexity. If anyone has any ideas where I can find a freely available version of this tool, please let me know cause it looked pretty cool. He mentioned that one of the &lt;a href=&quot;http://www.csc2.ncsu.edu/conferences/issre/2008/&quot;&gt;ISSRE 2008&lt;/a&gt; chairs had worked on it, but I can’t figure out which one just yet. I might just have to talk to &lt;a href=&quot;http://www.arpitonline.com&quot;&gt;Arpit&lt;/a&gt; on this one a bit since I’m sure it wouldn’t be too hard for him to come up with such a visualization given his &lt;a href=&quot;http://digggraphr.arpitonline.com&quot;&gt;previous experience in tree maps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So that’s one down and about 12 to go. Stay tuned coverage on the rest of the talks and I’ll also let you know as soon as I see that the videos are available from google.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Fork my bash profile</title>
  <link href="https://matschaffer.com/tech/2008/10/02/fork-my-bash-profile.html"/>
  <updated>2008-10-02T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/10/02/fork-my-bash-profile</id>
  <content type="html">&lt;p&gt;Some of you may remember my &lt;a href=&quot;http://schapht.tumblr.com/post/27522735/if-your-profile-is-getting-too-long&quot;&gt;tumblr post&lt;/a&gt; about using a directory of configuration files in place of the standard single .profile script for setting up a Unix environment. I’ve since refined the idea even more and started working on generalizing it for use across multiple operating systems. And to make it even more fun I’ve started publishing it to github.&lt;/p&gt;

&lt;p&gt;I have a number of tricks in there like tab-completion for both ssh hosts and rake tasks, and prompts that react to error conditions. If you’re interested just clone my repository as ~/.profile.d and link it like you see in this gist:&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;mv&lt;/span&gt; ~/.profile ~/.profile.old
git clone git://github.com/matschaffer/profile.git ~/.profile.d
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; ~/.profile.d/init ~/.profile&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Or if you have some of your own ideas, just fork my project and have fun! The url for the repository browser is: &lt;a href=&quot;http://github.com/matschaffer/profile/tree/master&quot;&gt;http://github.com/matschaffer/profile/tree/master&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know what you think. I’d love to incorporate other tricks that people might have.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>More work on Corundum</title>
  <link href="https://matschaffer.com/tech/2008/09/09/more-work-on-corundum.html"/>
  <updated>2008-09-09T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/09/09/more-work-on-corundum</id>
  <content type="html">&lt;p&gt;So last night I did a little more work on &lt;a href=&quot;http://github.com/matschaffer/corundum&quot;&gt;Corundum&lt;/a&gt; (still not totally satisfied with the name). I managed to get most of the pom stuff and JRuby dependency packaged. Now any project that uses it just needs &lt;a href=&quot;http://github.com/matschaffer/corundum/tree/master/testapp/pom.xml&quot;&gt;this simple pom file&lt;/a&gt; and &lt;a href=&quot;http://github.com/matschaffer/corundum/tree/master/testapp/src/main/assembly/jruby-complete.xml&quot;&gt;this assembly file&lt;/a&gt;. The assembly file is still a thorn in my side and hopefully I can figure out how to get it out of there. I’m planning to look at the &lt;a href=&quot;http://code.google.com/p/flex-mojos/&quot;&gt;flex-mojos&lt;/a&gt; project to see how they implement the swc packaging, maybe there’s an answer there.&lt;/p&gt;

&lt;p&gt;I feel like this exercise has taught me just how inextensible maven really is. Maybe I’m doing it wrong but packaging this experiment for distribution has resulted in 2 sub-projects: one to set up the dependencies and assembly plugin and another to package the &lt;a href=&quot;http://github.com/matschaffer/corundum/tree/master/runner/src/main/java/com/cimians/code/corundum/Corundum.java&quot;&gt;jruby runner class&lt;/a&gt;. And now I fear to think what it will take to release this. I might have to try doing something similar with rake or &lt;a href=&quot;http://incubator.apache.org/buildr/&quot;&gt;buildr&lt;/a&gt; as well just for comparison’s sake.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>An experiment with Maven and JRuby</title>
  <link href="https://matschaffer.com/tech/2008/08/30/an-experiment-with-maven-and-jruby.html"/>
  <updated>2008-08-30T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/08/30/an-experiment-with-maven-and-jruby</id>
  <content type="html">&lt;p&gt;So in following with &lt;a href=&quot;http://www.matschaffer.com/2008/07/bdb-je-in-jruby/&quot;&gt;my last JRuby post&lt;/a&gt;, this weekend I came up with the next piece to the puzzle; how to use maven to build a self-contained jar file that runs ruby scripts. It’s still pretty raw and lacking an interesting name, but I threw my test code up on &lt;a href=&quot;http://github.com/matschaffer/corundum/tree/master&quot;&gt;github&lt;/a&gt; for now to assist in its evolution. If you’re interested in trying it out do the following:&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;git clone git://github.com/matschaffer/corundum.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;corundum/
mvn clean &lt;span class=&quot;nb&quot;&gt;install
cd &lt;/span&gt;testapp/
mvn clean &lt;span class=&quot;nb&quot;&gt;install
&lt;/span&gt;java &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; target/testapp-0.0.1-SNAPSHOT-jruby-complete.jar&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With luck, I’ll have this abstracted somehow so you don’t have to really invade your project, but rather can just include an appropriate pom.xml and be on your way. Maybe some of the friendly folks in &lt;a href=&quot;irc://irc.codehaus.org#maven&quot;&gt;#maven&lt;/a&gt; can help me out. And once it’s more polished, expect a release on &lt;a href=&quot;http://code.cimians.com&quot;&gt;code.cimians.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Consider this one more step in my plan to subvert the java-centric development world. And now I’ll be accompanied by a small army of ruby scripts wearing jar-file disguises.&lt;/p&gt;
</content>
</entry>

<entry>
  <title>Automatic iSync</title>
  <link href="https://matschaffer.com/tech/2008/07/26/automatic-isync.html"/>
  <updated>2008-07-26T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/07/26/automatic-isync</id>
  <content type="html">&lt;p&gt;I got this from &lt;a href=&quot;http://hohle.net/scrap_post.php?post=217&quot;&gt;hohle&lt;/a&gt;, but the formatting on Jon’s post is less-than-ideal. So here it is, reposted for easy copy/paste:&lt;/p&gt;

&lt;p&gt;Download his applescript &lt;a href=&quot;http://hohle.net/downloads/AutoSync.scpt&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And here’s the plist:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot;
 &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Disabled&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;false/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;local.isync.sync&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;LowPriorityIO&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Nice&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;osascript&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;
          /Users/schapht/Library/Scripts/AutoSync.scpt
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardErrorPath&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/dev/null&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardOutPath&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/dev/null&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StartInterval&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;1800&lt;span class=&quot;nt&quot;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Just make sure to change your plist to point to wherever you keep AuthSync.scpt. Then save it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/LaunchAgents/local.isync.sync.plist&lt;/code&gt; and run the following from your Terminal:&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;launchctl load ~/Library/LaunchAgents/local.isync.sync.plist
launchctl start local.isync.sync&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
</entry>

<entry>
  <title>BerkelyDB Java Edition in JRuby</title>
  <link href="https://matschaffer.com/tech/2008/07/20/bdb-je-in-jruby.html"/>
  <updated>2008-07-20T00:00:00+00:00</updated>
  <id>https://matschaffer.com/tech/2008/07/20/bdb-je-in-jruby</id>
  <content type="html">&lt;p&gt;At work I’ve recently been tasked with figuring out how to backup and rename databases in the Berkeley DB store that’s in one of our middleware components. So I thought I’d try it out in &lt;a href=&quot;https://jruby.org/&quot;&gt;JRuby&lt;/a&gt;. Getting the basics was surprisingly easy:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;J&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;java&apos;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;je-3.2.44.jar&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;com.sleepycat.je.Environment&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;com.sleepycat.je.EnvironmentConfig&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EnvironmentConfig&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;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Environment&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;java&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/opt/data/bdb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_database_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rename_database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_database_names&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are a few gotchas to be aware of. First keeping all your java includes in a module seems to be the best approach. I got somewhat flakey behavior when trying to do those import statements in the global scope. And second, don’t try to include java.io.File. It causes warnings about redefining File. I’m guessing because it has the same name as the ruby File class.&lt;/p&gt;

&lt;p&gt;Next I had to make a standalone jar file so that our operations team could run it without installing JRuby. Sadly, this is not so easy. &lt;a href=&quot;https://rubyforge.org/projects/rawr/&quot;&gt;Rawr&lt;/a&gt; will allow you to package your ruby script into a jar, but the resulting jar doesn’t contain JRuby or any other jar dependencies. There’s also the &lt;a href=&quot;https://mojo.codehaus.org/rubyscript-maven-plugin/introduction.html&quot;&gt;rubyscript maven plugin&lt;/a&gt;, but that only stores ruby scripts into your system as maven mojos.&lt;/p&gt;

&lt;p&gt;Neither of these solutions really fit, so I decided to just code it in java for now. For java projects, maven makes creating the standalone jar easy, as shown &lt;a href=&quot;http://www.mail-archive.com/users@maven.apache.org/msg72607.html&quot;&gt;here&lt;/a&gt;. I’m thinking that between some of Rawr’s work and maven’s assembly plugin there’s a perfect solution. But I’ll have to do a bit more hacking to find it.&lt;/p&gt;
</content>
</entry>



</feed>
