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

  <title>Digressions of a Drive-by Hacker</title>
  <link href="https://mojodna.net/atom.xml" rel="self"/>
  <link href="https://mojodna.net/"/>
  <updated>2026-04-27T21:29:35Z</updated>
  <id>https://mojodna.net/</id>
  <author>
    <name>Seth Fitzsimmons</name>
    <email>seth@mojodna.net</email>
  </author>

  
  <entry>
    <title>Resolved: GDAL on AWS GPU Instances</title>
    <link href="https://mojodna.net/2015/01/27/resolved-gdal-on-aws-gpus.html"/>
    <updated>2015-01-27T00:00:00Z</updated>
    <id>https://mojodna.net/2015/01/27/resolved-gdal-on-aws-gpus.html</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Cross-posted from &lt;a href=&#34;https://openterrain.tumblr.com/post/109330474336/resolved-gdal-on-aws-gpu-instances&#34;&gt;openterrain.tumblr.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Specifically, &lt;code&gt;g2.2xlarge&lt;/code&gt; instances with Nvidia GRID K520 GPUs running Amazon Linux.&lt;/p&gt;
&lt;p&gt;As we kicked off the &lt;a href=&#34;https://content.stamen.com/new_knight_grant_new_toner_new_infrastructure&#34;&gt;new Knight News
Grant&lt;/a&gt;,
it was clear early on that we were going to be processing quite a lot of raster
data. Given that, I wanted to ensure that we&#39;d be able to benefit from GDAL&#39;s
OpenCL-accelerated warping (for reprojection and scaling).&lt;/p&gt;
&lt;p&gt;Ubuntu is typically my weapon of choice for these sorts of things, however
I wanted to minimize hardware-related compatibility problems, and Amazon
publishes an &lt;a href=&#34;https://aws.amazon.com/marketplace/ordering?productId=d3fbf14b-243d-46e0-916c-82a8bf6955b4&amp;amp;ref_=dtl_psb_continue&amp;amp;region=us-east-1&#34;&gt;Amazon Linux AMI with NVIDIA GRID GPU Driver&lt;/a&gt; on the AWS Marketplace.&lt;/p&gt;
&lt;p&gt;Straightforward, right? Sadly, no.&lt;/p&gt;
&lt;p&gt;It seemed thoroughly unlikely that GDAL from yum would include OpenCL support
(rightly so), so I went about compiling GDAL from source, omitting everything
I didn&#39;t care about (basically everything except GeoTIFF, zlib, curl, and
OpenCL support):&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;# enable EPEL (for proj-devel)&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; yum-config-manager &lt;span class=&#34;token parameter variable&#34;&gt;--enable&lt;/span&gt; epel
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; yum &lt;span class=&#34;token parameter variable&#34;&gt;-y&lt;/span&gt; update
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; yum &lt;span class=&#34;token parameter variable&#34;&gt;-y&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;make&lt;/span&gt; automake gcc gcc-c++ libcurl-devel proj-devel

&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /tmp
&lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-L&lt;/span&gt; https://download.osgeo.org/gdal/1.11.1/gdal-1.11.1.tar.gz &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf -
&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; gdal-1.11.1
./configure  &lt;span class=&#34;token parameter variable&#34;&gt;--prefix&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/opt/local &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-threads &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-ogr &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-geos &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-libtool &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-libz&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;internal &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-libtiff&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;internal &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-geotiff&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;internal &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-gif &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-pg &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-grass &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-libgrass &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-cfitsio &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-pcraster &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-netcdf &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-png &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-jpeg &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-gif &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-ogdi &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-fme &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-hdf4 &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-hdf5 &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-jasper &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-ecw &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-kakadu &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-mrsid &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-jp2mrsid &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-bsb &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-grib &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-mysql &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-ingres &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-xerces &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-expat &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-odbc &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-sqlite3 &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-dwgdirect &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-idb &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-sde &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-perl &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-php &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-ruby &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --without-python &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-hide-internal-symbols &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-opencl &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
            --with-opencl-include&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/opt/nvidia/cuda/include
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;make&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;# allow GDAL to find necessary libraries&lt;/span&gt;
&lt;span class=&#34;token builtin class-name&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/opt/local/lib:&lt;span class=&#34;token variable&#34;&gt;$LD_LIBRARY_PATH&lt;/span&gt;
&lt;span class=&#34;token builtin class-name&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;&lt;span class=&#34;token environment constant&#34;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/opt/local/bin:&lt;span class=&#34;token environment constant&#34;&gt;$PATH&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My first discovery was that GDAL wouldn&#39;t even try to use the GPU, even when
explicitly requested to (using &lt;code&gt;-wo &amp;quot;USE_OPENCL=TRUE&amp;quot;&lt;/code&gt;). Running as &lt;code&gt;root&lt;/code&gt;
solved the problem, allowing subsequent non-&lt;code&gt;root&lt;/code&gt; invocations (without
explicitly requesting OpenCL) to also work.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;watch nvidia-smi&lt;/code&gt; is a good way to see whether tasks are being offloaded to
the GPU.&lt;/p&gt;
&lt;p&gt;Once I got it to start using the GPU, I immediately ran into a problem:&lt;/p&gt;
&lt;pre class=&#34;language-text&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;ERROR 1: Error: Failed to build program executable!
 Build Log:
 :55:20: error: cannot decrement value of type &#39;float
 __attribute__((address_space(1)))&#39;
 dstPtr[iDstOffset] --;
 ~~~~~~~~~~~~~~~~~~ ^

 ERROR 1: Error at file gdalwarpkernel_opencl.c line 2325:
 CL_BUILD_PROGRAM_FAILURE
 ERROR 1: OpenCL routines reported failure (-11) on line 3250.
 ERROR 1: Error: Failed to build program executable!
 Build Log:
 :55:20: error: cannot decrement value of type &#39;float
 __attribute__((address_space(1)))&#39;
 dstPtr[iDstOffset] --;
 ~~~~~~~~~~~~~~~~~~ ^

 ERROR 1: Error at file gdalwarpkernel_opencl.c line 2325:
 CL_BUILD_PROGRAM_FAILURE
 ERROR 1: OpenCL routines reported failure (-11) on line 3250.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fortunately, this turned out to be a quick fix for Even Roualt (thanks!!),
though it did reinforce my fear of GPU compatibility headaches (&lt;code&gt;&amp;lt;thing&amp;gt;--&lt;/code&gt; vs
&lt;code&gt;&amp;lt;thing&amp;gt; = &amp;lt;thing&amp;gt; - 1&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&#34;language-diff&#34;&gt;&lt;code class=&#34;language-diff&#34;&gt;&lt;span class=&#34;token unchanged&#34;&gt;&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;Index: alg/gdalwarpkernel_opencl.c
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;===================================================================
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;--- alg/gdalwarpkernel_opencl.c (révision 28173)
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;+++ alg/gdalwarpkernel_opencl.c (copie de travail)
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;@@ -593,7 +593,7 @@
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;         &#34;if (dstPtr[iDstOffset] == dstMinVal)\n&#34;
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;             &#34;dstPtr[iDstOffset] = dstMinVal + 1;\n&#34;
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;         &#34;else\n&#34;
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;-            &#34;dstPtr[iDstOffset] --;\n&#34;
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;+            &#34;dstPtr[iDstOffset] = dstPtr[iDstOffset] - 1;\n&#34;
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt;     &#34;}\n&#34;
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt; &#34;}\n&#34;
&lt;span class=&#34;token prefix unchanged&#34;&gt; &lt;/span&gt; &#34;#endif\n&#34;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once patched, it works like a dream, happily churning through jobs at an
impressive clip.&lt;/p&gt;
&lt;p&gt;This fix will be included in GDAL-1.11.2.&lt;/p&gt;
&lt;p&gt;The requirement to run GDAL as &lt;code&gt;root&lt;/code&gt; to initialize the GPU turned out to be
a configuration issue in the Amazon AMI. Running &lt;code&gt;modprobe nvidia_uvm&lt;/code&gt; to load
the kernel driver for the GPU on boot solved the problem, but only after adding
a &lt;code&gt;udev&lt;/code&gt; rule (&lt;code&gt;/etc/udev/rules.d/99-nvidia.rules&lt;/code&gt;) to create the necessary
device nodes:&lt;/p&gt;
&lt;pre class=&#34;language-text&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;# /etc/udev/rules.d/99-nvidia.rules
KERNEL==&#34;nvidia_uvm&#34;, RUN+=&#34;/bin/sh -c &#39;/bin/mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0&#39;&#34;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>make For Data Using Makefiles</title>
    <link href="https://mojodna.net/2015/01/07/make-for-data-using-make.html"/>
    <updated>2015-01-07T00:00:00Z</updated>
    <id>https://mojodna.net/2015/01/07/make-for-data-using-make.html</id>
    <content type="html">&lt;p&gt;&lt;a href=&#34;https://smathermather.wordpress.com/&#34;&gt;Mr. Mather&lt;/a&gt; recently spotted the
&lt;a href=&#34;https://github.com/stamen/toner-carto/blob/master/Makefile&#34;&gt;&lt;code&gt;Makefile&lt;/code&gt;&lt;/a&gt; we&#39;ve
been using for the &lt;a href=&#34;https://content.stamen.com/new_knight_grant_new_toner_new_infrastructure&#34;&gt;updated version of
Toner&lt;/a&gt;
and asked for a walk-through.&lt;/p&gt;
&lt;p&gt;Like many other people who wrangle data for a living, I&#39;ve been in on-and-off
pursuit of a &lt;code&gt;make&lt;/code&gt;-like approach to processing and transforming data.
Specifically, one that allows me to idempotently bootstrap and process data
without starting from zero each time. Also, one that makes it easy enough to
script experiments pro-actively rather than putting replicability off.&lt;/p&gt;
&lt;h2 id=&#34;common-idioms&#34; tabindex=&#34;-1&#34;&gt;Common Idioms&lt;/h2&gt;
&lt;p&gt;We use a number of &lt;code&gt;sh&lt;/code&gt; / &lt;code&gt;bash&lt;/code&gt; idioms that are helpful to recognize as such.&lt;/p&gt;
&lt;h3 id=&#34;use-of-exit-codes&#34; tabindex=&#34;-1&#34;&gt;Use of Exit Codes&lt;/h3&gt;
&lt;p&gt;When a process exits, it does so with a numeric code that indicates success or
failure (in the latter case, often with a value that can be used to determine
why). &lt;code&gt;0&lt;/code&gt; is considered success; anything else is a failure of some sort.
These codes aren&#39;t directly visible, but &lt;code&gt;make&lt;/code&gt; uses them to determine whether
a target was successful (and whether execution should continue). &lt;code&gt;$?&lt;/code&gt; can be
used to surface the code in a shell context:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ &lt;span class=&#34;token builtin class-name&#34;&gt;echo&lt;/span&gt; hi&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token builtin class-name&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;$?&lt;/span&gt;
hi
&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a few cases where we want to shield &lt;code&gt;make&lt;/code&gt; from failures (to prevent
execution from stopping). For example:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;carto &lt;span class=&#34;token parameter variable&#34;&gt;-l&lt;/span&gt; $&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;$@&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;rm &lt;span class=&#34;token parameter variable&#34;&gt;-f&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This runs &lt;code&gt;carto&lt;/code&gt;; if it fails, it deletes the output (&lt;code&gt;$@&lt;/code&gt;). However, if
there was no output, &lt;code&gt;rm&lt;/code&gt; will return successfully, but the task actually
failed. To work-around that, we execute &lt;code&gt;rm&lt;/code&gt; in a subshell (using parentheses)
and explicitly return false (&lt;code&gt;false&lt;/code&gt;) so that &lt;code&gt;make&lt;/code&gt; ceases execution.&lt;/p&gt;
&lt;h3 id=&#34;%7C%7C-and-%26%26&#34; tabindex=&#34;-1&#34;&gt;&lt;code&gt;||&lt;/code&gt; and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;||&lt;/code&gt; and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; are used in conjunction with exit codes to conditionally execute
subsequent parts of a composite command. &lt;code&gt;||&lt;/code&gt; is used when you want to execute
a command only if the first returns an error (non-zero exit code); &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; when
you want to execute a command only if the first was successful.&lt;/p&gt;
&lt;h3 id=&#34;%3E-%2Fdev%2Fnull-2%3E%261&#34; tabindex=&#34;-1&#34;&gt;&lt;code&gt;&amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;POSIX processes are provided 3 file descriptions (handles to files or file-like
things) by default. &lt;code&gt;stdin&lt;/code&gt; (content from a file or other source like
a keyboard) is file descriptor &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;stdout&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;, and &lt;code&gt;stderr&lt;/code&gt; is &lt;code&gt;2&lt;/code&gt;. Thus,
&lt;code&gt;echo hi &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt; says to redirect &lt;code&gt;stdout&lt;/code&gt; (the default output) to
&lt;code&gt;/dev/null&lt;/code&gt; (into the abyss) and then &lt;code&gt;stderr&lt;/code&gt; (&lt;code&gt;2&lt;/code&gt;) to the same place as
&lt;code&gt;stdout&lt;/code&gt; (as a reference: &lt;code&gt;&amp;amp;1&lt;/code&gt;). In this context, we use this to squelch
output, since we generally just care about the exit code (either directly, or
after &lt;code&gt;grep&lt;/code&gt;ing through it or counting lines (&lt;code&gt;wc -l&lt;/code&gt;)).&lt;/p&gt;
&lt;h3 id=&#34;psql-checks&#34; tabindex=&#34;-1&#34;&gt;&lt;code&gt;psql&lt;/code&gt; Checks&lt;/h3&gt;
&lt;p&gt;At various points, we want to short-circuit tasks if a resource already exists.
In a traditional &lt;code&gt;Makefile&lt;/code&gt;, these resources would be files and that
short-circuiting is built-in. However, since we&#39;re since working with database
tables (etc.) and there are no file equivalents, we need to implement
equivalent functionality.&lt;/p&gt;
&lt;p&gt;We use &lt;code&gt;psql -c&lt;/code&gt; for practically all existence checks, as it allows us to
construct a SQL command and use it to query PostgreSQL. Unfortunately,
different checks result in varying output and exit codes.&lt;/p&gt;
&lt;h4 id=&#34;looking-for-relations&#34; tabindex=&#34;-1&#34;&gt;Looking for Relations&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;\d &amp;lt;relation&amp;gt;&lt;/code&gt; will check for the existence of a &amp;quot;relation&amp;quot; (which could be
a table, view, etc.). It displays the name, type, and owner of the relation and
exits with &lt;code&gt;0&lt;/code&gt; only if it matched something.&lt;/p&gt;
&lt;h4 id=&#34;looking-for-extensions&#34; tabindex=&#34;-1&#34;&gt;Looking for Extensions&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;\dx &amp;lt;extension&amp;gt;&lt;/code&gt; will check for extensions matching the provided name. If
present, it will display various information about it. Unfortunately, it exits
with &lt;code&gt;0&lt;/code&gt; regardless of whether anything has been found or not, so we need to
&lt;code&gt;grep&lt;/code&gt; the output for the presence of the name.&lt;/p&gt;
&lt;h4 id=&#34;looking-for-functions&#34; tabindex=&#34;-1&#34;&gt;Looking for Functions&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;\df &amp;lt;name&amp;gt;&lt;/code&gt; will check for functions matching the provided name. When
a function has multiple signatures, it will be displayed multiple times. Like
&lt;code&gt;\dx&lt;/code&gt;, it exits with &lt;code&gt;0&lt;/code&gt; no matter what, so we need to &lt;code&gt;grep&lt;/code&gt;
(case-insensitively) for the function we&#39;re looking for.&lt;/p&gt;
&lt;h3 id=&#34;implicit-make-variables&#34; tabindex=&#34;-1&#34;&gt;Implicit &lt;code&gt;make&lt;/code&gt; Variables&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;make&lt;/code&gt; includes many &lt;a href=&#34;https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html&#34;&gt;implicit
variables&lt;/a&gt;,
most of which are intended for use in an environment where source files are
being compiled and linked into binaries. However, there are still a few we make
use of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$@&lt;/code&gt; - the name of the target (the thing before the &lt;code&gt;:&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$&amp;lt;&lt;/code&gt; - the name of the first prerequisite. Convenient.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$^&lt;/code&gt; - all prerequisites, space-delimited. Can be combined with
&lt;code&gt;$(word &amp;lt;n&amp;gt;,$^&lt;/code&gt;) to select the _n_th one.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;make-functions&#34; tabindex=&#34;-1&#34;&gt;&lt;code&gt;make&lt;/code&gt; Functions&lt;/h3&gt;
&lt;p&gt;Since many of the targets we&#39;re working with are synthetic, we need to extract
relevant components of their names. Database-related functionality is grouped
under the &lt;code&gt;db/&lt;/code&gt; &amp;quot;path&amp;quot;, so we primarily use
&lt;a href=&#34;https://www.gnu.org/software/make/manual/html_node/Text-Functions.html&#34;&gt;&lt;code&gt;subst&lt;/code&gt;&lt;/a&gt;
to remove irrelevant components. We also use &lt;code&gt;word&lt;/code&gt; to refer to components
within space-delimited values.&lt;/p&gt;
&lt;h3 id=&#34;make-patterns&#34; tabindex=&#34;-1&#34;&gt;&lt;code&gt;make&lt;/code&gt; Patterns&lt;/h3&gt;
&lt;p&gt;There are many resources that follow patterns when we work with data. Natural
Earth&#39;s filenames and source URLs are a good example of this. In keeping with
the DRY principle (&amp;quot;don&#39;t repeat yourself&amp;quot;), we fold these into a smaller
number of targets using &lt;code&gt;make&lt;/code&gt; patterns. These are strings that include &lt;code&gt;%&lt;/code&gt;
anywhere text may vary.&lt;/p&gt;
&lt;p&gt;The convenient bit is that prerequisites can also use the &lt;code&gt;%&lt;/code&gt; syntax and the
value of the pattern in the target name will be substituted. Thus, &lt;code&gt;%: %.mml&lt;/code&gt;
will convert &lt;code&gt;toner&lt;/code&gt; to &lt;code&gt;toner: toner.mml&lt;/code&gt; and gives us the behavior we&#39;re
looking for.&lt;/p&gt;
&lt;h2 id=&#34;annotated-makefile&#34; tabindex=&#34;-1&#34;&gt;Annotated &lt;code&gt;Makefile&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Without further ado, here&#39;s an annotated snapshot of the &lt;code&gt;Makefile&lt;/code&gt;-driven
approach we&#39;ve been using. Suggestions are most welcome, especially if they
simplify or clarify.&lt;/p&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;SHELL := /bin/bash&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;bash&lt;/code&gt; for sub-shells, allowing use of &lt;code&gt;bash&lt;/code&gt;-specific functionality.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;PATH := $(PATH):node_modules/.bin&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Add &lt;code&gt;npm&lt;/code&gt;-installed binaries to the &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;define EXPAND_EXPORTS
export $(word 1, $(subst =, , $(1))) := $(word 2, $(subst =, , $(1)))
endef&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Define a macro that expands (splits on &lt;code&gt;=&lt;/code&gt;) and exports (makes available to
sub-shells) key-value arguments, e.g. &lt;code&gt;DATABASE\_URL=postgres:///db&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# wrap Makefile body with a check for pgexplode
ifeq ($(shell test -f node_modules/.bin/pgexplode; echo $$?), 0)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Check whether &lt;code&gt;pgexplode&lt;/code&gt; has been installed (it&#39;s effectively a prerequisite
for the entire &lt;code&gt;Makefile&lt;/code&gt;); &lt;code&gt;test&lt;/code&gt; doesn&#39;t output anything, so the return
code (&lt;code&gt;$?&lt;/code&gt;) needs to be &lt;code&gt;echo&lt;/code&gt;&#39;d for comparison with &lt;code&gt;0&lt;/code&gt; (and escaped with an
extra &lt;code&gt;$&lt;/code&gt; to be passed through to the &lt;code&gt;shell&lt;/code&gt; call).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# load .env
$(foreach a,$(shell cat .env 2&gt; /dev/null),$(eval $(call EXPAND_EXPORTS,$(a))))&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Read &lt;code&gt;.env&lt;/code&gt; (squelching error messages if one doesn&#39;t exist) and pass each
environment pair to &lt;code&gt;EXPAND\_EXPORTS&lt;/code&gt; to make it available to commands in
targets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# expand PG* environment vars
$(foreach a,$(shell set -a &amp;&amp; source .env 2&gt; /dev/null; node_modules/.bin/pgexplode),$(eval $(call EXPAND_EXPORTS,$(a))))&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;pgexplode&lt;/code&gt; to expand &lt;code&gt;DATABASE\_URL&lt;/code&gt; into &lt;a href=&#34;https://www.postgresql.org/docs/9.4/static/libpq-envars.html&#34;&gt;&lt;code&gt;libpq&lt;/code&gt;-compatible
environment
variables&lt;/a&gt;. This
will read from the environment (&lt;code&gt;$DATABASE\_URL&lt;/code&gt;) if one isn&#39;t present in
&lt;code&gt;.env&lt;/code&gt; (or &lt;code&gt;.env&lt;/code&gt; doesn&#39;t exist).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;define create_relation
@psql -c &#34;\d $(subst db/,,$@)&#34; &gt; /dev/null 2&gt;&amp;1 || \
        psql -v ON_ERROR_STOP=1 -qX1f sql/$(subst db/,,$@).sql
endef

define create_extension
@psql -c &#34;\dx $(subst db/,,$@)&#34; | grep $(subst db/,,$@) &gt; /dev/null 2&gt;&amp;1 || \
        psql -v ON_ERROR_STOP=1 -qX1c &#34;CREATE EXTENSION $(subst db/,,$@)&#34;
endef&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Macro definitions that will strip &lt;code&gt;db/&lt;/code&gt; from targets&#39; &lt;code&gt;$@&lt;/code&gt; (target name) and
use it as the name of a SQL file or PostgreSQL extension (&lt;code&gt;$(subst db/,,$@)&lt;/code&gt;). The first command (&lt;code&gt;psql -c &amp;quot;\d $(subst db/,,$@)&amp;quot; &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt;) checks for the presence of a relation in the database specified by
&lt;code&gt;DATABASE\_URL&lt;/code&gt; and only evaluates SQL commands if it fails. &lt;code&gt;\d &amp;lt;name&amp;gt;&lt;/code&gt;
checks for the presence of a relation of any kind, &lt;code&gt;\dx&lt;/code&gt; for loaded
extensions, etc.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ON\_ERROR\_STOP=1&lt;/code&gt; is used to abort as soon as an error occurs, &lt;code&gt;-q&lt;/code&gt; is
&amp;quot;quiet&amp;quot;, &lt;code&gt;-X&lt;/code&gt; ignores any &lt;code&gt;psqlrc&lt;/code&gt; files, &lt;code&gt;-1&lt;/code&gt; runs the command in a single
transaction, &lt;code&gt;-f&lt;/code&gt; provides a file containing commands, and &lt;code&gt;-c&lt;/code&gt; tells it to
use the provided command.&lt;/p&gt;
&lt;p&gt;Commands are prefixed with &lt;code&gt;@&lt;/code&gt; to prevent &lt;code&gt;make&lt;/code&gt; from printing them (since
they&#39;re unnecessarily complicated due to the need to check for the existence
of non-file resources).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;define register_function_target
.PHONY: db/functions/$(strip $(1))

db/functions/$(strip $(1)): db
        @psql -c &#34;\df $(1)&#34; | grep -i $(1) &gt; /dev/null 2&gt;&amp;1 || \
                psql -v ON_ERROR_STOP=1 -qX1f sql/functions/$(1).sql
endef

$(foreach fn,$(shell ls sql/functions/ 2&gt; /dev/null | sed &#39;s/\..*//&#39;),$(eval $(call register_function_target,$(fn))))&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Generate targets for each file in &lt;code&gt;sql/functions&lt;/code&gt;, callable as
&lt;code&gt;db/function/&amp;lt;name&amp;gt;&lt;/code&gt; and depending on the &lt;code&gt;db&lt;/code&gt; target (keep reading).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# Import PBF ($2) as $1
define import
.PHONY: db/osm-$(strip $(word 1, $(subst :, ,$(1)))) db/$(strip $(word 1, $(subst :, ,$(1))))

db/$(strip $(word 1, $(subst :, ,$(1)))): db/osm-$(strip $(word 1, $(subst :, ,$(1)))) db/shared

db/osm-$(strip $(word 1, $(subst :, ,$(1)))): db/postgis db/hstore $(strip $(word 2, $(subst :, ,$(1))))
        @psql -c &#34;\d osm_roads&#34; &gt; /dev/null 2&gt;&amp;1 || \
        imposm3 import \
                --cachedir cache \
                -mapping=imposm3_mapping.json \
                -read $(strip $(word 2, $(subst :, ,$(1)))) \
                -connection=&#34;$${DATABASE_URL}&#34; \
                -write \
                -deployproduction \
                -overwritecache
endef&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Macro definition for importing OSM extracts. When called, it generates
targets like &lt;code&gt;db/&amp;lt;place&amp;gt;&lt;/code&gt; (which depends on both the import and &lt;code&gt;db/shared&lt;/code&gt;
(see below)) and &lt;code&gt;db/osm-&amp;lt;place&amp;gt;&lt;/code&gt;, which does the actual import. &lt;code&gt;osm\_roads&lt;/code&gt;
is assumed to be created by &lt;code&gt;imposm3&lt;/code&gt; in this case. &lt;code&gt;$${DATABASE\_URL}&lt;/code&gt; is
escaped because this is a macro (so it&#39;s evaluated at runtime) and uses
braces to use the environment variable.&lt;/p&gt;
&lt;p&gt;Now begins what looks like a more conventional &lt;code&gt;Makefile&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# default target
default: toner

# symlink into TileMill&#39;s project folder (if TM is installed)
link:
        @test -e ${HOME}/Documents/MapBox/project &amp;&amp; \
                test -e ${HOME}/Documents/MapBox/project/toner || \
                ln -sf &#34;`pwd`&#34; ${HOME}/Documents/MapBox/project/toner

# clean up derivative files
clean:
        @rm -f *.mml *.xml

# create a default .env file with a sensible(?) default
.env:
        @echo DATABASE_URL=postgres:///toner &gt; $@&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;%: %.mml
        @cp $&lt; project.mml&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;A pattern, which obviates the need to declare multiple redundant targets. If
&lt;code&gt;make toner&lt;/code&gt; is run, it will depend on &lt;code&gt;toner.mml&lt;/code&gt; (see below) and will
silently copy the output (&lt;code&gt;$&amp;lt;&lt;/code&gt; is the expanded name of the first dependency)
to &lt;code&gt;project.mml&lt;/code&gt; for TileMill to read.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;mml: $(subst .yml,.mml,$(filter-out circle.yml,$(wildcard *.yml)))

xml: $(subst .yml,.xml,$(filter-out circle.yml,$(wildcard *.yml)))&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Defines targets that will make MML and XML files corresponding to all &lt;code&gt;.yml&lt;/code&gt;
files &lt;strong&gt;except&lt;/strong&gt; &lt;code&gt;circle.yml&lt;/code&gt; (which is a control file for
&lt;a href=&#34;https://circleci.com&#34;&gt;CircleCI&lt;/a&gt;, not a style).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.PRECIOUS: %.mml

%.mml: %.yml map.mss labels.mss %.mss interp js-yaml
        @echo Building $@
        @cat $&lt; | interp | js-yaml &gt; tmp.mml &amp;&amp; mv tmp.mml $@&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Builds &lt;code&gt;*.mml&lt;/code&gt; by interpolating environment variables into
a &lt;a href=&#34;https://mustache.github.io/&#34;&gt;Mustache&lt;/a&gt;-templated YAML file
(&lt;a href=&#34;https://github.com/stamen/interp&#34;&gt;&lt;code&gt;interp&lt;/code&gt;&lt;/a&gt;) and converting to JSON
(&lt;a href=&#34;https://github.com/nodeca/js-yaml&#34;&gt;&lt;code&gt;js-yaml&lt;/code&gt;&lt;/a&gt;). &lt;code&gt;tmp.mml&lt;/code&gt; is used so that
&lt;code&gt;mv&lt;/code&gt; can atomically move the file into place (without doing this, TileMill
periodically chokes when reading partially-written files).&lt;/p&gt;
&lt;p&gt;This depends on &lt;code&gt;&amp;lt;style&amp;gt;.yml&lt;/code&gt;, &lt;code&gt;map.mss&lt;/code&gt;, &lt;code&gt;labels.mss&lt;/code&gt;, and &lt;code&gt;&amp;lt;style.mss&amp;gt;&lt;/code&gt; so
that it will be considered stale when any of those are modified. &lt;code&gt;interp&lt;/code&gt; and
&lt;code&gt;js-yaml&lt;/code&gt; are explicitly called out as dependencies so that they can be
installed if necessary (see below).&lt;/p&gt;
&lt;p&gt;This is marked as &lt;code&gt;.PRECIOUS&lt;/code&gt; so that artifacts won&#39;t be deleted when called
as an intermediate target (i.e. from an XML target).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.PRECIOUS: %.xml

%.xml: %.mml carto
        @echo
        @echo Building $@
        @echo
        @carto -l $&lt; &gt; $@ || (rm -f $@; false)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Builds &lt;code&gt;*.xml&lt;/code&gt; from &lt;code&gt;&amp;lt;style&amp;gt;.mml&lt;/code&gt; (declared above). &lt;code&gt;|| (rm -f $@; false)&lt;/code&gt; is
included because &lt;code&gt;carto&lt;/code&gt; may leave behind invalid XML files when it fails
(and because we want to pass the failure through and terminate the current
&lt;code&gt;make&lt;/code&gt; invocation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.PHONY: carto

carto: node_modules/carto/package.json

.PHONY: interp

interp: node_modules/interp/package.json

.PHONY: js-yaml

js-yaml: node_modules/js-yaml/package.json

node_modules/carto/package.json: PKG = $(word 2,$(subst /, ,$@))
node_modules/carto/package.json: node_modules/millstone/package.json
        @type node &gt; /dev/null 2&gt;&amp;1 || (echo &#34;Please install Node.js&#34; &amp;&amp; false)
        @echo &#34;Installing $(PKG)&#34;
        @npm install $(PKG)

node_modules/interp/package.json: PKG = $(word 2,$(subst /, ,$@))
node_modules/interp/package.json:
        @type node &gt; /dev/null 2&gt;&amp;1 || (echo &#34;Please install Node.js&#34; &amp;&amp; false)
        @echo &#34;Installing $(PKG)&#34;
        @npm install $(PKG)

node_modules/js-yaml/package.json: PKG = $(word 2,$(subst /, ,$@))
node_modules/js-yaml/package.json:
        @type node &gt; /dev/null 2&gt;&amp;1 || (echo &#34;Please install Node.js&#34; &amp;&amp; false)
        @echo &#34;Installing $(PKG)&#34;
        @npm install $(PKG)

node_modules/millstone/package.json: PKG = $(word 2,$(subst /, ,$@))
node_modules/millstone/package.json:
        @type node &gt; /dev/null 2&gt;&amp;1 || (echo &#34;Please install Node.js&#34; &amp;&amp; false)
        @echo &#34;Installing $(PKG)&#34;
        @npm install $(PKG)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Artificial (&lt;code&gt;.PHONY&lt;/code&gt;) targets for required commands along with file-based
dependencies and checks for Node. &lt;code&gt;PKG&lt;/code&gt; is defined as a &lt;code&gt;make&lt;/code&gt; variable in
preparation for future refactoring that turns this boilerplate into a macro.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; declares dependencies on these commands, but it also includes
everything else to run a rendering node, so this is a lower-impact way of
ensuring that they&#39;re installed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.PHONY: DATABASE_URL

DATABASE_URL:
        @test &#34;${$@}&#34; || (echo &#34;$@ is undefined&#34; &amp;&amp; false)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;A target definition that can be used when one wants to ensure that
a &lt;code&gt;DATABASE\_URL&lt;/code&gt; was provided.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.PHONY: db

db: DATABASE_URL
        @psql -c &#34;SELECT 1&#34; &gt; /dev/null 2&gt;&amp;1 || \
        createdb&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Target to ensure that a database exists. If &lt;code&gt;psql&lt;/code&gt; returns fall, &lt;code&gt;createdb&lt;/code&gt;
will be run (using &lt;code&gt;libpq&lt;/code&gt; environment variables extracted from
&lt;code&gt;DATABASE\_URL&lt;/code&gt;) to create one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.PHONY: db/postgis

db/postgis: db
        $(call create_extension)

.PHONY: db/hstore

db/hstore: db
        $(call create_extension)&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Targets that create extensions (and require that a database exists) using the
macros defined above.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.PHONY: db/shared

db/shared: db/postgres db/shapefiles

.PHONY: db/postgres

db/postgres: db/functions/highroad&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Meta-targets that depend on both explicit (&lt;code&gt;db/shapefiles&lt;/code&gt;) and implicit
(&lt;code&gt;db/functions/highroad&lt;/code&gt;) targets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.PHONY: db/shapefiles

db/shapefiles: shp/osmdata/land-polygons-complete-3857.zip \
               shp/natural_earth/ne_50m_land-merc.zip \
               shp/natural_earth/ne_50m_admin_0_countries_lakes-merc.zip \
               shp/natural_earth/ne_10m_admin_0_countries_lakes-merc.zip \
               shp/natural_earth/ne_10m_admin_0_boundary_lines_map_units-merc.zip \
               shp/natural_earth/ne_50m_admin_1_states_provinces_lines-merc.zip \
               shp/natural_earth/ne_10m_geography_marine_polys-merc.zip \
               shp/natural_earth/ne_50m_geography_marine_polys-merc.zip \
               shp/natural_earth/ne_110m_geography_marine_polys-merc.zip \
               shp/natural_earth/ne_10m_airports-merc.zip \
               shp/natural_earth/ne_10m_roads-merc.zip \
               shp/natural_earth/ne_10m_lakes-merc.zip \
               shp/natural_earth/ne_50m_lakes-merc.zip \
               shp/natural_earth/ne_10m_admin_0_boundary_lines_land-merc.zip \
               shp/natural_earth/ne_50m_admin_0_boundary_lines_land-merc.zip \
               shp/natural_earth/ne_10m_admin_1_states_provinces_lines-merc.zip&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Meta-target for processed Shapefiles.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# TODO places target that lists registered places
PLACES=BC:data/extract/north-america/ca/british-columbia-latest.osm.pbf \
       CA:data/extract/north-america/us/california-latest.osm.pbf \
       belize:data/extract/central-america/belize-latest.osm.pbf \
       cle:data/metro/cleveland_ohio.osm.pbf \
       MA:data/extract/north-america/us/massachusetts-latest.osm.pbf \
       NY:data/extract/north-america/us/new-york-latest.osm.pbf \
       OH:data/extract/north-america/us/ohio-latest.osm.pbf \
       sf:data/metro/san-francisco.osm.pbf \
       sfbay:data/metro/sf-bay-area.osm.pbf \
       seattle:data/metro/seattle_washington.osm.pbf \
       WA:data/extract/north-america/us/washington-latest.osm.pbf

$(foreach place,$(PLACES),$(eval $(call import,$(place))))&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Define a list of places along with the local reference to their corresponding
extract (&lt;code&gt;data/extract/%&lt;/code&gt; and &lt;code&gt;data/metro/%&lt;/code&gt; are patterns defined below) and
generate import targets (e.g. &lt;code&gt;db/BC&lt;/code&gt; for British Columbia).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.SECONDARY: data/extract/%

data/extract/%:
        @mkdir -p $$(dirname $@)
        curl -Lf https://download.geofabrik.de/$(@:data/extract/%=%) -o $@

.SECONDARY: data/metro/%

data/metro/%:
        @mkdir -p $$(dirname $@)
        curl -Lf https://s3.amazonaws.com/metro-extracts.mapzen.com/$(@:data/metro/%=%) -o $@&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OSM extract patterns; i.e. anything under &lt;code&gt;data/extract/&lt;/code&gt; will be downloaded
from &lt;a href=&#34;https://www.geofabrik.de/&#34;&gt;Geofabrik&lt;/a&gt;, anything under &lt;code&gt;data/metro/&lt;/code&gt; from
&lt;a href=&#34;https://mapzen.com/&#34;&gt;Mapzen&lt;/a&gt;&#39;s &lt;a href=&#34;https://mapzen.com/metro-extracts/&#34;&gt;Metro
Extracts&lt;/a&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;These are marked as &lt;code&gt;.SECONDARY&lt;/code&gt; to prevent them from being deleted (as
they&#39;re &amp;quot;expensive&amp;quot; to create). &lt;strong&gt;Note&lt;/strong&gt;: this isn&#39;t quite right; my
intention is to keep them around but also to delete them if the target
failed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkdir -p&lt;/code&gt; is used to ensure that the target directory exists. &lt;code&gt;$$(dirname $@)&lt;/code&gt; is used to pass the literal &lt;code&gt;$(dirname data/metro/&amp;lt;whatever&amp;gt;)&lt;/code&gt; to
&lt;code&gt;mkdir&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$(@:data/metro/%=%)&lt;/code&gt; substitutes &lt;code&gt;&amp;lt;whatever&amp;gt;&lt;/code&gt; for &lt;code&gt;data/metro/&amp;lt;whatever&amp;gt;&lt;/code&gt; in
the target name.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl&lt;/code&gt;&#39;s &lt;code&gt;-f&lt;/code&gt; option is provided so that it will return with a non-zero exit
code on failure and cause the target to fail.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;.SECONDARY: data/osmdata/land_polygons.zip

# so the zip matches the shapefile name
data/osmdata/land_polygons.zip:
        @mkdir -p $$(dirname $@)
        curl -Lf https://data.openstreetmapdata.com/land-polygons-complete-3857.zip -o $@

shp/osmdata/%.shp \
shp/osmdata/%.dbf \
shp/osmdata/%.prj \
shp/osmdata/%.shx: data/osmdata/%.zip
        @mkdir -p $$(dirname $@)
        unzip -ju $&lt; -d $$(dirname $@)

shp/osmdata/land_polygons.index: shp/osmdata/land_polygons.shp
        shapeindex $&lt;

.SECONDARY: data/osmdata/land-polygons-complete-3857.zip

shp/osmdata/land-polygons-complete-3857.zip: shp/osmdata/land_polygons.shp \
        shp/osmdata/land_polygons.dbf \
        shp/osmdata/land_polygons.prj \
        shp/osmdata/land_polygons.shx \
        shp/osmdata/land_polygons.index
        zip -j $@ $^&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Similar to the OSM extracts, but for a specific file with a non-matching
source name. Some of this remains as a relic of when we were using different
versions of the land polygons.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;define natural_earth
db/$(strip $(word 1, $(subst :, ,$(1)))): $(strip $(word 2, $(subst :, ,$(1)))) db/postgis
        psql -c &#34;\d $$(subst db/,,$$@)&#34; &gt; /dev/null 2&gt;&amp;1 || \
        ogr2ogr --config OGR_ENABLE_PARTIAL_REPROJECTION TRUE \
                        --config SHAPE_ENCODING WINDOWS-1252 \
                        --config PG_USE_COPY YES \
                        -nln $$(subst db/,,$$@) \
                        -t_srs EPSG:3857 \
                        -lco ENCODING=UTF-8 \
                        -nlt PROMOTE_TO_MULTI \
                        -lco POSTGIS_VERSION=2.0 \
                        -lco GEOMETRY_NAME=geom \
                        -lco SRID=3857 \
                        -lco PRECISION=NO \
                        -clipsrc -180 -85.05112878 180 85.05112878 \
                        -segmentize 1 \
                        -skipfailures \
                        -f PGDump /vsistdout/ \
                        /vsizip/$$&lt;/$(strip $(word 3, $(subst :, ,$(1)))) | psql -q

shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.shp \
        shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.dbf \
        shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.prj \
        shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.shx: $(strip $(word 2, $(subst :, ,$(1))))
        @mkdir -p $$$$(dirname $$@)
        ogr2ogr --config OGR_ENABLE_PARTIAL_REPROJECTION TRUE \
                        --config SHAPE_ENCODING WINDOWS-1252 \
                        -t_srs EPSG:3857 \
                        -lco ENCODING=UTF-8 \
                        -clipsrc -180 -85.05112878 180 85.05112878 \
                        -segmentize 1 \
                        -skipfailures $$@ /vsizip/$$&lt;/$(strip $(word 3, $(subst :, ,$(1))))

shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.index: shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.shp
        shapeindex $$&lt;

.SECONDARY: shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.zip

shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.zip: shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.shp \
        shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.dbf \
        shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.prj \
        shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.shx \
        shp/natural_earth/$(strip $(word 1, $(subst :, ,$(1))))-merc.index
        zip -j $$@ $$^
endef&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Macro definition that creates &lt;code&gt;db/ne\_&amp;lt;whatever&amp;gt;&lt;/code&gt; and &lt;code&gt;shp/natural\_earth/*&lt;/code&gt;
targets for &lt;a href=&#34;https://www.naturalearthdata.com/&#34;&gt;Natural Earth&lt;/a&gt; sources. It
assumes that it&#39;s called with arguments in the form &lt;code&gt;&amp;lt;name&amp;gt;:&amp;lt;source file&amp;gt;:[shapefile]&lt;/code&gt;. (If someone can help simplify the repeated &lt;code&gt;$(strip $(word 1, $(subst :, ,$(1))))&lt;/code&gt; declarations (used to extract the first
component), I&#39;d appreciate it!)&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;db/&amp;lt;whatever&amp;gt;&lt;/code&gt; target runs &lt;code&gt;ogr2ogr&lt;/code&gt; with a set of options that we&#39;ve
found to work well with the Natural Earth Shapefiles over the years.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;shp/natural\_earth/*-merc.{shp,dbf,prj,shx}&lt;/code&gt; states that there will be
4 artifacts for each invocation of &lt;code&gt;ogr2ogr&lt;/code&gt; (used to reproject here). Again,
we use options gathered over the years along with &lt;code&gt;/vsizip&lt;/code&gt; to avoid needing
to unzip. If a single &amp;quot;file&amp;quot; (base name, really) exists in the zip, just the
name of the zip file is necessary, otherwise a path to the Shapefile within
the zip is necessary (the list of layers below takes advantage of that by
allowing &lt;code&gt;[shapefile]&lt;/code&gt; to be optional. (This is necessary due to errors in
the packaging of some of the Natural Earth layers.)&lt;/p&gt;
&lt;p&gt;Aggressive escaping is necessary in order for literal &lt;code&gt;$&lt;/code&gt;s to be passed
through, e.g. &lt;code&gt;$$$$(dirname $$@)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;shp/natural\_earth/*-merc.zip&lt;/code&gt; target uses &lt;code&gt;$^&lt;/code&gt; for the list of files to
compress, which contains the expanded names of all of the dependencies.
&lt;code&gt;zip&lt;/code&gt;&#39;s &lt;code&gt;-j&lt;/code&gt; option is used to &amp;quot;junk paths&amp;quot; and put everything in the root of
the zip file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# &lt;name&gt;:&lt;source file&gt;:[shapefile]
NATURAL_EARTH=ne_50m_land:data/ne/50m/physical/ne_50m_land.zip \
        ne_50m_admin_0_countries_lakes:data/ne/50m/cultural/ne_50m_admin_0_countries_lakes.zip \
        ne_10m_admin_0_countries_lakes:data/ne/10m/cultural/ne_10m_admin_0_countries_lakes.zip \
        ne_10m_admin_0_boundary_lines_map_units:data/ne/10m/cultural/ne_10m_admin_0_boundary_lines_map_units.zip \
        ne_50m_admin_1_states_provinces_lines:data/ne/50m/cultural/ne_50m_admin_1_states_provinces_lines.zip \
        ne_10m_geography_marine_polys:data/ne-stamen/10m/physical/ne_10m_geography_marine_polys.zip \
        ne_50m_geography_marine_polys:data/ne-stamen/50m/physical/ne_50m_geography_marine_polys.zip \
        ne_110m_geography_marine_polys:data/ne-stamen/110m/physical/ne_110m_geography_marine_polys.zip \
        ne_10m_airports:data/ne-stamen/10m/cultural/ne_10m_airports.zip \
        ne_10m_roads:data/ne/10m/cultural/ne_10m_roads.zip \
        ne_10m_lakes:data/ne/10m/physical/ne_10m_lakes.zip \
        ne_50m_lakes:data/ne/50m/physical/ne_50m_lakes.zip \
        ne_10m_admin_0_boundary_lines_land:data/ne/10m/cultural/ne_10m_admin_0_boundary_lines_land.zip \
        ne_50m_admin_0_boundary_lines_land:data/ne/50m/cultural/ne_50m_admin_0_boundary_lines_land.zip \
        ne_10m_admin_1_states_provinces_lines:data/ne/10m/cultural/ne_10m_admin_1_states_provinces_lines.zip:ne_10m_admin_1_states_provinces_lines.shp

$(foreach shape,$(NATURAL_EARTH),$(eval $(call natural_earth,$(shape))))&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Define a list of Natural Earth layers along with their local file references
and (optional) Shapefile names and call &lt;code&gt;natural_earth&lt;/code&gt; to generate targets.
As with the OSM extracts, patterns are used to match the conventions used by
the Natural Earth site.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;define natural_earth_sources
.SECONDARY: data/ne/$(1)/$(2)/%.zip

data/ne/$(1)/$(2)/%.zip:
        @mkdir -p $$(dir $$@)
        curl -fL https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/$(1)/$(2)/$$(@:data/ne/$(1)/$(2)/%=%) -o $$@

.SECONDARY: data/ne/$(1)/$(2)/%.zip

data/ne-stamen/$(1)/$(2)/%.zip:
        @mkdir -p $$(dir $$@)
        curl -fL &#34;https://github.com/stamen/natural-earth-vector/blob/master/zips/$(1)_$(2)/$$(@:data/ne-stamen/$(1)/$(2)/%=%)?raw=true&#34; -o $$@
endef&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Macro definition for meta Natural Earth patterns.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;scales=10m 50m 110m
themes=cultural physical raster

$(foreach a,$(scales),$(foreach b,$(themes),$(eval $(call natural_earth_sources,$(a),$(b)))))&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Generate targets for nested combinations of scales and themes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&#34;language-Makefile&#34;&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# complete wrapping
else
.DEFAULT:
        $(error Please install pgexplode: &#34;npm install pgexplode&#34;)
endif&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Provide a default target (truly default in that it will execute for any
requested target) explaining what needs to be done for things to work
correctly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Questions? Suggestions?&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>KVM, libvirt, and Ubuntu 14.04</title>
    <link href="https://mojodna.net/2014/05/14/kvm-libvirt-and-ubuntu-14-04.html"/>
    <updated>2014-05-14T00:00:00Z</updated>
    <id>https://mojodna.net/2014/05/14/kvm-libvirt-and-ubuntu-14-04.html</id>
    <content type="html">&lt;p&gt;I recently upgraded one of the servers in the office to Ubuntu 14.04, the most
recent LTS release. While doing this, I decided that I would figure out a clean
way of managing virtual machines. This is what I found:&lt;/p&gt;
&lt;p&gt;Canonical now publishes &lt;a href=&#34;https://cloud-images.ubuntu.com/&#34;&gt;Ubuntu cloud images&lt;/a&gt;.
These match the official images that are used on EC2. It turns out that they
can also be used in a local environment, complete with the customizations that
&lt;code&gt;user-data&lt;/code&gt; provide.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://libvirt.org/&#34;&gt;&lt;code&gt;libvirt&lt;/code&gt;&lt;/a&gt; appears to be the cleanest abstraction of
KVM/QEMU, Xen, LXC, and others, so I took a stab at using that to manage my
VMs.&lt;/p&gt;
&lt;p&gt;Installation was easy:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;apt-get&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; libvirt-bin&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creating VMs (well, importing existing images and running them) was another
matter. Over the last few years we&#39;ve seen a proliferation of tools that
attempt to provision VMs without needing to create XML manifests (ultimately
culminating in OpenStack). &lt;code&gt;virt-install&lt;/code&gt;, which comes with &lt;code&gt;libvirt&lt;/code&gt; ended up
being the tool for me.&lt;/p&gt;
&lt;p&gt;To start, I downloaded a qcow2 Ubuntu Cloud image into &lt;code&gt;libvirt&lt;/code&gt;&#39;s &lt;code&gt;images/&lt;/code&gt;
directory:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-LO&lt;/span&gt; https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;cp&lt;/span&gt; trusty-server-cloudimg-amd64-disk1.img /var/lib/libvirt/images/
&lt;span class=&#34;token function&#34;&gt;virsh&lt;/span&gt; pool-refresh default &lt;span class=&#34;token comment&#34;&gt;# tell libvirt to re-scan for new files&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also added an ethernet bridge device so that the VMs could co-exist on the
network with everything else in the office:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;cat&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;EOF&lt;span class=&#34;token bash punctuation&#34;&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tee&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-a&lt;/span&gt; /etc/network/interfaces&lt;/span&gt;
auto br0
iface br0 inet dhcp
  bridge_ports eth0
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When booting one of the Ubuntu Cloud images on its own, it briefly hangs while
waiting for network connections to EC2&#39;s internal network that exposes
metadata. Rather than replicating their environment, the Cloud images have
a relatively &lt;a href=&#34;https://www.technovelty.org/linux/running-cloud-images-locally.html&#34;&gt;secret super
power&lt;/a&gt;:
the ability to pull &lt;a href=&#34;https://cloudinit.readthedocs.org/en/latest/&#34;&gt;&lt;code&gt;cloud-init&lt;/code&gt;&lt;/a&gt;
configurations off of a secondary mounted image.&lt;/p&gt;
&lt;p&gt;To create an image containing a ``cloud-init&lt;code&gt;configuration, create 2 files:&lt;/code&gt;meta-data&lt;code&gt;and&lt;/code&gt;user-data&lt;code&gt;. These are their minimal forms (&lt;/code&gt;meta-data` appears
to be ignored / fails to set the hostname):&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;cat&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;EOF&lt;span class=&#34;token bash punctuation&#34;&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; meta-data&lt;/span&gt;
instance-id: iid-local01;
local-hostname: ubuntu
EOF&lt;/span&gt;

&lt;span class=&#34;token function&#34;&gt;cat&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;EOF&lt;span class=&#34;token bash punctuation&#34;&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; user-data&lt;/span&gt;
#cloud-config
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, write these into an ISO and copy it into &lt;code&gt;libvirt&lt;/code&gt;&#39;s &lt;code&gt;images/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;genisoimage &lt;span class=&#34;token parameter variable&#34;&gt;-output&lt;/span&gt; configuration.iso &lt;span class=&#34;token parameter variable&#34;&gt;-volid&lt;/span&gt; cidata &lt;span class=&#34;token parameter variable&#34;&gt;-joliet&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-rock&lt;/span&gt; user-data meta-data
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;cp&lt;/span&gt; configuration.iso /var/lib/libvirt/images/
&lt;span class=&#34;token function&#34;&gt;virsh&lt;/span&gt; pool-refresh default&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, it&#39;s now possible to boot a VM and avoid the network wait.
This will create a new VM (&amp;quot;domain&amp;quot; in &lt;code&gt;libvirt&lt;/code&gt; parlance) with 1G RAM, 1 vCPU,
and bridged networking:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;virsh&lt;/span&gt; vol-clone &lt;span class=&#34;token parameter variable&#34;&gt;--pool&lt;/span&gt; default trusty-server-cloudimg-amd64-disk1.img test.img

virt-install &lt;span class=&#34;token parameter variable&#34;&gt;-r&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1024&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-n&lt;/span&gt; &lt;span class=&#34;token builtin class-name&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;--vcpus&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;--autostart&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;--memballoon&lt;/span&gt; virtio &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;--network&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;bridge&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;br0 &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;--boot&lt;/span&gt; hd &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;--disk&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;vol&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;default/test.img,format&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;qcow2,bus&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;virtio &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;--disk&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;vol&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;default/configuration.iso,bus&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;virtio

&lt;span class=&#34;token function&#34;&gt;virsh&lt;/span&gt; list&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see the generated XML for this domain, run:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;virsh&lt;/span&gt; dumpxml &lt;span class=&#34;token builtin class-name&#34;&gt;test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To stop it:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;virsh&lt;/span&gt; destroy &lt;span class=&#34;token builtin class-name&#34;&gt;test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To remove it:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;virsh&lt;/span&gt; undefine &lt;span class=&#34;token builtin class-name&#34;&gt;test&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;virsh&lt;/span&gt; vol-delete &lt;span class=&#34;token parameter variable&#34;&gt;--pool&lt;/span&gt; default test.img&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, since we have the power of &lt;code&gt;cloud-init&lt;/code&gt;, we can modify the initial boot
configuration to do some initial provisioning. To do that, update &lt;code&gt;user-data&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;cat&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;EOF&lt;span class=&#34;token bash punctuation&#34;&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; user-data&lt;/span&gt;
#cloud-config

# upgrade packages on startup
package_upgrade: true

# install git
packages:
  - git

# create a user
runcmd:
  - [ useradd, -c, Seth Fitzsimmons, -u, 1001, -G, sudo, -U, -M, -p, &lt;span class=&#34;token variable&#34;&gt;$5&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;$FVJ1C48Rlhy&lt;/span&gt;/&lt;span class=&#34;token variable&#34;&gt;$GOidCu4a0qTmngqhFMGT7z&lt;/span&gt;/N.8nYTuXaaGzEDPhfIL., -s, /bin/bash, seth ]
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;cloud-init&lt;/code&gt; supports user creation since 0.7.0 (trusty comes with 0.7.5), but
it does not appear to work locally and I&#39;d like to be able to re-use these
configurations with Ubuntu 12.04 images (which ship with &lt;code&gt;cloud-init 0.6.3), so I&#39;m doing the same thing by hand with&lt;/code&gt;runcmd`.&lt;/p&gt;
&lt;p&gt;So far (which hasn&#39;t been that long), this has been working well. One of the
next steps is to achieve a similar streamlined workflow for LXC / Docker,
similar to what &lt;a href=&#34;https://mike.teczno.com/notes/disposable-virtualbox-lxc-environments.html&#34;&gt;Mike wrote up about LXC and
Virtualbox&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Binary Streaming with Hadoop (and Node.js)</title>
    <link href="https://mojodna.net/2013/12/27/binary-streaming-with-hadoop-and-nodejs.html"/>
    <updated>2013-12-27T00:00:00Z</updated>
    <id>https://mojodna.net/2013/12/27/binary-streaming-with-hadoop-and-nodejs.html</id>
    <content type="html">&lt;p&gt;Manipulating binary data from Hadoop streaming jobs is a black art.
There are Python (&lt;a href=&#34;https://klbostee.github.io/dumbo/&#34;&gt;dumbo&lt;/a&gt;,
&lt;a href=&#34;http://pydoop.sourceforge.net/docs/&#34;&gt;pydoop&lt;/a&gt; and
&lt;a href=&#34;http://www.hadoopy.com/en/latest/&#34;&gt;hadoopy&lt;/a&gt;) and
R (&lt;a href=&#34;https://github.com/RevolutionAnalytics/RHadoop/wiki/rmr&#34;&gt;rmr&lt;/a&gt;) tools to
facilitate streaming jobs, but all of them have abstracted the handling of byte
streams (using &lt;a href=&#34;https://hadoop.apache.org/docs/current/api/org/apache/hadoop/typedbytes/package-summary.html&#34;&gt;typed
bytes&lt;/a&gt;)
successfully enough that it&#39;s difficult to determine how they actually work.
Which, as it turns out, is important to know if you&#39;re &lt;em&gt;not&lt;/em&gt; using them.&lt;/p&gt;
&lt;p&gt;While researching this topic, I kept returning to &lt;a href=&#34;https://stackoverflow.com/questions/15171514/how-to-use-typedbytes-or-rawbytes-in-hadoop-streaming&#34;&gt;a Stack Overflow
post&lt;/a&gt;
that contains more information on this topic in one place than anywhere I&#39;ve
seen, but even then it doesn&#39;t fully explain what&#39;s going on. I found
additional useful information in
&lt;a href=&#34;https://issues.apache.org/jira/browse/HADOOP-1722&#34;&gt;HADOOP-1722&lt;/a&gt; and in &lt;a href=&#34;https://static.last.fm/johan/huguk-20090414/klaas-hadoop-1722.pdf&#34;&gt;Klaas
Bosteel&#39;s presentation on the
topic&lt;/a&gt;, but
eventually had to start over and work through it bit by byte.&lt;/p&gt;
&lt;p&gt;Without further ado:&lt;/p&gt;
&lt;h2 id=&#34;create-some-typed-bytes&#34; tabindex=&#34;-1&#34;&gt;Create Some Typed Bytes&lt;/h2&gt;
&lt;p&gt;This is simple program that will output 2 pairs of key/value pairs as typed
bytes:&lt;/p&gt;
&lt;pre class=&#34;language-javascript&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;token function-variable function&#34;&gt;prepare&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token parameter&#34;&gt;typeCode&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; value&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
  value &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;value&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; len &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  len&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;writeInt32BE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;value&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;length&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; Buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;concat&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;typeCode&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    len&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    value
  &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// record 1&lt;/span&gt;
process&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stdout&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;key&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;   &lt;span class=&#34;token comment&#34;&gt;// string&lt;/span&gt;
process&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stdout&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;value&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// bytes&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// record 2&lt;/span&gt;
process&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stdout&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;key2&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;token comment&#34;&gt;// string&lt;/span&gt;
process&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stdout&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;value&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// bytes&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use it to generate a file containing typed bytes:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;node&lt;/span&gt; write.js &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; string_bytes.tb&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result looks like this:&lt;/p&gt;
&lt;pre class=&#34;language-text&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;00000000  07 00 00 00 03 6b 65 79  00 00 00 00 05 76 61 6c  |.....key.....val|
00000010  75 65 07 00 00 00 04 6b  65 79 32 00 00 00 00 05  |ue.....key2.....|
00000020  76 61 6c 75 65                                    |value|&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;prepare-a-sequencefile&#34; tabindex=&#34;-1&#34;&gt;Prepare a &lt;code&gt;SequenceFile&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SequenceFile&lt;/code&gt;s are one of Hadoop&#39;s solutions to the &lt;a href=&#34;https://blog.cloudera.com/blog/2009/02/the-small-files-problem/&#34;&gt;small file
problem&lt;/a&gt;. The
format is supposedly a bit Java-centric, but with streaming, we never need to
interact with them directly. For our purposes, you can think of them as
splittable wrappers for records represented as typed bytes.&lt;/p&gt;
&lt;p&gt;To convert the typed bytes into
a &lt;a href=&#34;https://hadoop.apache.org/docs/current/api/org/apache/hadoop/io/SequenceFile.html&#34;&gt;&lt;code&gt;SequenceFile&lt;/code&gt;&lt;/a&gt;
stored in HDFS, use &lt;code&gt;loadtb&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming-2.0.0-cdh4.4.0.jar &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  loadtb string_bytes.seq &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; string_bytes.tb&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result looks like this:&lt;/p&gt;
&lt;pre class=&#34;language-text&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;00000000  53 45 51 06 2f 6f 72 67  2e 61 70 61 63 68 65 2e  |SEQ./org.apache.|
00000010  68 61 64 6f 6f 70 2e 74  79 70 65 64 62 79 74 65  |hadoop.typedbyte|
00000020  73 2e 54 79 70 65 64 42  79 74 65 73 57 72 69 74  |s.TypedBytesWrit|
00000030  61 62 6c 65 2f 6f 72 67  2e 61 70 61 63 68 65 2e  |able/org.apache.|
00000040  68 61 64 6f 6f 70 2e 74  79 70 65 64 62 79 74 65  |hadoop.typedbyte|
00000050  73 2e 54 79 70 65 64 42  79 74 65 73 57 72 69 74  |s.TypedBytesWrit|
00000060  61 62 6c 65 01 00 2a 6f  72 67 2e 61 70 61 63 68  |able..*org.apach|
00000070  65 2e 68 61 64 6f 6f 70  2e 69 6f 2e 63 6f 6d 70  |e.hadoop.io.comp|
00000080  72 65 73 73 2e 44 65 66  61 75 6c 74 43 6f 64 65  |ress.DefaultCode|
00000090  63 00 00 00 00 67 18 80  e0 41 fe df ee 0f 68 7a  |c....g...A....hz|
000000a0  d9 8f dd a5 d8 00 00 00  20 00 00 00 0c 00 00 00  |........ .......|
000000b0  08 07 00 00 00 03 6b 65  79 78 9c 63 60 60 e0 62  |......keyx.c``.b|
000000c0  00 02 d6 b2 c4 9c d2 54  00 06 ff 02 2d 00 00 00  |.......T....-...|
000000d0  22 00 00 00 0d 00 00 00  09 07 00 00 00 04 6b 65  |&#34;.............ke|
000000e0  79 32 78 9c 63 60 60 e0  66 00 02 b6 b2 c4 9c d2  |y2x.c``.f.......|
000000f0  54 23 00 09 71 02 61                              |T#..q.a|&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;read-and-write-sequencefiles&#34; tabindex=&#34;-1&#34;&gt;Read and Write &lt;code&gt;SequenceFile&lt;/code&gt;s&lt;/h2&gt;
&lt;p&gt;To run a job that only sees typed bytes but uses &lt;code&gt;SequenceFile&lt;/code&gt;s to contain both
input and output, start it like so:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming-2.0.0-cdh4.4.0.jar &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-D&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;mapred.map.tasks&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-io&lt;/span&gt; typedbytes &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-inputformat&lt;/span&gt; org.apache.hadoop.mapred.SequenceFileInputFormat &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-outputformat&lt;/span&gt; org.apache.hadoop.mapred.SequenceFileOutputFormat &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-mapper&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;/usr/bin/tee /tmp/seq.debug&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-reducer&lt;/span&gt; org.apache.hadoop.mapred.lib.IdentityReducer &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-input&lt;/span&gt; string_bytes.seq &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-output&lt;/span&gt; hello-output.seq&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-D mapred.map.tasks=1&lt;/code&gt; tells Hadoop to only start a single mapper, which is
necessary to prevent the intermediate debug file (&lt;code&gt;/tmp/seq.debug&lt;/code&gt;) from being
mangled while being written to by multiple processes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-mapper &amp;quot;/usr/bin/tee -a /tmp/seq.debug&lt;/code&gt; allows us to see what was passed to
the mapper after it was run.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-io typedbytes&lt;/code&gt; tells Hadoop to use
&lt;code&gt;org.apache.hadoop.typedbytes.TypedBytesWritable&lt;/code&gt; as key and value classes
(instead of &lt;code&gt;org.apache.hadoop.io.Text&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-inputformat org.apache.hadoop.mapred.SequenceFileInputFormat&lt;/code&gt; and
&lt;code&gt;-outputformat org.apache.hadoop.mapred.SequenceFileOutputFormat&lt;/code&gt; tell Hadoop
that our input and output files are &lt;code&gt;SequenceFile&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;The end result of this incantation is that the mapper is called with a stream
of typed bytes containing &lt;em&gt;no&lt;/em&gt; delimiters. In this context, a record consists
of a pair of typed bytes.&lt;/p&gt;
&lt;p&gt;This is the intermediate representation of the &lt;code&gt;SequenceFile&lt;/code&gt; we prepared
above, as seen by our mapper. Look familiar? It matches the bytes we created
above:&lt;/p&gt;
&lt;pre class=&#34;language-text&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;00000000  07 00 00 00 03 6b 65 79  00 00 00 00 05 76 61 6c  |.....key.....val|
00000010  75 65 07 00 00 00 04 6b  65 79 32 00 00 00 00 06  |ue.....key2.....|
00000020  76 61 6c 75 65 32                                 |value2|&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we didn&#39;t specify that compression should be used, the resulting
&lt;code&gt;SequenceFile&lt;/code&gt; is slightly different than the one that was created with
&lt;code&gt;loadtb&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-text&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;00000000  53 45 51 06 2f 6f 72 67  2e 61 70 61 63 68 65 2e  |SEQ./org.apache.|
00000010  68 61 64 6f 6f 70 2e 74  79 70 65 64 62 79 74 65  |hadoop.typedbyte|
00000020  73 2e 54 79 70 65 64 42  79 74 65 73 57 72 69 74  |s.TypedBytesWrit|
00000030  61 62 6c 65 2f 6f 72 67  2e 61 70 61 63 68 65 2e  |able/org.apache.|
00000040  68 61 64 6f 6f 70 2e 74  79 70 65 64 62 79 74 65  |hadoop.typedbyte|
00000050  73 2e 54 79 70 65 64 42  79 74 65 73 57 72 69 74  |s.TypedBytesWrit|
00000060  61 62 6c 65 00 00 00 00  00 00 7d 88 7f 43 96 14  |able......}..C..|
00000070  6b 83 15 d9 dc aa d6 ec  99 1c 00 00 00 1a 00 00  |k...............|
00000080  00 0c 00 00 00 08 07 00  00 00 03 6b 65 79 00 00  |...........key..|
00000090  00 0a 00 00 00 00 05 76  61 6c 75 65 00 00 00 1c  |.......value....|
000000a0  00 00 00 0d 00 00 00 09  07 00 00 00 04 6b 65 79  |.............key|
000000b0  32 00 00 00 0b 00 00 00  00 06 76 61 6c 75 65 32  |2.........value2|&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To dump the typed bytes contained in that &lt;code&gt;SequenceFile&lt;/code&gt;, use &lt;code&gt;dumptb&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming-2.0.0-cdh4.4.0.jar &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  dumptb hello-output.seq/part-00000 &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; hello-output.tb&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;read-text-and-write-sequencefiles&#34; tabindex=&#34;-1&#34;&gt;Read Text and Write &lt;code&gt;SequenceFile&lt;/code&gt;s&lt;/h2&gt;
&lt;p&gt;In some circumstances you may find yourself with text input (the output from
a Hive query, for example) and wish to produce binary output (images, say).
This works, but it&#39;s not obvious. The key is to &lt;em&gt;not&lt;/em&gt; use &lt;code&gt;-io typedbytes&lt;/code&gt; and
instead to be explicit about which stages of your workflow consume and produce
typed bytes using &lt;code&gt;-D stream.&amp;lt;stage&amp;gt;.&amp;lt;input|output&amp;gt;=typedbytes&lt;/code&gt;. More on this
shortly.&lt;/p&gt;
&lt;p&gt;First, create a text file containing tab-separated values and copy it to HDFS for use
as the input to your job:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-e&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;key&lt;span class=&#34;token entity&#34; title=&#34;\t&#34;&gt;\t&lt;/span&gt;value&lt;span class=&#34;token entity&#34; title=&#34;\n&#34;&gt;\n&lt;/span&gt;key2&lt;span class=&#34;token entity&#34; title=&#34;\t&#34;&gt;\t&lt;/span&gt;value2&#34;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; hello.tsv
hdfs dfs &lt;span class=&#34;token parameter variable&#34;&gt;-put&lt;/span&gt; hello.tsv&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Write a mapper that outputs typed bytes. This is &lt;code&gt;convert.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-javascript&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; split &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;split&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    through &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;through&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;token function-variable function&#34;&gt;prepare&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token parameter&#34;&gt;typeCode&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; value&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
  value &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;value&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; len &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  len&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;writeInt32BE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;value&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;length&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; Buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;concat&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;typeCode&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    len&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    value
  &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

process&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stdin
  &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pipe&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pipe&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;through&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token parameter&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;line&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;length &lt;span class=&#34;token operator&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; parts &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; line&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;\t&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
        key &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; parts&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
        value &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; parts&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;// output key as a string&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;queue&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; key&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;// output value as bytes&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;queue&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; value&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pipe&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;process&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stdout&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a JAR containing the artifacts needed to execute the job and copy them
to HDFS (the cluster I&#39;m using doesn&#39;t have Node installed, so I&#39;ve added
&lt;code&gt;bin/node&lt;/code&gt; to my working directory so it will be shipped alongside the code):&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;jar cf typedbytes.jar &lt;span class=&#34;token builtin class-name&#34;&gt;.&lt;/span&gt;
hdfs dfs &lt;span class=&#34;token parameter variable&#34;&gt;-put&lt;/span&gt; typedbytes.jar&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To run the job, we&#39;ll need a different set of arguments:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming-2.0.0-cdh4.4.0.jar &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-archives&lt;/span&gt; hdfs:///user/cloudera/typedbytes.jar&lt;span class=&#34;token comment&#34;&gt;#typedbytes \&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-D&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;mapred.map.tasks&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-D&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;stream.map.output&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;typedbytes &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-D&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;stream.reduce.input&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;typedbytes &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-D&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;stream.reduce.output&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;typedbytes &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-outputformat&lt;/span&gt; org.apache.hadoop.mapred.SequenceFileOutputFormat &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-mapper&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;typedbytes/bin/node typedbytes/convert.js&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-reducer&lt;/span&gt; org.apache.hadoop.mapred.lib.IdentityReducer &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-input&lt;/span&gt; hello.tsv &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-output&lt;/span&gt; hello.tsv.seq&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, typed bytes only encompass ¾ of the values we&#39;re passing around,
so we need to explicitly tell Hadoop that using &lt;code&gt;-D stream.&amp;lt;stage&amp;gt;.&amp;lt;input|output&amp;gt;=typedbytes&lt;/code&gt;. If they were 100%, as above, we
could have used &lt;code&gt;-io typedbytes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hello.tsv.seq&lt;/code&gt; looks similar to the uncompressed output from earlier, although
if you look closely, there are some minor differences (which seem to be
differences in the &lt;code&gt;SequenceFile&lt;/code&gt;&#39;s metadata):&lt;/p&gt;
&lt;pre class=&#34;language-text&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;00000000  53 45 51 06 2f 6f 72 67  2e 61 70 61 63 68 65 2e  |SEQ./org.apache.|
00000010  68 61 64 6f 6f 70 2e 74  79 70 65 64 62 79 74 65  |hadoop.typedbyte|
00000020  73 2e 54 79 70 65 64 42  79 74 65 73 57 72 69 74  |s.TypedBytesWrit|
00000030  61 62 6c 65 2f 6f 72 67  2e 61 70 61 63 68 65 2e  |able/org.apache.|
00000040  68 61 64 6f 6f 70 2e 74  79 70 65 64 62 79 74 65  |hadoop.typedbyte|
00000050  73 2e 54 79 70 65 64 42  79 74 65 73 57 72 69 74  |s.TypedBytesWrit|
00000060  61 62 6c 65 00 00 00 00  00 00 c7 91 8e c1 6a bc  |able..........j.|
00000070  e3 bb 3a 03 86 40 3a 01  72 8b 00 00 00 1a 00 00  |..:..@:.r.......|
00000080  00 0c 00 00 00 08 07 00  00 00 03 6b 65 79 00 00  |...........key..|
00000090  00 0a 00 00 00 00 05 76  61 6c 75 65 00 00 00 1c  |.......value....|
000000a0  00 00 00 0d 00 00 00 09  07 00 00 00 04 6b 65 79  |.............key|
000000b0  32 00 00 00 0b 00 00 00  00 06 76 61 6c 75 65 32  |2.........value2|&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;output-binary-files&#34; tabindex=&#34;-1&#34;&gt;Output Binary Files&lt;/h2&gt;
&lt;p&gt;Before writing this, I had intended to produce a single logical file at the end
of my workflow (without a key) that could be fetched and used immediately.
However, when using MapReduce, the fundamental building blocks are
record-based, so all values must be accompanied by a key (unless one writes to
HDFS directly, presumably with &lt;code&gt;hdfs dfs -put - dest&lt;/code&gt; to stream from &lt;code&gt;stdin&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Working with smaller units of data (i.e. broken into records) facilitates the
use of subsequent jobs to perform further processing.&lt;/p&gt;
&lt;p&gt;With that under consideration, the most sensible approach (and one I&#39;ve seen
references to) appears to be the use of a &lt;code&gt;SequenceFile&lt;/code&gt; as a virtual
filesystem using keys as filenames. &lt;code&gt;dumptb&lt;/code&gt; will unpack an HDFS-hosted file
into one containing pairs of typed bytes which can be processed locally.&lt;/p&gt;
&lt;p&gt;Once you&#39;ve dumped some typed bytes to the local filesystem, you can unpack
records into individual files, named by key using &lt;code&gt;unpack.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming-2.0.0-cdh4.4.0.jar &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  dumptb hello-output.seq/part-00000 &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; hello-output.tb
&lt;span class=&#34;token function&#34;&gt;node&lt;/span&gt; unpack.js hello-output.tb&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;unpack.js&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;pre class=&#34;language-javascript&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;use strict&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; assert &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;assert&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    fs &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;fs&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    path &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;path&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// get the file containing typed bytes from the command line&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; tb &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; path&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;process&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;argv&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;slice&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pop&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; stats &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;statSync&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tb&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    fd &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;openSync&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tb&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;r&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    offset &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    keyType &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    keyLength &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    key&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    valueType &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    valueLength &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;offset &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; stats&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;size&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
  fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readSync&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;fd&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; keyType&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; offset&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  offset&lt;span class=&#34;token operator&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  assert&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;equal&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;keyType&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readSync&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;fd&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; keyLength&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; offset&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  offset &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  keyLength &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; keyLength&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readInt32BE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  key &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;keyLength&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readSync&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;fd&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; key&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; keyLength&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; offset&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  offset &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; keyLength&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readSync&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;fd&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; valueType&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; offset&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  offset&lt;span class=&#34;token operator&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  assert&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;equal&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;valueType&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readSync&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;fd&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; valueLength&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; offset&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  offset &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  valueLength &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; valueLength&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readInt32BE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;// pipe the subset of the file out&lt;/span&gt;
  fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;createReadStream&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tb&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token literal-property property&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; offset&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;token literal-property property&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; offset &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; valueLength &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;
  &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pipe&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;fs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;createWriteStream&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;./&#34;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; key&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  offset &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; valueLength&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

  console&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;%s %d&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; key&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;toString&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; valueLength&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>DIY Fusion Drives: A Data Point</title>
    <link href="https://mojodna.net/2013/09/24/fusion-drive-data-point.html"/>
    <updated>2013-09-24T00:00:00Z</updated>
    <id>https://mojodna.net/2013/09/24/fusion-drive-data-point.html</id>
    <content type="html">&lt;p&gt;In light of Apple having announced new iMacs today, I thought I&#39;d throw out
a data point to the internet.&lt;/p&gt;
&lt;p&gt;tl;dr: I&#39;ve used 2 DIY Fusion Drives on 2 different Macs for the past year,
they&#39;re awesome, but you should use a Samsung SSD (I have an 830 and an 840
Pro).&lt;/p&gt;
&lt;p&gt;Since last year&#39;s &lt;a href=&#34;https://www.anandtech.com/show/6679/a-month-with-apples-fusion-drive&#34;&gt;Fusion Drive
announcement&lt;/a&gt;
(and subsequent &lt;a href=&#34;https://jollyjinx.tumblr.com/post/34638496292/fusion-drive-on-older-macs-yes-since-apple-has&#34;&gt;&amp;quot;reverse
engineering&amp;quot;&lt;/a&gt;),
I&#39;ve been running 256MB SSD + 1TB HD Fusion drive setups on 2 Macs: a 2009 iMac
(with the SSD replacing the optical drive) and a 2012 Mac Mini (using the extra
bay after purchasing a second SATA cable). It&#39;s been awesome, especially after
sorting the kinks out. (No, no benchmarks, but it really does feel / act as
though it&#39;s an SSD-backed system, even when data needs to get shuffled around,
as in the case of processing a couple hundred gigs of GeoTIFFs.)&lt;/p&gt;
&lt;p&gt;The kinks: I&#39;d already upgraded the iMac with a 256GB Crucial M4 about a year
before, so I figured I would just back everything up and use that in
combination with the Apple-provided drive. Bad plan. I went through 2 M4s
(though probably not for the hardware problems they seemed to be manifesting)
before throwing in the towel and buying a Samsung SSD 840 Pro, which has been
completely problem-free since installing it. When I bought the Mini, I also
bought a Samsung 830 at the same time, which has been trouble-free since the
start (Apple uses, or used Samsung 830s at one point, so this seemed like the
best choice).&lt;/p&gt;
&lt;p&gt;With the M4s, large files (100GB+ VMware VMs) would spontaneously corrupt
(presumably the filesystem passed the ~256GB threshold) and no amount of
monkeying with Disk Utility could successfully repair it, ultimately
necessitating Time Machine restores on multiple occasions. Things would be
peachy for a week or 2 and then, bam, time to reinstall. No good. One Crucial
rep I talked to when initiating an RMA suggested that the M4 wasn&#39;t getting
a chance to run its garbage collection routines, probably due to the I/O
patterns resulting from shifting data between disks.&lt;/p&gt;
&lt;p&gt;Would I do it again? Definitely, though the move to PCIe SSDs complicates the
matter a bit. In the case of the Mini, it was borderline unusable (by my
standards, anyway) due to the I/O performance of the built-in 5400rpm drive,
even with an i7 and lots of RAM. With the Fusion drive? Incredibly pleasant
to use as my primary work machine for the last 9½ months.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Building Blocks of Real-Time Distribution</title>
    <link href="https://mojodna.net/2010/10/27/building-blocks-of-real-time-distribution.html"/>
    <updated>2010-10-27T00:00:00Z</updated>
    <id>https://mojodna.net/2010/10/27/building-blocks-of-real-time-distribution.html</id>
    <content type="html">&lt;p&gt;Back in July, I gave a talk about the &lt;a href=&#34;https://github.com/downloads/mojodna/mojodna.github.com/Building%20Blocks%20of%20Real-Time%20Distribution.pdf&#34;&gt;&amp;quot;Building Blocks of Real-Time
Distribution&amp;quot;&lt;/a&gt;
at the &lt;a href=&#34;http://geowebconference.org/&#34;&gt;GeoWeb 2010&lt;/a&gt; conference in Vancouver, BC.
It was pretty heavy on the buzzwords and the name dropping, so I created &lt;a href=&#34;https://mojodna.net/gw2010/&#34;&gt;an
accompanying page&lt;/a&gt; with links and
definitions/background in order to make the slides a bit more coherent. The PDF
itself also has a rough transcript of what I said in the talk, so that should
help too.&lt;/p&gt;
&lt;p&gt;In the talk, I attempted to cover the considerations and technology choices
when implementing real-time services in various environments with different
types of data. The talk focused on spatial data, but I think it remains
relevant, with the exception of one or two slides.&lt;/p&gt;
&lt;p&gt;I continue to pitch XMPP because I still think it&#39;s a good, if
over-complicated, solution. Presence tracking and the ability to run ephemeral
clients are seriously awesome features. However, the real momentum these days
seems to be behind streaming HTTP APIs and PubSubHubbub. Julien and
&lt;a href=&#34;https://superfeedr.com/&#34;&gt;SuperFeedr&lt;/a&gt; have done a great job adapting to the
changing landscape and shipping awesome features, so definitely check them out.&lt;/p&gt;
&lt;p&gt;That is all.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The OS X Spatial Stack</title>
    <link href="https://mojodna.net/2009/12/05/the-os-x-spatial-stack.html"/>
    <updated>2009-12-05T00:00:00Z</updated>
    <id>https://mojodna.net/2009/12/05/the-os-x-spatial-stack.html</id>
    <content type="html">&lt;p&gt;I&#39;ve been doing a bunch of spatial analysis and exploration recently. Where I
previously would have used a Debian or Ubuntu server (or VM) to experiment, I&#39;m
now using Snow Leopard exclusively.&lt;/p&gt;
&lt;p&gt;However, it&#39;s not all roses. As with most things, there are many paths to
installing and configuring software, not all of them complementary. What
follows is &lt;strong&gt;my&lt;/strong&gt; experience installing and configuring various tools and their
dependencies. I&#39;ve attempted to install as few duplicate dependencies as
possible (preferring Apple-provided versions) and to simplify the overall
experience in order that individual elements may be upgraded independently.&lt;/p&gt;
&lt;!--
## Virtualizing Snow Leopard

Writing instructions for system configuration is difficult when you&#39;re not
starting from a fresh slate, as you may inadvertently omit something that was
installed in another way. Virtualization is often a good solution for this, but
virtualizing OS X isn&#39;t as easy (or as legal) as it could be.

I did some searching around and found a few tricks for getting Leopard running
under VMware Fusion. These instructions were condensed from
http://blog.rectalogic.com/2008/08/virtualizing-mac-os-x-leopard-client.html
and were sufficient to get a running Snow Leopard VM under Fusion 2.0.5.

```bash
# cd /Library/Application\ Support/VMware\ Fusion/isoimages/
# mkdir original
# mv darwin.iso tools-key.pub *.sig  original/
# sed &#34;s/ServerVersion.plist/System.plist/g &#34; &lt; original/darwin.iso &gt; darwin.iso
# openssl genrsa -out tools-priv.pem  2048
  Generating RSA private key, 2048 bit long modulus
  .........................+++
  .................................................................+++
  e is 65537 (0x10001)

# openssl rsa -in tools-priv.pem -pubout -pubout -out tools-key.pub
writing RSA key

# for A in *.iso ; do openssl dgst -sha1 -sign tools-priv.pem &lt; $A &gt; $A.sig ; done
```

Unfortunately, upgrading to 10.6.2 consistently causes the VM to crash during
boot. I was hopeful that Fusion 3 would address this problem (since it claims
to have better support for Snow Leopard (Server) guests), but the 10.6.0 VM
won&#39;t boot at all (nor will it install using the voodoo above).
--&gt;
&lt;h2 id=&#34;xcode&#34; tabindex=&#34;-1&#34;&gt;Xcode&lt;/h2&gt;
&lt;p&gt;First, &lt;a href=&#34;https://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?bundleID=20505&#34;&gt;download and install Xcode
3.2.1&lt;/a&gt;.
The defaults are fine.&lt;/p&gt;
&lt;h2 id=&#34;homebrew&#34; tabindex=&#34;-1&#34;&gt;Homebrew&lt;/h2&gt;
&lt;p&gt;Next, install &lt;a href=&#34;https://github.com/mxcl/homebrew&#34;&gt;homebrew&lt;/a&gt;. I&#39;ve found that
homebrew is significantly lighter and easier than
&lt;a href=&#34;https://macports.org/&#34;&gt;MacPorts&lt;/a&gt; for installing additional open source
software, as it re-uses system versions of libraries wherever possible.&lt;/p&gt;
&lt;p&gt;Part of the homebrew philosophy is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OS X is designed to minimise sudo use, you only need it for real root-level
stuff. You know your /System and /usr are as clean and pure as the day you
bought your Mac because you didn&#39;t sudo. Sleep better at night! ... Lets face
it; Homebrew is not installing anything system-critical. Apple already did
that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Homebrew is typically installed in &lt;code&gt;/usr/local&lt;/code&gt;, so:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;mkdir&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-p&lt;/span&gt; /usr/local
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;chown&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-R&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;&lt;span class=&#34;token variable&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;whoami&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;`&lt;/span&gt;&lt;/span&gt; /usr/local&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(If you already had MySQL installed in &lt;code&gt;/usr/local&lt;/code&gt;, fix it: &lt;code&gt;sudo chown -R mysql:mysql /usr/local/mysql&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;Now, install homebrew:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local
&lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-L&lt;/span&gt; https://github.com/mxcl/homebrew/tarball/master &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; xz &lt;span class=&#34;token parameter variable&#34;&gt;--strip&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-C&lt;/span&gt; &lt;span class=&#34;token builtin class-name&#34;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;homebrew-provided-libraries&#34; tabindex=&#34;-1&#34;&gt;Homebrew-provided Libraries&lt;/h2&gt;
&lt;p&gt;While we&#39;re working with homebrew, let&#39;s install a dependencies that we&#39;ll
need later.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://pkg-config.freedesktop.org/&#34;&gt;&lt;code&gt;pkg-config&lt;/code&gt;&lt;/a&gt; is necessary to cleanly
compile PIL:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;brew &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; pkg-config&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&#39;ll want to make sure that &lt;code&gt;/usr/local/bin&lt;/code&gt; is in your &lt;code&gt;$PATH&lt;/code&gt; so that
&lt;code&gt;homebrew&lt;/code&gt;-installed binaries will get run.&lt;/p&gt;
&lt;h2 id=&#34;kyngchaos-frameworks-and-binaries&#34; tabindex=&#34;-1&#34;&gt;KyngChaos Frameworks and Binaries&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&#34;https://www.kyngchaos.com/&#34;&gt;KyngChaos Wiki&lt;/a&gt; is the holy grail for OS X
spatial downloads, as everything is cleanly packaged, up-to-date, and very well
organized.&lt;/p&gt;
&lt;p&gt;From the &lt;a href=&#34;https://www.kyngchaos.com/software:frameworks&#34;&gt;Unix Compatibility
Frameworks&lt;/a&gt; page, you&#39;ll want to
download and install:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FreeType&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GSL&lt;/strong&gt; - The GNU Scientific Library&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GDAL Complete&lt;/strong&gt; (this includes UnixImageIO (notably &lt;code&gt;jpeg&lt;/code&gt; and &lt;code&gt;libpng&lt;/code&gt;), PROJ,
GEOS, SQLite3 (including Spatialite), and GDAL)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These will install to &lt;code&gt;/Library/Frameworks&lt;/code&gt; and will set up pointers to their
corresponding Python packages in &lt;code&gt;/Library/Python/2.6/site-packages/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;From the &lt;a href=&#34;https://www.kyngchaos.com/software:qgis&#34;&gt;Qgis&lt;/a&gt; page, you&#39;ll want the
most up-to-date Qgis installer (non-standalone). If you intend to open
Spatialite data sources, you may need to install a more up-to-date version of
the SQLite3 Framework than is included in the GDAL Complete package (this is
generally applicable; if you want/need to live on the bleeding edge, install
frameworks individually).&lt;/p&gt;
&lt;p&gt;From the &lt;a href=&#34;https://www.kyngchaos.com/software:postgres&#34;&gt;PostgreSQL&lt;/a&gt; page,
download and install the newest versions of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt; (server + client)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PostGIS&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;mapnik&#34; tabindex=&#34;-1&#34;&gt;Mapnik&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a href=&#34;http://dbsgeo.com/&#34;&gt;Dane Springmeyer&lt;/a&gt; and others, Mapnik is now
&lt;a href=&#34;https://mapnik.org/news/2009/dec/16/osx_installers/&#34;&gt;available as a framework&lt;/a&gt;.
This means fewer &lt;code&gt;homebrew&lt;/code&gt; dependencies, a simple upgrade path, and
compatibility with 32-bit QGIS Python plugins. To install it, grab a Snow
Leopard build from the &lt;a href=&#34;http://dbsgeo.com/downloads/&#34;&gt;Mapnik OSX Downloads page&lt;/a&gt;
and run both the &lt;em&gt;Mapnik Framework&lt;/em&gt; and &lt;em&gt;Mapnik Python 2.6 System&lt;/em&gt; installers.&lt;/p&gt;
&lt;p&gt;If you&#39;d previously installed Mapnik from source, you should remove it from
your system before running the installer:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ &lt;span class=&#34;token function&#34;&gt;rm&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-rf&lt;/span&gt; /Library/Python/2.6/site-packages/mapnik
$ &lt;span class=&#34;token function&#34;&gt;rm&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-rf&lt;/span&gt; /usr/local/lib/mapnik
$ &lt;span class=&#34;token function&#34;&gt;rm&lt;/span&gt; /usr/local/lib/libmapnik.dylib&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also remove &lt;code&gt;boost&lt;/code&gt; and &lt;code&gt;icu&lt;/code&gt; with &lt;code&gt;homebrew&lt;/code&gt; if nothing else is using
them:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;brew uninstall boost
brew uninstall icu&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;quantumnik&#34; tabindex=&#34;-1&#34;&gt;Quantumnik&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://bitbucket.org/springmeyer/quantumnik/wiki/Home&#34;&gt;Quantumnik&lt;/a&gt; is a QGIS
plugin that allows Mapnik to be used for rendering. In addition, it will map
many QGIS styles to Mapnik styles and provides an interface for editing them. In
short, it&#39;s an excellent way to prototype your maps before starting up a batch
rendering job.&lt;/p&gt;
&lt;p&gt;To install Quantumnik, start up QGIS, enable the &lt;strong&gt;Plugin Installer&lt;/strong&gt; in the
Plugin Manager, choose &lt;strong&gt;Fetch Python Plugins&lt;/strong&gt;, add a new repository
(&lt;a href=&#34;http://qgis.dbsgeo.com/&#34;&gt;http://qgis.dbsgeo.com/&lt;/a&gt;), find it in the list, and check the box to enable it.&lt;/p&gt;
&lt;h2 id=&#34;python&#34; tabindex=&#34;-1&#34;&gt;Python&lt;/h2&gt;
&lt;p&gt;We&#39;ll start with the easy stuff and &lt;code&gt;easy_install&lt;/code&gt; the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flickr.API - Flickr has good geo data (I&#39;m biased)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://ipython.scipy.org/&#34;&gt;IPython&lt;/a&gt; - Interactive Python&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://code.google.com/p/mapnik-utils/wiki/Nik2Img&#34;&gt;nik2img&lt;/a&gt; - Mapnik
command-line utility.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://code.google.com/p/mapnik-utils/wiki/Nikweb&#34;&gt;Nikweb&lt;/a&gt; - Mapnik-based
&amp;quot;static maps API&amp;quot; (though it requires GeoJSON input to determine appropriate
bounding boxes and additional features)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://code.google.com/p/python-nose/&#34;&gt;nose&lt;/a&gt; - Unit testing framework&lt;/li&gt;
&lt;li&gt;numscons - scons for numpy/scipy--required for scipy installation&lt;/li&gt;
&lt;li&gt;readline - causes IPython to use readline instead of libedit, solving all
sorts of problems&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://tilecache.org/&#34;&gt;TileCache&lt;/a&gt; - WMS-C compliant tile server&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;easy_install Flickr.API
easy_install ipython
easy_install nik2img
easy_install Nikweb
easy_install nose
easy_install numscons
easy_install readline
easy_install TileCache&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;pil&#34; tabindex=&#34;-1&#34;&gt;PIL&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;http://www.pythonware.com/products/pil/&#34;&gt;PIL&lt;/a&gt; is the Python Imaging Library.
Download the current source kit and extract it to &lt;code&gt;/usr/local/src&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local/src
&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf ~/Downloads/Imaging-1.1.6.tar.gz
&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; Imaging-1.1.6/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since you previously installed &lt;code&gt;pkg-config&lt;/code&gt;, building it is straightforward:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;python setup.py &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;matplotlib&#34; tabindex=&#34;-1&#34;&gt;matplotlib&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;http://matplotlib.sourceforge.net/&#34;&gt;matplotlib&lt;/a&gt; is a plotting library for
Python. It can be used both programmatically and interactively.&lt;/p&gt;
&lt;p&gt;Download the source distribution from SourceForge and extract it to
&lt;code&gt;/usr/local/src&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local/src
&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf ~/Downloads/matplotlib-0.99.1.2.tar.gz
&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; matplotlib-0.99.1.1/ &lt;span class=&#34;token comment&#34;&gt;# why yes, this is a mistake in the pkg&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, this is straightforward:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;python setup.py &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;matplotlib includes the &lt;a href=&#34;http://matplotlib.sourceforge.net/basemap/doc/html/&#34;&gt;Basemap
Toolkit&lt;/a&gt;, which makes it
easier to work with cartographic data.&lt;/p&gt;
&lt;p&gt;Download the source distribution from
&lt;a href=&#34;https://sourceforge.net/projects/matplotlib/files/matplotlib-toolkits/&#34;&gt;SourceForge&lt;/a&gt;
and extract it to &lt;code&gt;/usr/local/src&lt;/code&gt;. While you&#39;re at it, download &lt;code&gt;natgrid&lt;/code&gt; from
the same place.&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local/src
&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf ~/Downloads/basemap-0.99.4.tar.gz
&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf ~/Downloads/natgrid-0.1.tar.gz&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Basemap needs to know where GEOS was installed:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; basemap-0.99.4/
&lt;span class=&#34;token assign-left variable&#34;&gt;GEOS_DIR&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/Library/Frameworks/GEOS.framework/unix python setup.py &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;natgrid&lt;/code&gt; is straightforward:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; natgrid-0.1/
python setup.py &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;numpy-%2F-scipy&#34; tabindex=&#34;-1&#34;&gt;numpy / scipy&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.scipy.org/&#34;&gt;numpy and scipy&lt;/a&gt; are the Pythonic chainsaws for
scientific computing. numpy will mostly install out-of-the-box, but we&#39;ll
install it from source.&lt;/p&gt;
&lt;p&gt;Before we can build them, we need to install a Fortran compiler from &lt;a href=&#34;http://r.research.att.com/tools/&#34;&gt;AT&amp;amp;T
Research&lt;/a&gt;:
&lt;a href=&#34;http://r.research.att.com/gfortran-4.2.3.dmg&#34;&gt;http://r.research.att.com/gfortran-4.2.3.dmg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Download source distributions of numpy and scipy from
SourceForge(&lt;a href=&#34;https://sourceforge.net/projects/numpy/files/&#34;&gt;numpy&lt;/a&gt;,
&lt;a href=&#34;https://sourceforge.net/projects/scipy/files/&#34;&gt;scipy&lt;/a&gt;) and extract them to
&lt;code&gt;/usr/local/src&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local/src
&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf ~/Downloads/numpy-1.3.0.tar.gz
&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf ~/Downloads/scipy-0.7.1.tar.gz&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build and install numpy:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ &lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; numpy-1.3.0/
$ &lt;span class=&#34;token assign-left variable&#34;&gt;LDFLAGS&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;-lgfortran -arch x86_64&#34;&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;FFLAGS&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;-arch x86_64&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    python setup.py &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build and install scipy:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ &lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; scipy-0.7.1/
$ &lt;span class=&#34;token assign-left variable&#34;&gt;LDFLAGS&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;-lgfortran -arch x86_64&#34;&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;FFLAGS&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;-arch x86_64&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    python setup.py &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: the GDAL installer from KyngChaos bundles a version of numpy in
&lt;code&gt;/Library/Frameworks/GDAL.framework/Versions/1.6/Python/site-packages/numpy/&lt;/code&gt;,
so if you run into weirdness, it&#39;s probably safe to delete that one.&lt;/p&gt;
&lt;p&gt;Note: scipy recommends installing &lt;code&gt;fftw&lt;/code&gt; for fast Fourier transformations; I was
unable to get scipy to build when it was installed (using homebrew), so I&#39;m
omitting that step.&lt;/p&gt;
&lt;h3 id=&#34;cascadenik&#34; tabindex=&#34;-1&#34;&gt;Cascadenik&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;http://code.google.com/p/mapnik-utils/wiki/Cascadenik&#34;&gt;Cascadenik&lt;/a&gt; is a
pre-processor that converts CSS-like syntax into Mapnik XML configuration
files. It supports handy shorthand such as &amp;quot;&lt;code&gt;.thing[zoom &amp;gt; 12] { ... }&lt;/code&gt;&amp;quot; for
targeting standard slippy zoom levels and generally makes it easier to maintain
map styles. &lt;code&gt;nik2img&lt;/code&gt; supports Cascadenik styling natively, making it easier to
create and tweak styles (otherwise you&#39;ll need to run &lt;code&gt;cascadenik-compile.py&lt;/code&gt;
manually).&lt;/p&gt;
&lt;p&gt;First, check out Cascadenik from Google Code:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local/src
svn co http://mapnik-utils.googlecode.com/svn/trunk/serverside/cascadenik&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, build and install it:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; cascadenik
python setup.py &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will automatically download and install &lt;code&gt;cssutils&lt;/code&gt; for you before making
&lt;code&gt;cascadenik-compile.py&lt;/code&gt; and &lt;code&gt;cascadenik-style.py&lt;/code&gt; available in your &lt;code&gt;$PATH&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;openstreetmap&#34; tabindex=&#34;-1&#34;&gt;OpenStreetMap&lt;/h2&gt;
&lt;p&gt;When working with data from &lt;a href=&#34;https://openstreetmap.org/&#34;&gt;OpenStreetMap&lt;/a&gt;,
&lt;a href=&#34;https://wiki.openstreetmap.org/wiki/Osm2pgsql&#34;&gt;&lt;code&gt;osm2pgsql&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&#34;https://wiki.openstreetmap.org/wiki/Osmosis&#34;&gt;Osmosis&lt;/a&gt; are critical.&lt;/p&gt;
&lt;p&gt;To install &lt;code&gt;osm2pgsql&lt;/code&gt;, check it out from OSM&#39;s subversion repository and use
&lt;code&gt;make&lt;/code&gt; to build and install it, linking against the KyngChaos framework:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ &lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local/src
$ svn co https://svn.openstreetmap.org/applications/utils/export/osm2pgsql/
$ &lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; osm2pgsql
$ &lt;span class=&#34;token assign-left variable&#34;&gt;&lt;span class=&#34;token environment constant&#34;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token environment constant&#34;&gt;$PATH&lt;/span&gt;:/Library/Frameworks/GEOS.framework/unix/bin/ &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token assign-left variable&#34;&gt;CFLAGS&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;-I/Library/Frameworks/PROJ.framework/unix/include&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token assign-left variable&#34;&gt;LDFLAGS&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;-L/Library/Frameworks/PROJ.framework/unix/lib/&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;make&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&#39;ll have to install it by hand to &lt;code&gt;/usr/local&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-m&lt;/span&gt; 0755 osm2pgsql /usr/local/bin
&lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; default.style /usr/local/share/osm2pgsql&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;default.style&lt;/code&gt; is the standard map of OSM tags to database columns; you&#39;ll
need it when you run an import, even if the defaults are fine.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://wiki.openstreetmap.org/wiki/Osmosis&#34;&gt;Osmosis&lt;/a&gt; is a filter-driven tool
for processing large volumes of OSM XML.&lt;/p&gt;
&lt;p&gt;To begin, download the &lt;a href=&#34;https://dev.openstreetmap.org/~bretth/osmosis-build/osmosis-latest.tar.gz&#34;&gt;latest version of
Osmosis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Extract it to &lt;code&gt;/usr/local&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local
&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf ~/Downloads/osmosis-latest-bin.tar.gz&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, create a symlink to the binary:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;ln&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-s&lt;/span&gt; /usr/local/osmosis-0.36/bin/osmosis /usr/local/bin/osmosis&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;postgresql-additions&#34; tabindex=&#34;-1&#34;&gt;PostgreSQL Additions&lt;/h2&gt;
&lt;p&gt;At present, the KyngChaos distribution of PostgreSQL does not include the
&lt;a href=&#34;https://www.postgresql.org/docs/current/static/intarray.html&#34;&gt;intarray&lt;/a&gt; module
(you can check &lt;code&gt;/usr/local/pgsql/share/contrib&lt;/code&gt; to see if it&#39;s been added).
&lt;code&gt;osm2pgsql&lt;/code&gt; uses this module to create updatable Postgres OSM mirrors (meaning
that you can stay up-to-date by applying &lt;a href=&#34;https://planet.openstreetmap.org/&#34;&gt;planet
diffs&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This means that we need to build it ourselves. First, &lt;a href=&#34;https://www.postgresql.org/ftp/source/v8.4.5/&#34;&gt;download the source
tarball corresponding to the version you already have
installed&lt;/a&gt;. Next, extract it to
&lt;code&gt;/usr/local/src&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /usr/local/src
&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; zxf ~/Downloads/postgresql-8.4.5.tar.bz2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Configure Postgres:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; postgres-8.4.5
./configure&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change to the &lt;em&gt;intarray&lt;/em&gt; contrib directory and &lt;code&gt;make&lt;/code&gt; it:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; contrib/intarray
&lt;span class=&#34;token builtin class-name&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;&lt;span class=&#34;token environment constant&#34;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/usr/local/pgsql/bin:&lt;span class=&#34;token environment constant&#34;&gt;$PATH&lt;/span&gt;
&lt;span class=&#34;token builtin class-name&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;USE_PGXS&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;make&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;make&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(This same process can be repeated for other extensions that you desire.)&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;intarray&lt;/code&gt; now installed, you can enable the database of your choosing
(&lt;code&gt;osm&lt;/code&gt; in this case):&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-u&lt;/span&gt; postgres psql &lt;span class=&#34;token parameter variable&#34;&gt;-d&lt;/span&gt; osm &lt;span class=&#34;token parameter variable&#34;&gt;-f&lt;/span&gt; /usr/local/pgsql/share/contrib/_int.sql&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;gui-utilities&#34; tabindex=&#34;-1&#34;&gt;GUI Utilities&lt;/h2&gt;
&lt;p&gt;For graphical management of spatial data beyond what Qgis can handle, I usually
use &lt;a href=&#34;https://menial.co.uk/software/base/&#34;&gt;Base&lt;/a&gt; (for SQLite databases) and
&lt;a href=&#34;https://www.pgadmin.org/&#34;&gt;pgAdmin&lt;/a&gt; (for PostgreSQL databases).&lt;/p&gt;
&lt;h2 id=&#34;congratulations&#34; tabindex=&#34;-1&#34;&gt;Congratulations&lt;/h2&gt;
&lt;p&gt;You now have a working spatial stack on OS X! There are certainly things missed
here, but I&#39;ll try to keep this up-to-date as I discover new tools and simpler
methods (and in response to your comments, dear reader).&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>CSSpring Cleaning</title>
    <link href="https://mojodna.net/2009/09/08/csspring-cleaning.html"/>
    <updated>2009-09-08T00:00:00Z</updated>
    <id>https://mojodna.net/2009/09/08/csspring-cleaning.html</id>
    <content type="html">&lt;p&gt;I was talking to some front-end devs a couple weeks ago about maintaining CSS
on an evolving website. In particular, how to figure out which rules are no
longer being applied (remnants from experiments / old designs / whatever) and
remove them.&lt;/p&gt;
&lt;p&gt;I remembered seeing something a few months ago that claimed to do this. After
digging around on &lt;a href=&#34;https://github.com/&#34;&gt;GitHub&lt;/a&gt; for a bit, I re-found
&lt;a href=&#34;https://github.com/aanand/deadweight&#34;&gt;deadweight&lt;/a&gt;. Unfortunately, after
looking at it briefly, I realized that it made some pretty strong assumptions,
mainly about being run in the context of a Rails app.&lt;/p&gt;
&lt;p&gt;Time for some hacking.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mojodna/deadweight&#34;&gt;My fork&lt;/a&gt; can be installed with
RubyGems (&lt;code&gt;0.0.5&lt;/code&gt; is required for &lt;em&gt;deflate&lt;/em&gt; and &lt;em&gt;gzip&lt;/em&gt; content-encodings):&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; gem &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; mojodna-deadweight &lt;span class=&#34;token parameter variable&#34;&gt;-s&lt;/span&gt; https://gems.github.com/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first step was to make it accessible as a command-line utility (ideally in
the spirit of the &lt;a href=&#34;https://en.wikipedia.org/wiki/Unix_philosophy&#34;&gt;UNIX
Philosophy&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;deadweight &lt;span class=&#34;token parameter variable&#34;&gt;-s&lt;/span&gt; styles.css &lt;span class=&#34;token parameter variable&#34;&gt;-s&lt;/span&gt; ie.css index.html about.html&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(This will check &lt;code&gt;styles.css&lt;/code&gt; and &lt;code&gt;ie.css&lt;/code&gt; against &lt;code&gt;index.html&lt;/code&gt; and
&lt;code&gt;about.html&lt;/code&gt;, outputting unused rules.)&lt;/p&gt;
&lt;p&gt;It also supports input from pipes, meaning that you can chain it together and
filter orphaned rules without writing them to a file (you&#39;ll have to configure
logging in order to limit output).&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;cat&lt;/span&gt; styles.css &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; deadweight index.html&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deadweight now contains an experimental &lt;code&gt;-L&lt;/code&gt; argument that causes it to use
&lt;a href=&#34;https://github.com/defunkt/lyndon&#34;&gt;Lyndon&lt;/a&gt; if the &lt;code&gt;lyndon&lt;/code&gt; executable is in
your &lt;code&gt;$PATH&lt;/code&gt; (only possible on OS X with MacRuby installed). It was a bit
flaky in my testing, but I may be using an older version of MacRuby. Great
idea though. &lt;a href=&#34;http://ozmm.org/posts/lyndon.html&#34;&gt;Check this post for more info on what it does / how it
works.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The next step was to expose it as an HTTP proxy. It uses &lt;code&gt;8002&lt;/code&gt; by default for
no particular reason.
(&lt;a href=&#34;/2009/08/21/exploring-oauth-protected-apis.html&#34;&gt;oauth-proxy&lt;/a&gt; uses &lt;code&gt;8001&lt;/code&gt;.)&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;deadweight &lt;span class=&#34;token parameter variable&#34;&gt;-l&lt;/span&gt; deadweight.log &lt;span class=&#34;token parameter variable&#34;&gt;-s&lt;/span&gt; styles.css &lt;span class=&#34;token parameter variable&#34;&gt;-w&lt;/span&gt; https://github.com/ &lt;span class=&#34;token parameter variable&#34;&gt;-P&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(This dumps log output to &lt;code&gt;deadweight.log&lt;/code&gt; in &lt;code&gt;$PWD&lt;/code&gt; and matches
&lt;code&gt;https://github.com/*&lt;/code&gt; against &lt;code&gt;styles.css&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;It&#39;s probably most useful as an HTTP proxy; you can start it with a list of
target stylesheets, configure your browser to load pages through it (sorry, it
gets &lt;strong&gt;really&lt;/strong&gt; slow when requesting &lt;em&gt;gzip&lt;/em&gt;- or &lt;em&gt;deflate&lt;/em&gt;-encoded pages, which
is a lot of the time), and watch the output to see how many rules it&#39;s hit.
When you&#39;re done, hit &lt;code&gt;^C&lt;/code&gt; to stop it and (optionally) output the remaining
CSS rules into a file for you to examine (&lt;code&gt;-o orphans.css&lt;/code&gt; will do this for
you when it shuts down).&lt;/p&gt;
&lt;p&gt;If you&#39;re lucky, the file containing orphaned CSS will be empty. If not,
re-start the proxy with the orphaned CSS as the target and continue browsing
until you&#39;re ready to start looking for matches by hand (this is necessary, as
it won&#39;t catch classes applied by JavaScript).&lt;/p&gt;
&lt;h2 id=&#34;things-to-improve&#34; tabindex=&#34;-1&#34;&gt;Things to Improve&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/WEBrick&#34;&gt;WEBrick&lt;/a&gt;&#39;s HTTP proxy implementation
leaves a lot to be desired (it&#39;s nowhere on the same level as
&lt;a href=&#34;https://twistedmatrix.com/trac/&#34;&gt;Twisted&lt;/a&gt;&#39;s), but it&#39;s the only game in town.
&lt;a href=&#34;https://www.igvita.com/&#34;&gt;Ilya Grigorik&lt;/a&gt; has done some excellent work with
&lt;a href=&#34;https://github.com/igrigorik/em-proxy&#34;&gt;EventMachine and proxies&lt;/a&gt;, but it
doesn&#39;t appear that anyone has built a general-purpose HTTP proxy that easily
supports header modification. It&#39;s not a particularly hard problem and being
able to move deadweight&#39;s proxy implementation to such a base would provide
the ability to turn off compression and thus speed things up considerably.&lt;/p&gt;
&lt;p&gt;One could also imagine a world in which the proxy server also contains a web
interface that displays unmatched CSS rules and updates on the fly.
EventMachine would do the trick here, too.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Exploring OAuth-Protected APIs</title>
    <link href="https://mojodna.net/2009/08/21/exploring-oauth-protected-apis.html"/>
    <updated>2009-08-21T00:00:00Z</updated>
    <id>https://mojodna.net/2009/08/21/exploring-oauth-protected-apis.html</id>
    <content type="html">&lt;p&gt;From time to time I need to debug OAuth-protected APIs, checking response
headers and examining XML and JSON payloads. &lt;code&gt;curl&lt;/code&gt; generally rocks for this
sort of thing, but when the APIs in question are protected with OAuth, things
break down. Likewise for benchmarking (&lt;code&gt;ab&lt;/code&gt;, &lt;code&gt;httperf&lt;/code&gt;, etc.) and
exploration--isn&#39;t it nice to browse APIs that return XML in Firefox?&lt;/p&gt;
&lt;p&gt;This needn&#39;t to be the case.&lt;/p&gt;
&lt;h2 id=&#34;enter-oauth-proxy&#34; tabindex=&#34;-1&#34;&gt;Enter &lt;code&gt;oauth-proxy&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This is why I wrote
&lt;a href=&#34;https://github.com/mojodna/oauth-proxy&#34;&gt;&lt;code&gt;oauth-proxy&lt;/code&gt;&lt;/a&gt;. It does what it
says on the tin: it acts a proxy server that transparently adds OAuth headers
to requests.&lt;/p&gt;
&lt;p&gt;There are 2 steps to using it. First, install it:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;easy_install oauth-proxy&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, start it:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ oauth-proxy &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;--token &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;token&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;--token-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;token secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;-p &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;proxy port&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;--ssl&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re accessing a resource that only requires 2-legged OAuth, you can omit
&lt;code&gt;--token&lt;/code&gt; and &lt;code&gt;--token-secret&lt;/code&gt;. The proxy port defaults to &lt;code&gt;8001&lt;/code&gt;, and &lt;code&gt;--ssl&lt;/code&gt;
is used if you&#39;re proxying connections to a provider that requires SSL (Fire
Eagle, for example).&lt;/p&gt;
&lt;p&gt;Once it&#39;s been started, use &lt;code&gt;curl&lt;/code&gt; to make requests through it:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-x&lt;/span&gt; localhost:8001 http://host.name/path&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also benchmark your APIs through it using ApacheBench (&lt;code&gt;ab&lt;/code&gt;, as it
includes support for HTTP proxies). Note that you are introducing additional
overhead by proxying the request, so your numbers may be a bit off.&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;ab &lt;span class=&#34;token parameter variable&#34;&gt;-X&lt;/span&gt; localhost:8001 http://host.name/path&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Firefox (and browsers in general) supports HTTP proxies, so you can add a
&amp;quot;Manual Proxy Configuration&amp;quot; and pass requests through &lt;code&gt;oauth-proxy&lt;/code&gt; to
explore APIs in the comfort of your favorite browser. By default, all requests
that Firefox makes will go through the proxy and be signed, which may confuse
other websites you visit (and will inadvertently reveal your consumer key and
access token but not the corresponding secrets).&lt;/p&gt;
&lt;h3 id=&#34;access-tokens-sold-separately&#34; tabindex=&#34;-1&#34;&gt;Access Tokens Sold Separately&lt;/h3&gt;
&lt;p&gt;If you&#39;re accessing a resource that requires 3-legged OAuth, you&#39;ll need a
token. You may already have one, but if you don&#39;t, you can use the &lt;a href=&#34;https://github.com/mojodna/oauth&#34;&gt;OAuth
library for Ruby&lt;/a&gt; to obtain one.&lt;/p&gt;
&lt;p&gt;First, install the gem (0.3.5 is current as of this writing):&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; gem &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; oauth&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, trigger the authorization process from the command-line:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ oauth &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --access-token-url http://host.name/path/to/access_token &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --authorize-url http://host.name/path/to/authorize &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --request-token-url http://host.name/path/to/request_token &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    authorize&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Follow the prompts, and voilà, an access token and secret that you can use
with &lt;code&gt;oauth-proxy&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;a-concrete-example&#34; tabindex=&#34;-1&#34;&gt;A Concrete Example&lt;/h3&gt;
&lt;p&gt;Twitter&#39;s popular, right? Let&#39;s use that.&lt;/p&gt;
&lt;p&gt;First, &lt;a href=&#34;https://twitter.com/apps/new&#34;&gt;register an application&lt;/a&gt; to get a
consumer key and secret. I registered as a &amp;quot;client&amp;quot; application, since the
command-line still doesn&#39;t have a callback url.&lt;/p&gt;
&lt;p&gt;Once that&#39;s done, you&#39;ll get a set of credentials that can be used to initiate
the authorization process.&lt;/p&gt;
&lt;p&gt;Let&#39;s authorize.&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ oauth &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  --consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  --consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  --access-token-url https://twitter.com/oauth/access_token &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  --authorize-url https://twitter.com/oauth/authorize &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  --request-token-url https://twitter.com/oauth/request_token &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  authorize&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After following the prompts, you&#39;ll get something back that looks like this:&lt;/p&gt;
&lt;pre class=&#34;language-yaml&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;&lt;span class=&#34;token key atrule&#34;&gt;oauth_token_secret&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;redacted&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;token key atrule&#34;&gt;oauth_token&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;redacted&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;token key atrule&#34;&gt;user_id&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;redacted&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;token key atrule&#34;&gt;screen_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;redacted&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can then use those values to start the OAuth proxy:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ oauth-proxy &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token parameter variable&#34;&gt;--token&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;access token&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --token-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;token secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&#39;re set. Let&#39;s go exploring:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ &lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-sx&lt;/span&gt; http://localhost:8001 &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    https://twitter.com/statuses/friends_timeline.json &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    jsonpretty &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; pygmentize &lt;span class=&#34;token parameter variable&#34;&gt;-l&lt;/span&gt; js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&#39;ll get exactly what you&#39;re expecting &lt;strong&gt;and&lt;/strong&gt; you&#39;ll be using OAuth (this
is a partially contrived example since Twitter still supports HTTP Base Auth).&lt;/p&gt;
&lt;p&gt;(&lt;a href=&#34;https://github.com/nicksieger/jsonpretty&#34;&gt;&lt;code&gt;jsonpretty&lt;/code&gt;&lt;/a&gt; rocks. &lt;code&gt;pygmentize&lt;/code&gt;
(&lt;code&gt;easy install pygments&lt;/code&gt;) makes it easier to make sense of the chaos.)&lt;/p&gt;
&lt;p&gt;That&#39;s all. I use this stuff all the time and can&#39;t imagine debugging APIs
without it.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Subscribing to Wordpress.com with Switchboard</title>
    <link href="https://mojodna.net/2009/07/21/subscribing-to-wordpress-com-with-switchboard.html"/>
    <updated>2009-07-21T00:00:00Z</updated>
    <id>https://mojodna.net/2009/07/21/subscribing-to-wordpress-com-with-switchboard.html</id>
    <content type="html">&lt;p&gt;Last week, Andy Skelton announced &lt;a href=&#34;https://andy.wordpress.com/2009/07/16/real-time-wordpress-com-subscription/&#34;&gt;Real-time Wordpress.com
subscriptions&lt;/a&gt;
via XMPP. It does IM, but more interestingly for me, it supports PubSub. Had I
realized this when he posted that, I would have included it as an example in
&lt;a href=&#34;https://mojodna.net/2009/07/16/switchboard-curl-for-xmpp.html&#34;&gt;switchboard : XMPP :: curl :
HTTP&lt;/a&gt; alongside
Fire Eagle and Superfeedr. What&#39;s done is done, however, so here&#39;s a quick
rundown of how to interact with &lt;a href=&#34;http://Wordpress.com&#34;&gt;Wordpress.com&lt;/a&gt; using Switchboard.&lt;/p&gt;
&lt;p&gt;First, let&#39;s get a basic idea of the identities and features that
&lt;a href=&#34;http://Wordpress.com&#34;&gt;Wordpress.com&lt;/a&gt; advertises:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard disco &lt;span class=&#34;token parameter variable&#34;&gt;--target&lt;/span&gt; pubsub.im.wordpress.com info
&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; Switchboard started.
Discovery Info &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; pubsub.im.wordpress.com
Identities:
  pubsub/service: Publish-Subscribe

Features:
  http://jabber.org/protocol/disco&lt;span class=&#34;token comment&#34;&gt;#info&lt;/span&gt;
  http://jabber.org/protocol/disco&lt;span class=&#34;token comment&#34;&gt;#items&lt;/span&gt;
  http://jabber.org/protocol/pubsub
  vcard-temp
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#access-authorize&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#access-open&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#access-presence&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#access-whitelist&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#auto-create&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#collections&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#config-node&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#create-and-configure&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#create-nodes&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#delete-items&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#delete-nodes&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#instant-nodes&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#item-ids&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#last-published&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#manage-subscriptions&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#member-affiliation&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#modify-affiliations&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#multi-subscribe&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#outcast-affiliation&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#persistent-items&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#presence-notifications&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#presence-subscribe&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#publish&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#publisher-affiliation&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#purge-nodes&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#retract-items&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#retrieve-affiliations&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#retrieve-default&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#retrieve-items&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#retrieve-subscriptions&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#subscribe&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#subscription-notifications&lt;/span&gt;
  http://jabber.org/protocol/pubsub&lt;span class=&#34;token comment&#34;&gt;#subscription-options&lt;/span&gt;
Shutdown initiated.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lo, it supports PubSub! (But it doesn&#39;t support node discovery, so we can&#39;t
get a list of available nodes.) Fortunately, Andy&#39;s post includes a couple.
Let&#39;s subscribe:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; pubsub.im.wordpress.com &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; /blogs/andy.wordpress.com subscribe
&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; Switchboard started.
Subscribe successful.
Shutdown initiated.

$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; pubsub.im.wordpress.com &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; /blogs/andy.wordpress.com/comments subscribe
&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; Switchboard started.
Subscribe successful.
Shutdown initiated.

$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; pubsub.im.wordpress.com &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
     /blogs/andy.wordpress.com/2009/07/16/real-time-wordpress-com-subscription/ &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    subscribe
&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; Switchboard started.
Subscribe successful.
Shutdown initiated.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Switchboard claimed that that worked, but let&#39;s double-check and list our
subscriptions:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; pubsub.im.wordpress.com subscriptions
&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; Switchboard started.
Subscriptions:
4E039BC19E028: me@jabber.org &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; /blogs/andy.wordpress.com &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;subscribed&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
4E039D2A2B691: me@jabber.org &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; /blogs/andy.wordpress.com/2009/07/16/real-time-wordpress-com-subscription &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;subscribed&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
4E039BC7E8A69: me@jabber.org &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; /blogs/andy.wordpress.com/comments &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;subscribed&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
Shutdown initiated.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Actually, there was no need to subscribe to comments for &amp;quot;Real-time
&lt;a href=&#34;http://Wordpress.com&#34;&gt;Wordpress.com&lt;/a&gt; subscription&amp;quot; because we&#39;re also subscribed to the comments
feed. Let&#39;s unsubscribe and check our subscriptions again:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; pubsub.im.wordpress.com &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
     /blogs/andy.wordpress.com/2009/07/16/real-time-wordpress-com-subscription &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    unsubscribe
&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; Switchboard started.
Unsubscribe was successful.
Shutdown initiated.

$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; pubsub.im.wordpress.com subscriptions
&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; Switchboard started.
Subscriptions:
4E039BC19E028: me@jabber.org &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; /blogs/andy.wordpress.com &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;subscribed&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
4E039BC7E8A69: me@jabber.org &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; /blogs/andy.wordpress.com/comments &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;subscribed&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
Shutdown initiated.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, all good. Now let&#39;s hope Andy posts something or gets a comment on
something so we can listen for it:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; pubsub.im.wordpress.com listen
&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;sample post and comment &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;event /&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; stanzas to come&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For bonus points, let&#39;s implement a &lt;code&gt;WordpressJack&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;WordpressJack&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;connect&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;switchboard&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; settings&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;plug&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;AutoAcceptJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; NotifyJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; PubSubJack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;hook&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:new_post&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:new_comment&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

    switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_pubsub_event &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;event&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
      event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;payload&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;payload&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
        payload&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;elements&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;item&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
          &lt;span class=&#34;token comment&#34;&gt;# grab the feed out of the payload&lt;/span&gt;
          feed &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; item&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;first_element&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;feed&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

          &lt;span class=&#34;token comment&#34;&gt;# TODO determine whether it&#39;s a comment or a post&lt;/span&gt;
          &lt;span class=&#34;token comment&#34;&gt;# for now, we&#39;ll assume it&#39;s a post&lt;/span&gt;
          &lt;span class=&#34;token comment&#34;&gt;# invoke the :new_post hook with the feed subtree&lt;/span&gt;
          &lt;span class=&#34;token comment&#34;&gt;# (later: parse it with FeedTools or something)&lt;/span&gt;
          on&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:new_post&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; feed&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
      &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a quick-and-dirty consumer:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;#!/usr/bin/env ruby -rubygems&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;switchboard&#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;wordpress_jack&#39;&lt;/span&gt;&lt;/span&gt;

switchboard &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Switchboard&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Client&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;
switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;plug&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;WordpressJack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_new_post &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;post&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;A new post was received:&#34;&lt;/span&gt;&lt;/span&gt;
  puts post
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_new_comment &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;comment&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;A new comment was received:&#34;&lt;/span&gt;&lt;/span&gt;
  puts comment
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;run&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a number of improvements that can be made here (this is the &amp;quot;making
blind assumptions about responses&amp;quot; version). Go wild.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Switchboard as a Framework</title>
    <link href="https://mojodna.net/2009/07/19/switchboard-as-a-framework.html"/>
    <updated>2009-07-19T00:00:00Z</updated>
    <id>https://mojodna.net/2009/07/19/switchboard-as-a-framework.html</id>
    <content type="html">&lt;p&gt;While Switchboard is a &lt;a href=&#34;https://mojodna.net/2009/07/16/switchboard-curl-for-xmpp.html&#34;&gt;useful tool for debugging and probing XMPP
services&lt;/a&gt;, it&#39;s
also a convenient and full-featured framework for building clients and
components.&lt;/p&gt;
&lt;h2 id=&#34;a-simple-client&#34; tabindex=&#34;-1&#34;&gt;A Simple Client&lt;/h2&gt;
&lt;p&gt;This is the simplest client (bot) I could think of. It listens for input and
replies with whatever was sent in the first place.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;#!/usr/bin/env ruby -rubygems&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;switchboard&#39;&lt;/span&gt;&lt;/span&gt;

switchboard &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Switchboard&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Client&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;
switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;plug&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;AutoAcceptJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; EchoJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; NotifyJack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;run&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s break it down.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;#!/usr/bin/env ruby -rubygems&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;switchboard&#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Um, I hope this is pretty straightforward.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;switchboard &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Switchboard&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Client&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This instantiates &lt;code&gt;Switchboard::Client&lt;/code&gt; with some default options, mainly
&lt;code&gt;spin = true&lt;/code&gt; (this is the 2nd argument). &lt;code&gt;spin&lt;/code&gt; means that &lt;code&gt;#run!&lt;/code&gt; will cause
the process to run in a loop and not return immediately. &lt;code&gt;^C&lt;/code&gt; will interrupt
the process and shut it down cleanly. (&lt;code&gt;^C&lt;/code&gt; a second time if it hangs.)&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;plug&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;AutoAcceptJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; EchoJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; NotifyJack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the meat of it, even though it doesn&#39;t look like it at first glance.
&lt;em&gt;Jack&lt;/em&gt;s are the basic units of shared functionality. More later, but the basic
rundown is that &lt;code&gt;AutoAcceptJack&lt;/code&gt; auto-accepts (and reciprocates) roster
additions (&amp;quot;friend requests&amp;quot;), &lt;code&gt;EchoJack&lt;/code&gt; does the heavy lifting of echoing
input back, and &lt;code&gt;NotifyJack&lt;/code&gt; sends presence (i.e. &amp;quot;I&#39;m online&amp;quot;) notifications
to everyone (and everything) in your roster.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;EchoJack&lt;/code&gt; (&lt;code&gt;lib/switchboard/jacks/echo.rb&lt;/code&gt;) looks like this:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EchoJack&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;connect&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;switchboard&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; settings&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_message &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;message&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
      stream&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;send&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;answer&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;ll get to the details of &lt;code&gt;self.connect&lt;/code&gt; shortly, but the gist of it is that
the &lt;code&gt;EchoJack&lt;/code&gt; registers an &lt;code&gt;on_message&lt;/code&gt; callback and uses &lt;code&gt;Jabber::Message&lt;/code&gt;&#39;s
&lt;code&gt;#answer&lt;/code&gt; method to create a response (inverting the sender and receiver)
before sending it back on the stream (client or component connection, as the
case may be).&lt;/p&gt;
&lt;p&gt;Implementing the &lt;code&gt;EchoBot&lt;/code&gt; as a jack is perhaps overkill, but the goal was to
demonstrate how short and modular Switchboard apps can be. The alternate
implementation looks like this:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;plug&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;AutoAcceptJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; NotifyJack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_message &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;message&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  stream&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;send&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;answer&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Callbacks are executed in the context of the &lt;code&gt;switchboard&lt;/code&gt; object; this is
important to remember, as variables defined in a different scope will be
unavailable.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;run&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This kicks off the process of connecting to the server. As XMPP is an
asynchronous protocol, the different hooks will be called in response to
varying inputs.&lt;/p&gt;
&lt;h3 id=&#34;jacks-and-hooks&#34; tabindex=&#34;-1&#34;&gt;Jacks and Hooks&lt;/h3&gt;
&lt;p&gt;Jacks are extractions of standard functionality that are executed in the
context of the Switchboard core. Thus, references to &lt;code&gt;stream&lt;/code&gt;, etc. call
methods implemented by &lt;code&gt;Switchboard::Core&lt;/code&gt; and its subclasses rather than the
jack itself. If you need convenience methods, they should be defined on the
&lt;code&gt;switchboard&lt;/code&gt; object provided as the 1st argument to &lt;code&gt;connect&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;connect&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;switchboard&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; settings&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token class-name&#34;&gt;switchboard&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;helper_method&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# do something&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_message &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;message&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
    helper_method
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;connect&lt;/code&gt; is the entry point for all jacks. When the jack is plugged into
switchboard (using &lt;code&gt;Switchboard::Core#plug!&lt;/code&gt;), &lt;code&gt;connect&lt;/code&gt; is called with the
active Switchboard instance and its corresponding &lt;code&gt;Switchboard::Settings&lt;/code&gt;. You
can modify the settings in the body of &lt;code&gt;connect&lt;/code&gt;, but what you&#39;ll most often
be doing is adding additional functionality (via method definitions or
&lt;em&gt;Module&lt;/em&gt;s) to the Switchboard instance.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PubSubJack&lt;/code&gt; (&lt;code&gt;lib/switchboard/jacks/pubsub.rb&lt;/code&gt;) modifies the &lt;code&gt;switchboard&lt;/code&gt;
object by extending a helper &lt;em&gt;Module&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;connect&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;switchoard&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; settings&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;extend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Switchboard&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Helpers&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;PubSubHelper&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

  switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_startup &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# ...&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like everything else, jacks have access to all hooks (lifecycle callbacks).
These are called using &lt;code&gt;on_&amp;lt;hook name&amp;gt;&lt;/code&gt;. In general, these map to callbacks
defined by &lt;code&gt;xmpp4r&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;startup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shutdown&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stream_connected&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exception&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stanza&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;message&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;presence&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iq&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clients provide the following additional hooks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;roster_presence&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;roster_query&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;roster_subscription&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;roster_subscription_request&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;roster_loaded&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;roster_update&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Strictly speaking, these hooks should probably be implemented by a
&lt;code&gt;RosterJack&lt;/code&gt; and plugged in to &lt;code&gt;Switchboard::Client&lt;/code&gt; by default.&lt;/p&gt;
&lt;p&gt;Some jacks also introduce additional hooks (using &lt;code&gt;hook(:name)&lt;/code&gt;), such as
&lt;code&gt;PubSubJack&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pubsub_event&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This hook is registed in &lt;code&gt;Switchboard::Helpers::PubSubHelper&lt;/code&gt;
(&lt;code&gt;lib/switchboard/helpers/pubsub.rb&lt;/code&gt;) with
&lt;code&gt;Switchboard::Core.hook(:pubsub_event)&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;standard-jacks&#34; tabindex=&#34;-1&#34;&gt;Standard Jacks&lt;/h3&gt;
&lt;p&gt;The following jacks are available from the standard distribution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AutoAcceptJack&lt;/code&gt; - Auto-accepts and reciprocates roster requests.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DebugJack&lt;/code&gt; - Displays color-coded &lt;code&gt;&amp;lt;message /&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;presence /&amp;gt;&lt;/code&gt;, and
&lt;code&gt;&amp;lt;iq /&amp;gt;&lt;/code&gt; stanzas.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EchoJack&lt;/code&gt; - Example jack that echos inputs back to the sender.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NotifyJack&lt;/code&gt; - Sends online/offline presence to all roster items.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PubSubJack&lt;/code&gt; - Adds a &lt;code&gt;pubsub_event&lt;/code&gt; hook and some PubSub convenience
methods.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OAuthPubSubJack&lt;/code&gt; - Adds a &lt;code&gt;pubsub_event&lt;/code&gt; hook and some OAuth-enabled PubSub
convenience methods. The &lt;code&gt;oauth&lt;/code&gt; gem must be installed for this to work.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RosterDebugJack&lt;/code&gt; - Like the &lt;code&gt;DebugJack&lt;/code&gt;, but for roster events and
colorless.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mojodna/fire-hydrant/tree&#34;&gt;Fire Hydrant&lt;/a&gt; includes a
&lt;code&gt;FireEagleJack&lt;/code&gt; that introduces a &lt;code&gt;location_update&lt;/code&gt; hook that yields a
&lt;code&gt;FireEagle::User&lt;/code&gt; object, demonstrating that Switchboard apps can be
blissfully unaware of XMPP semantics when using the right jacks.&lt;/p&gt;
&lt;h3 id=&#34;a-more-complex-example%2C-with-pandas&#34; tabindex=&#34;-1&#34;&gt;A More Complex Example, with Pandas&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mojodna/fire-hydrant/tree&#34;&gt;Bamboo Shooter&lt;/a&gt; is a
pseudo-realtime interface to the &lt;a href=&#34;https://flickr.com/&#34;&gt;Flickr&lt;/a&gt; &lt;a href=&#34;https://code.flickr.com/blog/2009/03/03/panda-tuesday-the-history-of-the-panda-new-apis-explore-and-you/&#34;&gt;Panda
APIs&lt;/a&gt;.
It polls the APIs once per minute and dribbles the responses out over XMPP
over the course of the subsequent minute.&lt;/p&gt;
&lt;p&gt;This represents a more complex example for several reasons. Firstly, it
coexists with &lt;a href=&#34;http://rubyeventmachine.com/&#34;&gt;EventMachine&lt;/a&gt; as a set of
additional threads (EventMachine is evented and thus single-threaded):&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token constant&#34;&gt;EM&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;run &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;token class-name&#34;&gt;Thread&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# Bamboo::Shooter subclasses Switchboard::Component&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@shooter&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Bamboo&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Shooter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SETTINGS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@shooter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;run&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;# ...&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Secondly, it implements a &lt;a href=&#34;https://xmpp.org/extensions/xep-0060.html&#34;&gt;PubSub&lt;/a&gt;
service as a &lt;a href=&#34;https://xmpp.org/extensions/xep-0225.html&#34;&gt;component&lt;/a&gt;, so it
subclasses &lt;code&gt;Switchboard::Component&lt;/code&gt; rather than &lt;code&gt;Switchboard::Client&lt;/code&gt;. A
side-effect of implementing a service as a component is that the logic for
everything that the server would ordinarily do has to be managed by your code
instead. Presence handling (implemented by &lt;code&gt;Bamboo::Shooter#presence_handler&lt;/code&gt;)
is usually the first example of this.&lt;/p&gt;
&lt;p&gt;PubSub requests and subscription handling are application-specific concerns,
but much of the implementation is essentially boilerplate.
&lt;a href=&#34;https://github.com/mojodna/dovetail/tree&#34;&gt;Dovetail&lt;/a&gt; represents a start at
abstracting this, but it&#39;s incomplete at the moment. &lt;code&gt;&amp;lt;subscribe /&amp;gt;&lt;/code&gt; and
&lt;code&gt;&amp;lt;unsubscribe /&amp;gt;&lt;/code&gt; requests are the only operations currently supported; the fallback is to return a &lt;code&gt;&amp;lt;feature-not-implemented /&amp;gt;&lt;/code&gt; response:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;iq_handler&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;iq&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;pubsub
    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; subscribe &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;pubsub&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;first_element&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;subscribe&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      node &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; subscribe&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;node&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;

      puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Subscription to &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt; requested by &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
      subscribers&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
      &lt;span class=&#34;token keyword&#34;&gt;unless&lt;/span&gt; subscribers&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strip&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
        subscribers&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strip
      &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

      resp &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Jabber&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Iq&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:result&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      resp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to &lt;span class=&#34;token comment&#34;&gt;# TODO component.domain (elsewhere, too)&lt;/span&gt;
      resp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;id &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;id
      pubsub &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Jabber&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;PubSub&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;IqPubSub&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;
      subscription &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Jabber&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;PubSub&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Subscription&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strip&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; node&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      subscription&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;state &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;subscribed&#34;&lt;/span&gt;&lt;/span&gt;
      pubsub&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;add&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;subscription&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      resp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;add&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pubsub&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

      deliver&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;resp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;elsif&lt;/span&gt; unsubscribe &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;pubsub&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;first_element&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;unsubscribe&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      node &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; unsubscribe&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;node&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;

      puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Unsubscription from &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt; requested by &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
      subscribers&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
      subscribers&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;delete&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strip&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

      resp &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Jabber&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Iq&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:result&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      resp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;from &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to &lt;span class=&#34;token comment&#34;&gt;# TODO component.domain (elsewhere, too)&lt;/span&gt;
      resp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;id &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;id
      deliver&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;resp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;
      puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Received a pubsub message&#34;&lt;/span&gt;&lt;/span&gt;
      puts iq&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_s
      &lt;span class=&#34;token comment&#34;&gt;# TODO not-supported&lt;/span&gt;
      not_implemented&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;iq&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# unrecognized iq&lt;/span&gt;
    not_implemented&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;iq&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is tightly tied to &lt;code&gt;xmpp4r&lt;/code&gt; and REXML, but as the rest of Switchboard is
too, it&#39;s not a big deal for the time being.&lt;/p&gt;
&lt;p&gt;Presence tracking is a somewhat tricky problem (particularly for large numbers
of consumers), so you&#39;ll notice that I completed punted and left it
unimplemented. This means that subscriptions remain active (and will attempt
to deliver photo payloads) even if a client has gone offline. For this reason,
the consumers of this service are written to subscribe on startup and
unsubscribe on shutdown. If the unsubscribe doesn&#39;t happen, there&#39;s a fair
chance that the &lt;code&gt;bamboo-shooter&lt;/code&gt; process&#39;s memory usage will balloon. As a
result, there is currently no public instance of this service running.&lt;/p&gt;
&lt;p&gt;Polling of the Panda APIs is done using &lt;code&gt;EventMachine:HttpRequest&lt;/code&gt;, which is a
non-blocking, evented HTTP client. The result of each request (scheduled to
run once per minute per panda) is parsed using REXML (because Switchboard is
already using it via &lt;code&gt;xmpp4r&lt;/code&gt;), split up, and scheduled for publishing using
&lt;code&gt;EventMachine.add_timer&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token constant&#34;&gt;EM&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;run &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;token comment&#34;&gt;# ...&lt;/span&gt;

  check_pandas &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; lambda &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
    params &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;api_key&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;SETTINGS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;flickr.key&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;method&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;flickr.panda.getPhotos&#39;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;ling ling&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;hsing hsing&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;wang wang&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;panda&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
      req &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; EventMachine&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;HttpRequest&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;https://api.flickr.com/services/rest/&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      http &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; req&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;get&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:query&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; params&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;merge&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;panda_name&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; panda&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

      http&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;callback &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;begin&lt;/span&gt;
          doc &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;REXML&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Document&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;http&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;response&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
          doc&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;root&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;each_element &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;rsp&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
            total &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; rsp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;total&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_s&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_f
            panda &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; rsp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;panda&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_s
            interval &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; rsp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;interval&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_s&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_f
            interval &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; interval &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; total
            delay &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;

            puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;panda&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt; found &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;total&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt; items with a &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;s delay.&#34;&lt;/span&gt;&lt;/span&gt;

            rsp&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;each_element &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;node&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
              EventMachine&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;add_timer&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;delay&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
                &lt;span class=&#34;token variable&#34;&gt;@shooter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;publish&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/flickr/pandas/&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;&lt;span class=&#34;token constant&#34;&gt;CGI&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;escape&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;panda&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; node&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
              &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
              delay &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; interval
            &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
          &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;rescue&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;REXML&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;ParseException
        &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
      &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  EventMachine&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;add_periodic_timer&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;61&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;check_pandas&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  check_pandas&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;call
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consuming the Bamboo Shooter feed can either be done by running &lt;code&gt;switchboard pubsub --server &amp;lt;server&amp;gt; listen&lt;/code&gt; or with code like this (&lt;code&gt;earth.rb&lt;/code&gt;, which
will zoom Google Earth to the location where the photo was taken). Remember,
Wang Wang is the Panda who likes maps.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;#!/usr/bin/env ruby -rubygems&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;begin&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;appscript&#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;rescue&lt;/span&gt; LoadError &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; e
  gem &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; e&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;split&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;--&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;last&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strip
  puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;The &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;gem&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt; gem is required.&#34;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;cgi&#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;switchboard&#39;&lt;/span&gt;&lt;/span&gt;

node &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/flickr/pandas/&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;&lt;span class=&#34;token constant&#34;&gt;CGI&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;escape&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ARGV&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;

earth &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Appscript&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;app&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Google Earth&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;token constant&#34;&gt;DEFAULTS&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;resource&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;earth&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

settings &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;YAML&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;load&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;read&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;bamboo_shooter.yml&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
switchboard &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Switchboard&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Client&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;settings&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;merge&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;DEFAULTS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;plug&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;AutoAcceptJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; NotifyJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; PubSubJack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_startup &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
  defer &lt;span class=&#34;token symbol&#34;&gt;:subscribed&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
    puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Subscribing to &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
    subscribe_to&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_shutdown &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
  puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Unsubscribing from &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
  unsubscribe_from&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_pubsub_event &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;event&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;payload&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;payload&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
    payload&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;elements&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;item&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
      photo &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; item&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;first_element&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;photo&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      lat &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; photo&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;latitude&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_f
      lon &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; photo&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;longitude&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_f
      earth&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;SetViewInfo&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:latitude&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; lat&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
                         &lt;span class=&#34;token symbol&#34;&gt;:longitude&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; lon&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
                         &lt;span class=&#34;token symbol&#34;&gt;:distance&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;rand &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;25000&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
                         &lt;span class=&#34;token symbol&#34;&gt;:azimuth&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; rand &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;360&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
                         &lt;span class=&#34;token symbol&#34;&gt;:tilt&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;rand &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;75&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
                        &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:speed&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;run&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Publishing is managed by the Switchboard component:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;publish&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; xml_node&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  event &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Jabber&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;PubSub&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Event&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;
  items &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Jabber&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;PubSub&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;EventItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;
  items&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;node &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; node
  item &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Jabber&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;PubSub&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;EventItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;

  item&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;add&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;xml_node&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  items&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;add&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;item&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;add&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;items&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

  &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;subscribers&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;node&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;subscriber&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
    message&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;subscriber&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Getting Bamboo Shooter running is a little tricky, as it requires an XMPP that
supports the component protocol (I use &lt;a href=&#34;https://ejabberd.im/&#34;&gt;ejabberd&lt;/a&gt; with
the following configuration). More complicatedly, this server either needs to
be public (with DNS SRV records configured to support federation) or on a
private network with a second XMPP server running. When developing locally, I
use an Ubuntu VM with ejabberd running in component mode (using &lt;code&gt;ejabberd.cfg&lt;/code&gt;
below) and a second ejabberd instance with the default configuration running
in OS X. &lt;code&gt;bamboo-shooter.rb&lt;/code&gt; connects to Ubuntu, the client connects to OS X,
and the ejabberd instances figure out how to connect to one another with
ZeroConf (&lt;code&gt;hostname.local&lt;/code&gt;; &lt;code&gt;avahi-daemon&lt;/code&gt; on Ubuntu makes this possible).&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;loglevel&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;hosts&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;localhost&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;listen&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;5222&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; ejabberd_c2s&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;access&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; c2s&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;shaper&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; c2s_shaper&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;max_stanza_size&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;65536&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;5269&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; ejabberd_s2s_in&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;shaper&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; s2s_shaper&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;max_stanza_size&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;131072&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;5288&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; ejabberd_service&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;host&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&amp;lt;hostname&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;password&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&amp;lt;password&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
 &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;auth_method&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; internal&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;shaper&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; normal&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;maxrate&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;shaper&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; fast&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;maxrate&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;50000&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;acl&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; local&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;user_regexp&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;language&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;en&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;modules&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;
 &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;that&#39;s-it-for-now&#34; tabindex=&#34;-1&#34;&gt;That&#39;s it for now&lt;/h3&gt;
&lt;p&gt;That&#39;s all I&#39;ve got for now. It should be enough to get you started building
clients and components, but if you have any questions, post a comment below or
write to the &lt;a href=&#34;https://groups.google.com/group/switchboard&#34;&gt;Switchboard Google
Group&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>switchboard : XMPP :: curl : HTTP</title>
    <link href="https://mojodna.net/2009/07/16/switchboard-curl-for-xmpp.html"/>
    <updated>2009-07-16T00:00:00Z</updated>
    <id>https://mojodna.net/2009/07/16/switchboard-curl-for-xmpp.html</id>
    <content type="html">&lt;h2 id=&#34;quick-start&#34; tabindex=&#34;-1&#34;&gt;Quick Start&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Switchboard on GitHub:
&lt;a href=&#34;https://github.com/mojodna/switchboard/tree&#34;&gt;https://github.com/mojodna/switchboard/tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Switchboard Google Group:
&lt;a href=&#34;https://groups.google.com/group/switchboard&#34;&gt;https://groups.google.com/group/switchboard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#39;s how to install and use Switchboard for a few basic use-cases:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ &lt;span class=&#34;token comment&#34;&gt;# install Switchboard&lt;/span&gt;
$ &lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; gem &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; switchboard

$ &lt;span class=&#34;token comment&#34;&gt;# list everyone on your roster (buddy list)&lt;/span&gt;
$ switchboard &lt;span class=&#34;token parameter variable&#34;&gt;--jid&lt;/span&gt; client@example.com &lt;span class=&#34;token parameter variable&#34;&gt;--password&lt;/span&gt; pa55word &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    roster list

$ &lt;span class=&#34;token comment&#34;&gt;# add a friend to your roster&lt;/span&gt;
$ switchboard &lt;span class=&#34;token parameter variable&#34;&gt;--jid&lt;/span&gt; client@example.com &lt;span class=&#34;token parameter variable&#34;&gt;--password&lt;/span&gt; pa55word &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    roster &lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt; friend@example.com

$ &lt;span class=&#34;token comment&#34;&gt;# listen for PubSub events&lt;/span&gt;
$ switchboard &lt;span class=&#34;token parameter variable&#34;&gt;--jid&lt;/span&gt; subscriber@example.com &lt;span class=&#34;token parameter variable&#34;&gt;--password&lt;/span&gt; pa55word &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;pubsub server&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; listen&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;%22curl-for-xmpp%3F%22&#34; tabindex=&#34;-1&#34;&gt;&amp;quot;curl for XMPP?&amp;quot;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;curl&lt;/code&gt; is the ultimate Swiss Army Knife for HTTP. Switchboard aims to be the
same for XMPP.&lt;/p&gt;
&lt;p&gt;HTTP is (relatively) easy. It&#39;s stateless and has fairly limited semantics.
However, if you&#39;re trying to debug a web service and want to determine what
certain &lt;code&gt;ETag&lt;/code&gt;s, additional headers, or specific content types respond with,
&lt;code&gt;curl&lt;/code&gt; is your go-to.&lt;/p&gt;
&lt;p&gt;XMPP is a bit more complicated; it&#39;s stateful, it (usually) requires login
credentials, it&#39;s asynchronous, and it has many extensions to the core
protocol that are in varying levels of use. Traditionally, in order to explore
an XMPP service, you&#39;d have to delve into the advanced features of a client
like &lt;a href=&#34;https://psi-im.org/&#34;&gt;Psi&lt;/a&gt; or &lt;a href=&#34;http://synapse.im/&#34;&gt;Synapse&lt;/a&gt;, often
dropping to the level of entering raw XML to see what happens in response.&lt;/p&gt;
&lt;p&gt;Switchboard (and its command-line equivalent, &lt;code&gt;switchboard&lt;/code&gt;) simplifies the
process of repeatedly making common requests (e.g. roster manipulation,
probing, and PubSub node operations) and is easily extended to support new
behaviors.&lt;/p&gt;
&lt;h3 id=&#34;switchboard-background&#34; tabindex=&#34;-1&#34;&gt;Switchboard background&lt;/h3&gt;
&lt;p&gt;Copying and pasting XML isn&#39;t the least error-prone process I can think of. In
addition, I found myself repeatedly testing the same types of stanzas (e.g.
&lt;a href=&#34;https://xmpp.org/extensions/xep-0060.html&#34;&gt;PubSub&lt;/a&gt; requests) and attempting to
implement extensions for which no implementations existed (i.e. &lt;a href=&#34;https://xmpp.org/extensions/xep-0235.html&#34;&gt;OAuth over
XMPP&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Diving into &lt;a href=&#34;http://home.gna.org/xmpp4r/&#34;&gt;&lt;code&gt;xmpp4r&lt;/code&gt;&lt;/a&gt;&#39;s functionality led me to
expose additional features to the command-line tool such as
&lt;a href=&#34;https://xmpp.org/extensions/xep-0163.html&#34;&gt;PEP&lt;/a&gt; and &lt;a href=&#34;https://xmpp.org/extensions/xep-0012.html&#34;&gt;Last
Activity&lt;/a&gt; to see how difficult it
would be.&lt;/p&gt;
&lt;p&gt;Since all of its dependencies have been released and are installable via
RubyGems now, people who aren&#39;t me can finally use Switchboard without much
hassle. In honor of this milestone, Switchboard has been updated to 0.1.0.
Real mature, I know, but it is still quite useful.&lt;/p&gt;
&lt;p&gt;People who &lt;em&gt;are&lt;/em&gt; me have been using this tool since last November (in
fact, this is the project that inspired &lt;a href=&#34;/2009/03/09/my-public-git-workflow.html&#34;&gt;&amp;quot;My (public) Git
Workflow&amp;quot;&lt;/a&gt;). (Some people have been
using it to consume &lt;a href=&#34;http://fireeagle.yahoo.net/&#34;&gt;Fire Eagle&lt;/a&gt;&#39;s &lt;a href=&#34;http://feblog.yahoo.net/2009/02/19/fire-eagle-location-streams/&#34;&gt;Location
Streams&lt;/a&gt;, but
it hasn&#39;t been for the faint of heart.)&lt;/p&gt;
&lt;p&gt;Switchboard&#39;s primary strength is certainly the command-line interface, but it
turns out that it&#39;s a pretty good abstraction over &lt;code&gt;xmpp4r&lt;/code&gt; for building
clients and servers (as components).&lt;/p&gt;
&lt;p&gt;Here are a few projects I&#39;ve worked on that use Switchboard:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mojodna/bamboo-shooter/tree&#34;&gt;bamboo-shooter&lt;/a&gt; - PubSub for
Pandas: an pseudo-realtime XMPP interface to the Flickr Panda APIs&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mojodna/fire-hydrant/tree&#34;&gt;fire-hydrant&lt;/a&gt; - A simple
library (as a Switchboard &amp;quot;jack&amp;quot;) for consuming
&lt;a href=&#34;http://fireeagle.yahoo.net/&#34;&gt;Fire Eagle&lt;/a&gt;&#39;s &lt;a href=&#34;http://feblog.yahoo.net/2009/02/19/fire-eagle-location-streams/&#34;&gt;Location Streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mojodna/mars/tree&#34;&gt;mars&lt;/a&gt; and
&lt;a href=&#34;https://github.com/mojodna/jupiter/tree&#34;&gt;jupiter&lt;/a&gt; - A matched pair of
experiments mapping REST to XMPP&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mojodna/dovetail/tree&#34;&gt;dovetail&lt;/a&gt; - A start at a framework
for building PubSub servers (again, as components)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-to-do%3F&#34; tabindex=&#34;-1&#34;&gt;What to do?&lt;/h3&gt;
&lt;h4 id=&#34;configuration&#34; tabindex=&#34;-1&#34;&gt;Configuration&lt;/h4&gt;
&lt;p&gt;The first thing you&#39;ll probably want to do with Switchboard is to provide some
basic configuration so you won&#39;t have to constantly enter your login
credentials:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard config jid jid@example.com
switchboard config password pa55word&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get the value of a setting, don&#39;t include a value:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard config jid &lt;span class=&#34;token comment&#34;&gt;# =&gt; jid@example.com&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some additional useful settings to set defaults for are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;debug&lt;/code&gt; - Turn on verbose mode: good for debugging. (&lt;code&gt;true&lt;/code&gt; / &lt;code&gt;false&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resource&lt;/code&gt; - Override the default resource of &lt;code&gt;/switchboard&lt;/code&gt; (don&#39;t include
the &lt;code&gt;/&lt;/code&gt;). This is useful if you want to run multiple instances with the same
&lt;em&gt;JID&lt;/em&gt;. This is equivalent to &lt;code&gt;switchboard --resource &amp;lt;resource&amp;gt;&lt;/code&gt;. (&lt;em&gt;String&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pubsub.server&lt;/code&gt; - Specify a default server to make PubSub requests against.
This is equivalent to &lt;code&gt;switchboard pubsub --server &amp;lt;server&amp;gt;&lt;/code&gt;. (&lt;em&gt;String&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oauth&lt;/code&gt; - Use &lt;a href=&#34;https://oauth.net/&#34;&gt;OAuth&lt;/a&gt; when making PubSub requests. This
is equivalent to &lt;code&gt;switchboard pubsub --oauth&lt;/code&gt; and requires that the
&lt;a href=&#34;http://oauth.rubyforge.org/&#34;&gt;&lt;code&gt;oauth&lt;/code&gt; gem&lt;/a&gt; be installed. (&lt;code&gt;true&lt;/code&gt; /
&lt;code&gt;false&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oauth.consumer_key&lt;/code&gt; - Specify a default OAuth consumer key. This is
equivalent to &lt;code&gt;switchboard pubsub --oauth-consumer-key &amp;lt;key&amp;gt;&lt;/code&gt;. (&lt;em&gt;String&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oauth.consumer_secret&lt;/code&gt; - Specify a default OAuth consumer secret. This is
equivalent to &lt;code&gt;switchboard pubsub --oauth-consumer-secret &amp;lt;secret&amp;gt;&lt;/code&gt;.
(&lt;em&gt;String&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oauth.token&lt;/code&gt; - Specify a default OAuth token. This is equivalent to
&lt;code&gt;switchboard pubsub --oauth-token &amp;lt;token&amp;gt;&lt;/code&gt;. (&lt;em&gt;String&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oauth.token_secret&lt;/code&gt; - Specify a default OAuth token secret. This is
equivalent to &lt;code&gt;switchboard pubsub --oauth-token-secret &amp;lt;secret&amp;gt;&lt;/code&gt;. (&lt;em&gt;String&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In general, anything that is referred to in the library in the form
&lt;code&gt;OPTIONS[&amp;quot;pubsub.node&amp;quot;]&lt;/code&gt; can be set as a Switchboard setting.&lt;/p&gt;
&lt;h4 id=&#34;roster-manipulation&#34; tabindex=&#34;-1&#34;&gt;Roster Manipulation&lt;/h4&gt;
&lt;p&gt;Roster manipulation is something that can be easily handled by a desktop XMPP
client, but sometimes it&#39;s more convenient to be able to do it from the
command-line.&lt;/p&gt;
&lt;p&gt;Rosters can be listed, added to, or removed from. I&#39;ll assume you&#39;ve
configured Switchboard with some login credentials.&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard roster list
switchboard roster online
switchboard roster &lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt; friend1@example.org friend2@example.org
switchboard roster remove friend2@example.org enemy@example.org&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;probing-and-discovery&#34; tabindex=&#34;-1&#34;&gt;Probing and Discovery&lt;/h4&gt;
&lt;p&gt;XMPP provides some pretty heady functionality when it comes to determining
what a server is capable of. A &lt;code&gt;disco#info&lt;/code&gt; query is the first step to use
when determining capabilities:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard disco &lt;span class=&#34;token parameter variable&#34;&gt;--target&lt;/span&gt; jabber.org info&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The response to this query includes &lt;code&gt;http://jabber.org/protocol/disco#items&lt;/code&gt;,
which means that &lt;code&gt;jabber.org&lt;/code&gt; supports &lt;code&gt;disco#items&lt;/code&gt; queries, which allow you
to determine what top-level items (services) are available:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard disco &lt;span class=&#34;token parameter variable&#34;&gt;--target&lt;/span&gt; jabber.org items&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This response includes &lt;code&gt;conference.jabber.org&lt;/code&gt;. Let&#39;s list items available
there:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard disco &lt;span class=&#34;token parameter variable&#34;&gt;--target&lt;/span&gt; conference.jabber.org items&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whoa. A list of &lt;em&gt;MUC&lt;/em&gt;s (multi-user chats) hosted on &lt;code&gt;conference.jabber.org&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can do the same thing to list available PubSub nodes if you&#39;re running a
local copy of &lt;a href=&#34;https://ejabberd.im/&#34;&gt;ejabberd&lt;/a&gt; or another XMPP server that
supports it. (Note that you may need to prefix your hostname with &lt;code&gt;pubsub&lt;/code&gt; in
order to see the nodes.)&lt;/p&gt;
&lt;h4 id=&#34;pubsub&#34; tabindex=&#34;-1&#34;&gt;PubSub&lt;/h4&gt;
&lt;p&gt;Switchboard supports more of
&lt;a href=&#34;https://xmpp.org/extensions/xep-0060.html&#34;&gt;PubSub&lt;/a&gt; than any other extension,
mainly because that&#39;s been my primary focus of XMPP experimentation. To get a
full list of available PubSub commands:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard pubsub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A basic sequence of events is to subscribe:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;server&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; subscribe &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;node&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;List subscriptions:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;server&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; subscriptions&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Listen for notifications:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;server&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; listen&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unsubscribe:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;server&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; unsubscribe &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;node&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s walk through a couple examples.&lt;/p&gt;
&lt;p&gt;First, &lt;a href=&#34;https://superfeedr.com/&#34;&gt;Superfeedr&lt;/a&gt;, which bills itself as &amp;quot;real-time
feed parsing in the cloud&amp;quot;. To begin, you&#39;ll need to register and activate
your account. Once you&#39;ve done that, set up a subscription a
&lt;a href=&#34;https://twitter.com/&#34;&gt;Twitter&lt;/a&gt; search for &amp;quot;xmpp&amp;quot;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard &lt;span class=&#34;token parameter variable&#34;&gt;--jid&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;@superfeedr.com &lt;span class=&#34;token parameter variable&#34;&gt;--password&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;password&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; firehoser.superfeedr.com &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    subscribe &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;http://search.twitter.com/search.atom?q=xmpp&#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We would next list subscriptions, but Superfeedr uses a non-standard mechanism
to do so. Instead, let&#39;s listen for new results:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard &lt;span class=&#34;token parameter variable&#34;&gt;--jid&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;username&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;@superfeedr.com &lt;span class=&#34;token parameter variable&#34;&gt;--password&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;password&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; firehoser.superfeedr.com listen&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re lucky, you&#39;ll get an Atom payload or two. Here&#39;s one:&lt;/p&gt;
&lt;pre class=&#34;language-xml&#34;&gt;&lt;code class=&#34;language-xml&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;event&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;xmlns&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;http://jabber.org/protocol/pubsub#event&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;status&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;feed&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;http://search.twitter.com/search.atom?q=xmpp&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;xmlns&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;https://superfeedr.com/xmpp-pubsub-ext&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;http&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;200&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;16933 bytes fetched in 0.600034s&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;http&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;next_fetch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;2009-07-22T17:26:22Z&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;next_fetch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;status&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;items&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;http://search.twitter.com/search.atom?q=xmpp&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;item&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;1&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;chunks&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;1&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;entry&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;xmlns&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;http://www.w3.org/2005/Atom&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Great... trillian update has killed my ability to view my xmpp rosters&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;summary&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Great... trillian update has killed my ability to view my &lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;lt;&#34;&gt;&amp;amp;lt;&lt;/span&gt;b&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;gt;&#34;&gt;&amp;amp;gt;&lt;/span&gt;xmpp&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;lt;&#34;&gt;&amp;amp;lt;&lt;/span&gt;/b&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;gt;&#34;&gt;&amp;amp;gt;&lt;/span&gt; rosters&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;summary&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;https://superfeedr.com/entries/tr5gfgstf8oqlcgr5opaeotxk39ovtos0oiat7h12mqfuoxdmgbjz1rnjtzswqvja2dqh8cgg31&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;alternate&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;text/html&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;published&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;2009-07-22T17:10:52Z&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;published&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;id&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;tag:search.twitter.com,2005:2781212809&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;id&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;entry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;item&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;item&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;1&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;chunks&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;1&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;entry&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;xmlns&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;http://www.w3.org/2005/Atom&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;usando Tkabber: TKabber is Tcl/Tk Jabber-client with great functionality. It supports MUC, XMPP-statuses a.. http://bit.ly/AsFPg&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;summary&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;usando Tkabber: TKabber is Tcl/Tk Jabber-client with great functionality. It supports MUC, &lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;lt;&#34;&gt;&amp;amp;lt;&lt;/span&gt;b&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;gt;&#34;&gt;&amp;amp;gt;&lt;/span&gt;XMPP&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;lt;&#34;&gt;&amp;amp;lt;&lt;/span&gt;/b&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;gt;&#34;&gt;&amp;amp;gt;&lt;/span&gt;-statuses a.. &lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;lt;&#34;&gt;&amp;amp;lt;&lt;/span&gt;a href=&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;quot;&#34;&gt;&amp;amp;quot;&lt;/span&gt;http://bit.ly/AsFPg&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;quot;&#34;&gt;&amp;amp;quot;&lt;/span&gt;&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;gt;&#34;&gt;&amp;amp;gt;&lt;/span&gt;http://bit.ly/AsFPg&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;lt;&#34;&gt;&amp;amp;lt;&lt;/span&gt;/a&lt;span class=&#34;token entity named-entity&#34; title=&#34;&amp;gt;&#34;&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;summary&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;https://superfeedr.com/entries/w6cezbjniqqgga3bd79ruklxhu7f4a6qqpro76hgz50gzccuekgmehz39yb1zi1cclgo83s&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;rel&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;alternate&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;text/html&lt;span class=&#34;token punctuation&#34;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;published&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;2009-07-22T17:07:54Z&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;published&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;id&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;tag:search.twitter.com,2005:2781162048&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;id&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;entry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;item&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;items&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;event&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It includes a Superfeedr-specific &lt;code&gt;&amp;lt;status/&amp;gt;&lt;/code&gt; element with information on the
most recent fetch as well as standard Atom feeds contained within standard
&lt;code&gt;&amp;lt;item/&amp;gt;&lt;/code&gt; elements. This means that you can extract the Atom elements from the
PubSub payload and hand it to a feedparser of some variety to work its magic.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://fireeagle.yahoo.net/&#34;&gt;Fire Eagle&lt;/a&gt;&#39;s &lt;a href=&#34;http://feblog.yahoo.net/2009/02/19/fire-eagle-location-streams/&#34;&gt;Location
Streams&lt;/a&gt; also
use PubSub, but subscription management requires that requests are signed
using OAuth. This allows 3rd party applications to make requests on behalf of
specific users without having to obtain their actual credentials (in short,
it&#39;s exactly the same use-case for OAuth over HTTP).&lt;/p&gt;
&lt;p&gt;Let&#39;s start with a subscriptions list request, since we have the credentials
(&amp;quot;General Purpose Access Token&amp;quot;) immediately after registering a &amp;quot;web&amp;quot;
application with &lt;a href=&#34;http://fireeagle.yahoo.net/&#34;&gt;Fire Eagle&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--oauth&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-token &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;general token&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-token-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;general token secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; fireeagle.com &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    subscriptions&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Odds are, you&#39;ll have nothing there. Let&#39;s change that. Send yourself through
the authorization process in order to get a valid OAuth token and secret:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ oauth --consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --access-token-url https://fireeagle.yahooapis.com/oauth/access_token
    --authorize-url https://fireeagle.yahoo.net/oauth/authorize
    --request-token-url https://fireeagle.yahooapis.com/oauth/request_token
    authorize&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that token and secret, subscribe to your Location Stream:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--oauth&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-token &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;token&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-token-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;token secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; fireeagle.com &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    subscribe &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;/api/0.1/user/&amp;lt;token&gt;&#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you&#39;ll have a subscription to query for:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--oauth&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-token &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;general token&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-token-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;general token secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; fireeagle.com &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    subscriptions&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Listen for location updates:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; fireeagle.com listen&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&#34;http://fireeagle.yahoo.net/my/location&#34;&gt;Update your current location&lt;/a&gt; and
watch as the update rolls in. If you&#39;d like to visualize updates with &lt;a href=&#34;http://earth.google.com/&#34;&gt;Google
Earth&lt;/a&gt;, check out &lt;a href=&#34;https://github.com/mojodna/fire-hydrant/tree&#34;&gt;fire-hydrant on
GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We&#39;re done, so we may as well clean up and unsubscribe:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ switchboard pubsub &lt;span class=&#34;token parameter variable&#34;&gt;--oauth&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-consumer-key &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer key&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-consumer-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;consumer secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-token &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;token&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    --oauth-token-secret &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;token secret&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    &lt;span class=&#34;token parameter variable&#34;&gt;--server&lt;/span&gt; fireeagle.com &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
    unsubscribe &lt;span class=&#34;token parameter variable&#34;&gt;--node&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;/api/0.1/user/&amp;lt;token&gt;&#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;pep-(personal-eventing-protocol)&#34; tabindex=&#34;-1&#34;&gt;PEP (Personal Eventing Protocol)&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://xmpp.org/extensions/xep-0163.html&#34;&gt;PEP&lt;/a&gt; is a specialized version of
PubSub, intended to allow individuals to associate data with their JIDs.
Switchboard supports publishing of User Tune and User Location.&lt;/p&gt;
&lt;p&gt;Due to the ability of XMPP to allow multiple instances of the same account
online (identified with different resources), Switchboard can serve up User
Tune and User Location for the same account you&#39;re already online with. Not
every client supports displaying them, but they&#39;re fun to play with
regardless.&lt;/p&gt;
&lt;p&gt;To publish User Tune, you need to be on a Mac, running iTunes, and have the
&lt;code&gt;rb-appscript&lt;/code&gt; gem installed (&lt;code&gt;sudo gem install rb-appscript&lt;/code&gt;). Once that&#39;s
done:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard &lt;span class=&#34;token parameter variable&#34;&gt;--resource&lt;/span&gt; switchtunes pep tune&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To publish User Location, you need to be updating &lt;a href=&#34;http://fireeagle.yahoo.net/&#34;&gt;Fire
Eagle&lt;/a&gt;
(&lt;a href=&#34;https://tomtaylor.co.uk/projects/clarke/&#34;&gt;Clarke&lt;/a&gt; is an excellent background
updater for OS X) and have the &lt;code&gt;fire-hydrant&lt;/code&gt; gem installed from GitHub (&lt;code&gt;sudo gem install mojodna-fire-hydrant -s http://gems.github.com&lt;/code&gt;). Once that&#39;s
square:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard &lt;span class=&#34;token parameter variable&#34;&gt;--resource&lt;/span&gt; switchfire pep location&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;more&#34; tabindex=&#34;-1&#34;&gt;More&lt;/h4&gt;
&lt;p&gt;Switchboard supports more functionality than I&#39;ve described above. To get a list of general &lt;code&gt;switchboard&lt;/code&gt; commands (some of which may have sub-commands):&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;getting-help&#34; tabindex=&#34;-1&#34;&gt;Getting Help&lt;/h3&gt;
&lt;p&gt;In theory, if you want more information about a specific command, you can use
&lt;code&gt;switchboard help &amp;lt;command&amp;gt;&lt;/code&gt;. For example:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;switchboard &lt;span class=&#34;token builtin class-name&#34;&gt;help&lt;/span&gt; pubsub&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For now, you&#39;ll notice that it&#39;s not particularly useful. If you&#39;d like to
help rectify this, you can implement various &lt;code&gt;help&lt;/code&gt; methods that are lying
around, such as &lt;code&gt;Switchboard::Commands::PubSub.help&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;extending&#34; tabindex=&#34;-1&#34;&gt;Extending&lt;/h3&gt;
&lt;p&gt;Writing new Switchboard commands is really easy, assuming that the primary
application logic that you&#39;re depending exists elsewhere (i.e. in &lt;code&gt;xmpp4r&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;I was on a panel with &lt;a href=&#34;https://stpeter.im/&#34;&gt;Peter St. Andre&lt;/a&gt; and &lt;a href=&#34;https://metajack.im/&#34;&gt;Jack Moffitt&lt;/a&gt; at the Glue Conference in Denver this Spring and we got to talking about tools like Switchboard. Jack wondered how hard it would be to implement something like &lt;code&gt;grep&lt;/code&gt; for XMPP.&lt;/p&gt;
&lt;p&gt;(Jack is one of the authors of a Python project similar to Switchboard named &lt;a href=&#34;https://launchpad.net/poetry&#34;&gt;&lt;code&gt;poetry&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I took a whack at it and it came out like this:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Switchboard&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Commands&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Grep&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; Switchboard&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Command
      description &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Search for an XPath expression&#34;&lt;/span&gt;&lt;/span&gt;

      &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;run&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;
        expr &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;ARGV&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;pop

        switchboard &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Switchboard&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Client&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;
        switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;plug&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;AutoAcceptJack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; NotifyJack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

        switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;on_stanza &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;stanza&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
          &lt;span class=&#34;token comment&#34;&gt;# TODO doesn&#39;t handle default namespaces properly&lt;/span&gt;
          &lt;span class=&#34;token constant&#34;&gt;REXML&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;XPath&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stanza&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; expr&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;el&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
            puts el&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_s
          &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

        switchboard&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;run&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;
      &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Jack&lt;/em&gt;s (not Moffitt) deserve their own discussion, but the thrust of this
piece of code is the &lt;code&gt;#on_stanza&lt;/code&gt; callback (which yields a &lt;code&gt;REXML::Node&lt;/code&gt;
object) and the XPath expression. Note that for whatever reason, you can&#39;t
search for nodes that have a default namespace (e.g. &lt;code&gt;&amp;lt;presence /&amp;gt;&lt;/code&gt;); I&#39;m
going to assume that this is a REXML quirk.&lt;/p&gt;
&lt;p&gt;If you want to take a shot at implementing a Switchboard command,
&lt;a href=&#34;https://xmpp.org/extensions/xep-0199.html&#34;&gt;Ping&lt;/a&gt; should be pretty simple.
Alternately, Switchboard doesn&#39;t support sending or receiving basic &lt;code&gt;&amp;lt;message /&amp;gt;&lt;/code&gt; stanzas from the command-line. Supporting those would make it simple to
interact with a service like &lt;a href=&#34;http://identi.ca/&#34;&gt;identi.ca&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;how-to-help&#34; tabindex=&#34;-1&#34;&gt;How to Help&lt;/h3&gt;
&lt;p&gt;There are a few simple ways to help out with Switchboard&#39;s development:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;explore the API&lt;/li&gt;
&lt;li&gt;build out the docs&lt;/li&gt;
&lt;li&gt;implement IM functionality&lt;/li&gt;
&lt;li&gt;write up how to use it for a particular service / use-case&lt;/li&gt;
&lt;li&gt;use it!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What&#39;s your favorite use for Switchboard?&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Updating Ruby Consumers and Providers to OAuth 1.0a</title>
    <link href="https://mojodna.net/2009/05/20/updating-ruby-consumers-and-providers-to-oauth-10a.html"/>
    <updated>2009-05-20T00:00:00Z</updated>
    <id>https://mojodna.net/2009/05/20/updating-ruby-consumers-and-providers-to-oauth-10a.html</id>
    <content type="html">&lt;p&gt;In a previous post, I did a &lt;a href=&#34;/2009/05/20/updating-ruby-consumers-and-providers-to-oauth-10a.html&#34;&gt;quick run-through of the changes that were
introduced in OAuth
1.0a&lt;/a&gt;.
As promised, here&#39;s a rough guide to updating Ruby Consumers and Providers to
1.0a. Don&#39;t mind the pseudo code.&lt;/p&gt;
&lt;h2 id=&#34;updating-ruby-oauth-consumers-to-1.0a&#34; tabindex=&#34;-1&#34;&gt;Updating Ruby OAuth Consumers to 1.0a&lt;/h2&gt;
&lt;p&gt;In order for things to work properly, you&#39;ll need to use a version of the
OAuth gem that&#39;s at least &lt;em&gt;0.3.4.1&lt;/em&gt; (0.3.5 was released on 6/3/09). To install
it and check the version number:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; gem &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; oauth
oauth &lt;span class=&#34;token parameter variable&#34;&gt;--version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Authorization code that once looked like this:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;request_token &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; consumer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;get_request_token
puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Please visit the following URL to authorize this application:&#34;&lt;/span&gt;&lt;/span&gt;
puts request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;authorize_url&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:oauth_callback&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; callback_url&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# wait for input&lt;/span&gt;
gets
access_token &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;get_access_token&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Should now look like this:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;request_token &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; consumer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;get_request_token&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:oauth_callback&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; callback_url&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Please visit the following URL to authorize this application:&#34;&lt;/span&gt;&lt;/span&gt;
puts request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;authorize_url
&lt;span class=&#34;token comment&#34;&gt;# wait for input&lt;/span&gt;
puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;What&#39;s the value of `oauth_verifier`?&#34;&lt;/span&gt;&lt;/span&gt;
oauth_verifier &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; gets&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;chomp
&lt;span class=&#34;token comment&#34;&gt;# `oauth_verifier` is extracted from the expanded callback URL or was displayed to the user&lt;/span&gt;
access_token &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;get_access_token&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:oauth_verifier&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; oauth_verifier&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can detect whether a Service Provider supports 1.0a:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;request_token &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; consumer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;get_request_token
puts &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;OAuth 1.0a detected&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;callback_confirmed&lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;updating-ruby-oauth-providers-to-1.0a&#34; tabindex=&#34;-1&#34;&gt;Updating Ruby OAuth Providers to 1.0a&lt;/h3&gt;
&lt;p&gt;As with the updates to consumer applications, you&#39;ll need to be running at
least &lt;em&gt;0.3.4.1&lt;/em&gt;. For the sake of simplicity (and additional laziness on my
part), I&#39;ll assume OAuth verification is baked into your Rails app rather than
as Rack middleware (where it probably belongs).&lt;/p&gt;
&lt;p&gt;You&#39;ll need to add a pair of columns to your &lt;code&gt;oauth_request_tokens&lt;/code&gt; table (or
whatever it&#39;s called): &lt;code&gt;callback&lt;/code&gt; and &lt;code&gt;verifier&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;accepting-oauth_callback-during-the-request-token-phase&#34; tabindex=&#34;-1&#34;&gt;Accepting &lt;code&gt;oauth_callback&lt;/code&gt; During the Request Token Phase&lt;/h4&gt;
&lt;p&gt;The first step to supporting OAuth 1.0a is to accept &lt;code&gt;oauth_token&lt;/code&gt; parameters when issuing Request Tokens. To do this, you&#39;ll need to make the &lt;code&gt;OAuth::RequestProxy::ActionControllerRequest&lt;/code&gt; available to methods that run later in a request&#39;s lifecycle:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;verify_oauth_signature&lt;/span&gt;&lt;/span&gt;
  valid &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; OAuth&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Signature&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;verify&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;request&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;request_proxy&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# make the request proxy available outside this block&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@_request_proxy&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; request_proxy

    &lt;span class=&#34;token comment&#34;&gt;# proceed normally...&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;# accessor for the request proxy&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;oauth_request_proxy&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token variable&#34;&gt;@_request_proxy&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this is set up, you&#39;ll need to modify your &lt;code&gt;request_token&lt;/code&gt; method to associate the &lt;code&gt;oauth_callback&lt;/code&gt; parameter with your Request token and set &lt;code&gt;oauth_callback_confirmed&lt;/code&gt; to &lt;em&gt;true&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;request_token&lt;/span&gt;&lt;/span&gt;
  request_token &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; new_request_token
   &lt;span class=&#34;token comment&#34;&gt;# request_proxy provides unified interface to params + headers&lt;/span&gt;
  request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;callback &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; request_proxy&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;oauth_callback
  request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;save

  render &lt;span class=&#34;token symbol&#34;&gt;:text&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;oauth_token=&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;token&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&amp;amp;&#34;&lt;/span&gt;&lt;/span&gt; \
                  &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;oauth_token_secret=&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;secret&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&amp;amp;&#34;&lt;/span&gt;&lt;/span&gt; \
                  &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;oauth_callback_confirmed=true&#34;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;generating-an-oauth_verifier-during-the-authorization-phase&#34; tabindex=&#34;-1&#34;&gt;Generating An &lt;code&gt;oauth_verifier&lt;/code&gt; During the Authorization Phase&lt;/h4&gt;
&lt;p&gt;In addition to validating the Request Token during the authorization phase,
you&#39;ll want to generate an &lt;code&gt;oauth_verifier&lt;/code&gt; value and return it to the
application via the pre-registered callback url (or display it to the user and
instruct them to enter it into their application).&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;authorize&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token comment&#34;&gt;# display the authorization page&lt;/span&gt;

  render &lt;span class=&#34;token keyword&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;unless&lt;/span&gt; request&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;post&lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;# validate the token&lt;/span&gt;

  request_token &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; find_request_token&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;params&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:oauth_token&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;validated &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;
  &lt;span class=&#34;token comment&#34;&gt;# generate a verification code&lt;/span&gt;
  request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;verifier &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; generate_verifier
  request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;save

  &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;callback&lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# this was previously params[:oauth_callback]&lt;/span&gt;
    redirect_to request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;callback &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;?oauth_verifier=&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;verifier&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@verifier&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; request_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;verifier
    render &lt;span class=&#34;token comment&#34;&gt;# display the verification code to the user&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;verifying-access-token-exchanges&#34; tabindex=&#34;-1&#34;&gt;Verifying Access Token Exchanges&lt;/h4&gt;
&lt;p&gt;When exchanging a Request Token for an Access Token, you need to confirm that
the verification code provided by the consumer matches the one on file.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;access_token&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token comment&#34;&gt;# this is a correctly signed request: oauth_token has already been loaded&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;# compare the verifier from the request proxy to the one we generated&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; oauth_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;verifier &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; request_proxy&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;oauth_verifier
    &lt;span class=&#34;token comment&#34;&gt;# exchange the request token for an access token&lt;/span&gt;
    access_token &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; oauth_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;exchange
    render &lt;span class=&#34;token symbol&#34;&gt;:text&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;oauth_token=&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;access_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;token&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&amp;amp;&#34;&lt;/span&gt;&lt;/span&gt; \
                    &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;oauth_token_secret=&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;access_token&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;secret&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;raise&lt;/span&gt; OAuth&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;InvalidVerifier
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;summary&#34; tabindex=&#34;-1&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Obviously, there are cleaner ways to do this, but they&#39;re presumably very
specific to individual codebases. If you have questions, check out the
&lt;a href=&#34;https://groups.google.com/group/oauth-ruby&#34;&gt;oauth-ruby&lt;/a&gt; mailing list.
Otherwise, patches can be submitted against
&lt;a href=&#34;https://github.com/mojodna/oauth/tree/mergeable&#34;&gt;https://github.com/mojodna/oauth/tree/mergeable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Good luck!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>An Idiot's Guide to OAuth 1.0a</title>
    <link href="https://mojodna.net/2009/05/20/an-idiots-guide-to-oauth-10a.html"/>
    <updated>2009-05-20T00:00:00Z</updated>
    <id>https://mojodna.net/2009/05/20/an-idiots-guide-to-oauth-10a.html</id>
    <content type="html">&lt;p&gt;I&#39;m lazy. I don&#39;t usually enjoy re-reading things I&#39;ve already read before
(Matt Ruff and Neil Stephenson books are an exception). So, as a service to
all 3 of my readers, I&#39;ll summarize the changes to the
&lt;a href=&#34;https://oauth.net/&#34;&gt;OAuth&lt;/a&gt; specification for &lt;a href=&#34;http://oauth.googlecode.com/svn/spec/core/1.0a/drafts/3/oauth-core-1_0a.html&#34;&gt;1.0a (draft
3)&lt;/a&gt;
as I understand them. Finding changes in a large document is a pain--you have
better things to do with your time. Embrace your inner ignorance and leave
responsibility to the wind.&lt;/p&gt;
&lt;p&gt;As an added bonus, I&#39;ll demonstrate how to &lt;a href=&#34;/2009/05/20/updating-ruby-consumers-and-providers-to-oauth-10a.html&#34;&gt;update consumer and provider code
using the Ruby
library&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;what-happened%3F&#34; tabindex=&#34;-1&#34;&gt;What Happened?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://www.readwriteweb.com/archives/how_the_oauth_security_battle_was_won_open_web_sty.php&#34;&gt;Much&lt;/a&gt;
has been written about this
&lt;a href=&#34;https://www.hueniverse.com/hueniverse/2009/04/explaining-the-oauth-session-fixation-attack.html&#34;&gt;elsewhere&lt;/a&gt;,
so I&#39;ll be brief. A &lt;a href=&#34;https://oauth.net/advisories/2009-1&#34;&gt;&lt;em&gt;session fixation&lt;/em&gt; attack was
discovered&lt;/a&gt; a little over a month ago.
Essentially, it affixes &lt;strong&gt;your&lt;/strong&gt; sessions to someone else&#39;s. Imagine burrs or
inverted balls of duct tape with little teeny messages (on grains of rice)
attached to them.&lt;/p&gt;
&lt;p&gt;Anyway. It was bad. It was one of those things that once you&#39;ve seen, it&#39;s
impossible to un-see. There was no straightforward fix that could be deployed,
so you started seeing lots of BIG ANGRY warning notices explaining that your
car might be repossessed if you were bold enough to continue.&lt;/p&gt;
&lt;p&gt;The longer-term solution was to &lt;a href=&#34;http://oauth.googlecode.com/svn/spec/core/1.0a/drafts/3/oauth-core-1_0a.html&#34;&gt;change the
specification&lt;/a&gt;,
get providers on-board to change their code, and finally to reach
out to developers to update their client applications. All so the world
doesn&#39;t implode next time you turn right. DON&#39;T TURN RIGHT. Ok?&lt;/p&gt;
&lt;h3 id=&#34;so%2C-what-changed%3F&#34; tabindex=&#34;-1&#34;&gt;So, What Changed?&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Callbacks (to your site or application) have been supported in various
forms (with varying restrictions), but they have always been parameters
passed to the Service Provider&#39;s authorization page. Instead, the
&lt;code&gt;oauth_callback&lt;/code&gt; parameter is now part of the Request Token phase. If your
client cannot accept callbacks, the value &lt;strong&gt;MUST&lt;/strong&gt; be &lt;code&gt;oob&lt;/code&gt;. SPs use this
to determine whether a client supports 1.0a.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;oauth_callback_confirmed&lt;/code&gt; will be present (indeed, &lt;strong&gt;MUST&lt;/strong&gt; according to
the spec) when Service Providers supporting 1.0a issue a Request Token.
(e.g. &lt;code&gt;oauth_token=asdf&amp;amp;oauth_token_secret=qwerty&amp;amp;oauth_callback_confirmed=true&lt;/code&gt;)
Clients can use this to determine whether a server supports 1.0a.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An &lt;code&gt;oauth_verifier&lt;/code&gt; parameter is provided to the client either in the
pre-configured callback URL or through the fingers of your users (the
aforementioned &lt;code&gt;oob&lt;/code&gt; (&amp;quot;out of band&amp;quot;) mechanism). This value &lt;strong&gt;MUST&lt;/strong&gt; be
included when exchanging Request Tokens for Access Tokens.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The changes to the spec are limited to the Request Token ➡ Access Token
exchange, so once you have an Access Token, everything should behave as it did
before. For this reason, clients that only implement 2-legged OAuth are
unaffected.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://oauth.googlecode.com/svn/spec/core/1.0a/drafts/3/oauth-core-1_0a.html#anchor38&#34;&gt;Section 11.14. Cross-Site Request Forgery
(CSRF)&lt;/a&gt;
is also worth reading (and understanding) in order to further secure the use
of callback URLs.&lt;/p&gt;
&lt;h3 id=&#34;services-supporting-oauth-1.0a&#34; tabindex=&#34;-1&#34;&gt;Services Supporting OAuth 1.0a&lt;/h3&gt;
&lt;p&gt;At the time of this writing, the only service provider I&#39;m aware of that
supports 1.0a is
&lt;a href=&#34;http://developer.yahoo.net/blog/archives/2009/05/oauth_update_3.html&#34;&gt;Yahoo!&lt;/a&gt;.
This doesn&#39;t include Fire Eagle (unless you include my laptop), although we
intend for this to change in the next couple weeks. Google&#39;s OAuth endpoint
should support 1.0a soon, but I&#39;ve yet to see any confirmation.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>My (public) Git Workflow</title>
    <link href="https://mojodna.net/2009/03/09/my-public-git-workflow.html"/>
    <updated>2009-03-09T00:00:00Z</updated>
    <id>https://mojodna.net/2009/03/09/my-public-git-workflow.html</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;: You want to be an effective contributor to a public project
that&#39;s using Git.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution&lt;/strong&gt;: Follow conventions and keep clean branches to make them
easier to merge.&lt;/p&gt;
&lt;h2 id=&#34;let&#39;s-go&#34; tabindex=&#34;-1&#34;&gt;Let&#39;s Go&lt;/h2&gt;
&lt;p&gt;For your own projects, it doesn&#39;t really matter what you do to publish and
maintain your code, at least until other people start forking and attempting
to hack on your stuff. In any case, you should keep things simple. Use topic
branches for particular features or refactoring if you want, but no big deal.&lt;/p&gt;
&lt;h3 id=&#34;determining-%22active%22-and-%22authoritative%22-forks&#34; tabindex=&#34;-1&#34;&gt;Determining &amp;quot;Active&amp;quot; and &amp;quot;Authoritative&amp;quot; Forks&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/blog/309-new-and-improved-search&#34;&gt;GitHub&#39;s search&lt;/a&gt; attempts
to surface active projects by displaying the number of forks, number of
watchers, and recent activity, but it doesn&#39;t tell the full story. The &amp;quot;root&amp;quot;
of the fork tree (which isn&#39;t really a tree, but is &lt;em&gt;kinda&lt;/em&gt; useful to think of
that way) isn&#39;t necessarily the current focus of activity. Someone may have
published a project and lost interest, but a community may have formed around
one of the forks.&lt;/p&gt;
&lt;p&gt;Alternately, a project may have been imported from Subversion by a wannabe
contributor prior to the original author joining GitHub and applying
community-created patches to his or her own fork (which, at this point, isn&#39;t
the root of the fork tree).&lt;/p&gt;
&lt;p&gt;If there are a relatively low number of forks (say, one or two), you can
(mostly) assume that the fork with the most activity is authoritative. Other
contributors, with forks that come and go or that are generally out of date,
can be considered &amp;quot;outsiders.&amp;quot;&lt;/p&gt;
&lt;p&gt;For projects with a larger number of forks, there may be multiple authorities
(and potentially even multiple development directions), with many outsiders
periodically contributing patches.&lt;/p&gt;
&lt;p&gt;All of this means that you, as a potential contributor, need to do some
additional research to determine which fork (or forks) is &amp;quot;active&amp;quot; and/or
&amp;quot;authoritative&amp;quot;. They may not be one and the same; the &amp;quot;authoritative&amp;quot; fork
may focus on stability; you may need bleeding edge features developed by an
&amp;quot;authoritative&amp;quot; member of the community instead.&lt;/p&gt;
&lt;p&gt;GitHub&#39;s &lt;a href=&#34;https://github.com/blog/39-say-hello-to-the-network-graph-visualizer&#34;&gt;Network
Graph&lt;/a&gt; is
a great tool for this purpose. From the interface, you can see which forks
have been the most active (hint: more •&#39;s), most recently active
(•&#39;s further right), or have had the most merges (more ⤴&#39;s). Those don&#39;t
tell the full story, so you&#39;ll want to mouse-over individual •&#39;s to see
who wrote the patch (and who committed it). That will help distinguish between
the forks where the patches originated and where they&#39;ve merely been pulled
in. You&#39;ll also get a sense of the number and names of people working on a
project.&lt;/p&gt;
&lt;h4 id=&#34;some-rules-of-thumb&#34; tabindex=&#34;-1&#34;&gt;Some Rules of Thumb&lt;/h4&gt;
&lt;p&gt;When hacking on other peoples&#39; stuff, your eventual goal is (probably) to get
those changes applied. Here are a few tips to make life easier for everyone
involved.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First rule of thumb&lt;/strong&gt;: merge from authoritative contributors, cherry pick
from outsiders.&lt;/p&gt;
&lt;p&gt;By merging (e.g. &lt;code&gt;git merge user/master&lt;/code&gt;) from authoritative contributors, it
becomes easier for them to apply your changes if and when they deem them fit.
Merging from outsiders may force the authorities into accepting patches (or
series) that they may not be ready for (as those branches may include changes
unrelated to what you were working on).&lt;/p&gt;
&lt;p&gt;GitHub&#39;s Fork Queue cherry picks (e.g. &lt;code&gt;git cherry-pick 469df8&lt;/code&gt;) commits by
default, so that&#39;s a good way to get your tree in order before adding your own
changes. If there are changes to your fork&#39;s parent, you can &lt;a href=&#34;https://github.com/blog/266-fast-forward-your-fork&#34;&gt;fast forward
your fork&lt;/a&gt; before starting
work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second rule of thumb&lt;/strong&gt;: keep your changes easily apply-able to the
authoritative repository.&lt;/p&gt;
&lt;p&gt;For a single line of development, this is easy: rebase against the
authoritative &lt;em&gt;master&lt;/em&gt; branch whenever possible. Feel free to rewrite history
(i.e. &lt;code&gt;git push -f&lt;/code&gt;) as long as you&#39;re confident no one is treating you as
authoritative.&lt;/p&gt;
&lt;p&gt;If others &lt;em&gt;are&lt;/em&gt; treating you as an authoritative source, create a separate
branch that gets periodically rebased and make it clear that it&#39;s not suitable
for tracking. Others&#39; changes will eventually need to be rebased against those
changes, but probably not until your changes have been applied by a real
authoritative contributor.&lt;/p&gt;
&lt;p&gt;For changes that do many things, this means keeping multiple branches active.
I usually use a branch per feature, plus a separate branch for general
bugfixes and documentation. For minor changes, all branches can diverge from a
common commit (usually the tip of the authoritative repository&#39;s &lt;em&gt;master&lt;/em&gt;
branch).&lt;/p&gt;
&lt;p&gt;For more drastic changes, you&#39;ll need to decide which is either the most
important feature or the least controversial change. Use that as the first
patch series and rebase it against the authoritative repository&#39;s &lt;em&gt;HEAD&lt;/em&gt;.
Rebase subsequent patch series against the next most important (or least
controversial).&lt;/p&gt;
&lt;p&gt;Write down the order in which your branches should be merged. You can use your
project wiki or put it at the top of the &lt;code&gt;README&lt;/code&gt; in your &lt;em&gt;master&lt;/em&gt; branch.
(&lt;a href=&#34;https://github.com/mojodna/xmpp4r/blob/06ec50260555ba242880dc2c69229ea5f8ff811e/README.rdoc&#34;&gt;See my fork of &lt;em&gt;xmpp4r&lt;/em&gt; to see how I&#39;ve done
it.&lt;/a&gt;)
At this point, your &lt;em&gt;master&lt;/em&gt; branch is just the default view of your fork and
shouldn&#39;t be considerably different from the branch of the authoritative copy
you&#39;re tracking (see the exception below).&lt;/p&gt;
&lt;p&gt;In some cases, you&#39;ll have branches that aren&#39;t quite ready for prime-time.
Treat them the same as branches that are, but don&#39;t make apply-able branches
dependent on them (i.e. they should always be the last changes to be applied).
Include them in the summary in your &lt;code&gt;README&lt;/code&gt;, but make it clear that they&#39;re
not ready yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third rule of thumb&lt;/strong&gt;: reset your history to match an authoritative
repository&#39;s history if their content is the same (or essentially the same).&lt;/p&gt;
&lt;p&gt;If your history frequently diverges from an authoritative repository&#39;s
history, you&#39;re going to have more trouble applying others&#39; changes and
synchronizing with authoritative repositories.&lt;/p&gt;
&lt;p&gt;The simplest way is to delete your repository and re-fork. If you have minor
changes, generate a diff to apply once you&#39;ve re-forked.&lt;/p&gt;
&lt;h4 id=&#34;an-exception&#34; tabindex=&#34;-1&#34;&gt;An Exception&lt;/h4&gt;
&lt;p&gt;Because GitHub will only build gems for changes on the &lt;em&gt;master&lt;/em&gt; branch, if you
have changes that you want to publish (prepared neatly in topic branches, as
above), you&#39;ll need to break the rules somewhat.&lt;/p&gt;
&lt;p&gt;To handle this scenario, start with your &lt;em&gt;master&lt;/em&gt; branch matching the
authoritative branch you&#39;re tracking, then merge branches like the owner of
the authoritative branch would (this has the helpful side-effect of
discovering whether your changes will apply cleanly).&lt;/p&gt;
&lt;p&gt;The tip of your &lt;em&gt;master&lt;/em&gt; should always contain the most up-to-date gemspec (in
order to trigger GitHub&#39;s building and publishing of your fork) as well as a
&lt;code&gt;README&lt;/code&gt; that contains information on the state of your repository (including
branches, as above, but also information about how you&#39;re treating the
&lt;em&gt;master&lt;/em&gt; branch).&lt;/p&gt;
&lt;h3 id=&#34;maintaining-a-project&#34; tabindex=&#34;-1&#34;&gt;Maintaining a Project&lt;/h3&gt;
&lt;p&gt;Over the last couple months, I&#39;ve become one of the authoritative forks for
the &lt;a href=&#34;https://github.com/mojodna/oauth&#34;&gt;OAuth gem&lt;/a&gt;, indirectly resulting in this
document. I&#39;ve attempted to be consistent in the management of my fork,
including publishing periodic releases for others to test functionality, but
it became clear that my methods weren&#39;t as transparent as they ought to be.&lt;/p&gt;
&lt;p&gt;As a maintainer (or authoritative fork), you frequently find the need to merge
in work from other developers. GitHub&#39;s &lt;a href=&#34;https://github.com/blog/270-the-fork-queue&#34;&gt;Fork
Queue&lt;/a&gt; is excellent for this, but
it&#39;s not the last word.&lt;/p&gt;
&lt;p&gt;The Fork Queue is perfect for merging individual commits, but when someone
submits a patch series, it&#39;s time to make a decision: you can &lt;em&gt;cherry-pick&lt;/em&gt;
(e.g. &lt;code&gt;git cherry-pick 469df8&lt;/code&gt;) all of their commits (which is what the Fork
Queue does behind the scenes)--retaining authorship but setting the
&lt;em&gt;committer&lt;/em&gt; field to yourself--or you can pull their branch down and perform a
&lt;em&gt;merge&lt;/em&gt; (e.g. &lt;code&gt;git merge user/master&lt;/code&gt;), retaining both &lt;em&gt;author&lt;/em&gt; and
&lt;em&gt;committer&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Cherry picking commits is easier--unless you want/need to make adjustments to
them (in which case the SHAs would change anyway)--and results in linear
history (no merges) with the downside of having repetitive commits (with
different &lt;em&gt;committer&lt;/em&gt;s) across the project network, thus confusing GitHub&#39;s
Network Graph (or anything that displays the history of multiple branches) and
making it more difficult to find un-applied patches (as the same patch may
exist with multiple SHAs).&lt;/p&gt;
&lt;p&gt;Merging changes locally is more difficult, but the &lt;a href=&#34;https://github.com/defunkt/github-gem&#34;&gt;github
gem&lt;/a&gt; makes it easier by pulling in the
entire patch universe with &lt;code&gt;gh network fetch&lt;/code&gt;. Once that&#39;s been done, the
combination of &lt;code&gt;gh network commits&lt;/code&gt;, &lt;a href=&#34;http://gitx.frim.nl/&#34;&gt;GitX&lt;/a&gt;, and the
Network Graph make it relatively easy to identify commits ripe for picking.&lt;/p&gt;
&lt;p&gt;An advantage of cherry picking is that you have the freedom to amend (&lt;code&gt;git commit --amend&lt;/code&gt;) commits before pushing them out as part of your tree. This is
a great opportunity to clean things up to match local coding standards (that
should already be the case, but YMMV) and to add additional context to the
commit message.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; You can achieve the same effect as amending commits by using &lt;code&gt;git format-patch -1 469df8&lt;/code&gt; to create a patch, applying it with &lt;code&gt;git apply&lt;/code&gt;,
pausing to clean it up, and committing it with &lt;code&gt;git commit -c 469df8&lt;/code&gt;. &lt;a href=&#34;http://gitready.com/&#34;&gt;git
ready&lt;/a&gt; has a good article on &lt;a href=&#34;http://gitready.com/intermediate/2009/03/04/pick-out-individual-commits.html&#34;&gt;picking out individual
commits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main problem with cherry picking becomes more acute when there are
multiple authoritative forks (which should maintain a consistent public state)
constantly passing work back and forth. If the participants periodically merge
others&#39; work, you end up with a number of merge commits, but a relatively
coherent history. If one or more participants choose to cherry pick commits
instead, merging their branches back will include repeated commits with
different SHAs and &lt;em&gt;committer&lt;/em&gt; fields. The resultant state of the tree is
fine, but the history gets somewhat polluted and becomes more difficult to
follow.&lt;/p&gt;
&lt;p&gt;This can be mitigated by sending patches via email (or any mechanism by which
public repositories don&#39;t change) and repeatedly munging history locally, but
that&#39;s often not an option, given the level of coordination required.&lt;/p&gt;
&lt;h3 id=&#34;aside%3A-complexity-and-%2F-or-maturity&#34; tabindex=&#34;-1&#34;&gt;Aside: Complexity and / or Maturity&lt;/h3&gt;
&lt;p&gt;I used to see complicated Network Graphs (with lots of merging and branching)
as a sign of project activity, but now I consider it more as a measure of
contributors&#39; comfort with &lt;code&gt;git&lt;/code&gt; (as individual contributors may be
responsible for multiple branches). I believe that the main reason that
projects end up with complicated history is due to (relatively) limited
coordination between collaborator and maintainer; if I had to guess, I would
expect closed projects using Git to have much more linear histories.&lt;/p&gt;
&lt;p&gt;Ultimately, I think complicated histories are a side-effect of Git&#39;s merging
prowess promoting and supporting the drive-by contributor model. However, as
powerful as Git is, coordination between collaborators still lessens the
burden on the person doing the merging, thus the above manifesto.&lt;/p&gt;
&lt;h2 id=&#34;now-what%3F&#34; tabindex=&#34;-1&#34;&gt;Now What?&lt;/h2&gt;
&lt;p&gt;Get yourself out there, coding whatever and whenever, and contribute back.
You&#39;ll feel good and you will have made the world a little better, one patch
at a time.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Extending ActiveRecord Attributes</title>
    <link href="https://mojodna.net/2007/06/14/extending-activerecord-attributes.html"/>
    <updated>2007-06-14T00:00:00Z</updated>
    <id>https://mojodna.net/2007/06/14/extending-activerecord-attributes.html</id>
    <content type="html">&lt;p&gt;David Black came to speak at the &lt;a href=&#34;http://boston.rubygroup.org/&#34;&gt;Boston Ruby
Group&lt;/a&gt; on Tuesday (along with Zed Shaw, making
it the most star-studded event since I started going). It was in one of the
lecture halls at the Harvard Science Center (which, if you follow the pattern
of every single other building on campus, is named after the &amp;quot;Science Center&amp;quot;
family; seems like it should be Merrill, or Morrill, or something
instead--here&#39;s your chance). I was expecting stand-up comedy, which turned
out to be an unreasonable expectation because it was in lecture hall A, not C
(which is where HSUCS (a group of stand-up comedians at Harvard) has had
events in the past). But I digress.&lt;/p&gt;
&lt;p&gt;David&#39;s talk was about &amp;quot;Per Object Behavior in Ruby&amp;quot; and about treating it as
a prototype language (a logical follow-up to Dave Thomas&#39; RailsConf keynote;
he didn&#39;t disappoint and provided classless code (except for the tests; that
would have been a nice touch)). I won&#39;t go much into the substance of the
talk, except to say that he managed to clarify the meaning and use of an
object&#39;s &amp;quot;singleton class&amp;quot; / &amp;quot;eigenclass&amp;quot; as well as further emphasizing that
class != type, but that a class is merely a template from which new objects
spring. He encouraged people to take advantage of Ruby&#39;s ability to extend
individual instances of objects with methods (generally via modules), as
that&#39;s a considerable part of what makes Ruby Ruby.&lt;/p&gt;
&lt;p&gt;I was reminded that I had written some ActiveRecord code this spring that
allows &amp;quot;primitive&amp;quot; (&lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Fixnum&lt;/code&gt;) attributes of AR objects to be
embellished by extending the attributes&#39; objects before returning them from
the containing object.&lt;/p&gt;
&lt;p&gt;In our case, we had a notion of &lt;code&gt;FormattedText&lt;/code&gt; objects, which were
essentially &lt;code&gt;String&lt;/code&gt;s that could contain HTML. Unfiltered HTML, as it turned
out. Rather than filtering during the input stage, we decided that we would
store what the user provided and leave it to the output stage to handle the
filtering. Instead of using a helper method in each and every case (with the
downside of having to remember to use it), I decided that the filtering
functionality would fit better within the object (so it would be used
appropriately in the majority case without any extra thought). In this case,
by overriding &lt;code&gt;String#to_s&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FormattedText&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;to_s&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# filter and output&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step was making sure that only attributes known to contain formatted
text were extended by &lt;code&gt;FormattedText&lt;/code&gt; (and that the internal representation
remained untouched, so that the filtered version wouldn&#39;t be saved back to the
database). A side-effect of this is that changing the value that was returned
and saving the object will not save the new value back to the database; to get
around this, you&#39;ll need to set the value on the AR object itself.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;# Quick and dirty monkey-patch (should work with 1.1.x+)&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Base
  &lt;span class=&#34;token comment&#34;&gt;# Attribute extension&lt;/span&gt;
  cattr_accessor &lt;span class=&#34;token symbol&#34;&gt;:extended_attributes&lt;/span&gt;
  &lt;span class=&#34;token variable&#34;&gt;@@extended_attributes&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# create the &#34;extends&#34; declaration&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;extends&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;attr_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; options&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      extended_attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;attr_name&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_sym&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; options&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:with&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  alias_method &lt;span class=&#34;token symbol&#34;&gt;:define_read_method_without_extends&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:define_read_method&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;define_read_method_with_extends&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;symbol&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; attr_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; column&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;read_methods&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;attr_name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt;
           &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;extended_attributes&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;keys&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;attr_name&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_sym&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      define_read_method_without_enhances&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;symbol&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; attr_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; column&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;
      old_method_name &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;_without_extends&#34;&lt;/span&gt;&lt;/span&gt;
      define_read_method_without_extends&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;old_method_name&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_sym&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; attr_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; column&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      evaluate_read_method attr_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;def &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;; enhance(&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;old_method_name&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;, &#39;&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;attr_name&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;); end&#34;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  alias_method &lt;span class=&#34;token symbol&#34;&gt;:define_read_method&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:define_read_method_with_extends&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;# Override read_attribute for first call (pre-edge)&lt;/span&gt;
  &lt;span class=&#34;token comment&#34;&gt;# or when AR::Base.generate_read_methods = false&lt;/span&gt;
  alias_method &lt;span class=&#34;token symbol&#34;&gt;:read_attribute_without_extends&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:read_attribute&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;read_attribute_with_extends&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;attr_name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    enhance&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;read_attribute_without_extends&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;attr_name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; attr_name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  alias_method &lt;span class=&#34;token symbol&#34;&gt;:read_attribute&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:read_attribute_with_extends&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;# Enhance the attribute as appropriate&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;enhance&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;value&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; attr_name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    mod &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;extended_attributes&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;attr_name&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_sym&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; mod &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;value&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt;
      value&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;dup&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;extend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;mod&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;
      value
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An alternate way to implement this pattern would have been to use
&lt;code&gt;composed_of&lt;/code&gt; and to create a &lt;code&gt;FormattedText&lt;/code&gt; class that extend &lt;code&gt;String&lt;/code&gt;. That
may be better for performance reasons (as it seems that it would simplify
method lookup), but strikes me as being slightly less Rubylike.&lt;/p&gt;
&lt;p&gt;What do you think?&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Teach Capistrano to Deploy From a Tag or Branch</title>
    <link href="https://mojodna.net/2007/03/08/teach-capistrano-to-deploy-from-a-tag-or-branch.html"/>
    <updated>2007-03-08T00:00:00Z</updated>
    <id>https://mojodna.net/2007/03/08/teach-capistrano-to-deploy-from-a-tag-or-branch.html</id>
    <content type="html">&lt;p&gt;There comes a time in the life of many an application where it becomes more or
less stable. And once it becomes stable, it also becomes boring because the
changes that a developer really wants to make are bound to make it unstable,
and thus unsuitable for deployment.&lt;/p&gt;
&lt;p&gt;An oft-used approach to this situation is to create a stable maintenance
branch (i.e. &lt;code&gt;application/branches/1.0&lt;/code&gt;) and create tags for point releases
(i.e. &lt;code&gt;application/tags/1.0.6&lt;/code&gt;), continuing on the trunk with potentially
destabilizing work.&lt;/p&gt;
&lt;p&gt;Out of the box, Capistrano only supports deploying from the tip of the trunk.
The snippet below demonstrates how to deploy from a branch (to a QA or staging
environment) or tag (releases to production should &lt;strong&gt;always&lt;/strong&gt; have tags).
Stick it in your &lt;code&gt;config/deploy.rb&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;set &lt;span class=&#34;token symbol&#34;&gt;:base_repository&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;http://svn.mojodna.net/repository/&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;application&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; variables&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:tag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
  set &lt;span class=&#34;token symbol&#34;&gt;:repository&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;base_repository&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;/tags/&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;variables&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:tag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;elsif&lt;/span&gt; variables&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:branch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;
  set &lt;span class=&#34;token symbol&#34;&gt;:repository&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;base_repository&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;/branches/&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;variables&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:branch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;
  set &lt;span class=&#34;token symbol&#34;&gt;:repository&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;base_repository&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;/trunk&#34;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this modification in place, you can now deploy from a branch:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;qa cap deploy &lt;span class=&#34;token parameter variable&#34;&gt;-Sbranch&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or from a tag:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;production cap deploy &lt;span class=&#34;token parameter variable&#34;&gt;-Stag&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;.6&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go forth and revel in your instability!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Classloading in Rails</title>
    <link href="https://mojodna.net/2007/02/12/classloading-in-rails.html"/>
    <updated>2007-02-12T00:00:00Z</updated>
    <id>https://mojodna.net/2007/02/12/classloading-in-rails.html</id>
    <content type="html">&lt;p&gt;Occasionally, I see code similar to the following used as a shortcut to load
all model objects. It&#39;s usually in a Rake task or somewhere that follows a
&amp;quot;global require&amp;quot; by looping through all available objects and doing something
with them. It&#39;s simple, straightforward, and a logical follow up to shorthand
like &lt;code&gt;[&amp;quot;post&amp;quot;, &amp;quot;user&amp;quot;].each { |m| require m }&lt;/code&gt;. However, it often sits out
there like a dormant volcano, with a wide variety of unpredictable behavior
waiting to erupt.&lt;/p&gt;
&lt;p&gt;In our case, we&#39;d been using said snippet to help test out
&lt;a href=&#34;https://www.danga.com/memcached/&#34;&gt;memcached&lt;/a&gt;; we would occasionally be handed
serialized objects of unrecognizable types; the source of the problem turned
out to be that the object&#39;s class definition had not yet been loaded. We also
discovered that finders in intermediate subclasses (we use &lt;abbr title=&#34;Single
Table Inheritance&#34;&gt;STI&lt;/abbr&gt; extensively) would occasionally fail to find
instances of subclasses, again because the subclasses had not been loaded (and
the intermediate subclass was unable to provide a complete list of its
subclasses).&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;# load all models explicitly&lt;/span&gt;
&lt;span class=&#34;token builtin&#34;&gt;Dir&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;glob&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;app&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;models&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;*.rb&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;rbfile&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; rbfile
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a couple of problems with this approach. It doesn&#39;t guarantee that a
model will only be loaded once--classes may have already been loaded through
some external mechanism. Alternately, dependencies may be loaded while loading
a specific class. In both cases, the above code may cause classes to be loaded
multiple times (which can cause bizarre behavior). It also doesn&#39;t handle
namespaced models correctly.&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;# force loading (but not reloading) of all models by saying their name&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# Adapted from PragDave&#39;s Annotate Models plugin.&lt;/span&gt;
model_path &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;app&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;models&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;token builtin&#34;&gt;Dir&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;glob&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;model_path&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;**&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;*.rb&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;m&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  class_name &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; m&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;sub&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;model_path &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SEPARATOR&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;sub&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token regex-literal&#34;&gt;&lt;span class=&#34;token regex&#34;&gt;/\\.rb$/&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;camelize
  klass &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; class_name&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;split&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;::&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inject&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token builtin&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;klass&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;part&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; klass&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;const_get&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;part&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;rescue&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach is considerably better; it supports namespaced models and defers
the actual classloading to Rails (note the lack of an explicit &lt;code&gt;require&lt;/code&gt;),
which prevents classes from being loaded multiple times.&lt;/p&gt;
&lt;p&gt;Anything to avoid time-eating debugging sessions, like the one described in
&lt;a href=&#34;http://blog.duncandavidson.com/2006/11/rails_sometimes.html&#34;&gt;Rails Sometimes Eats Class Load
Errors&lt;/a&gt;. In our
case, it had to do with
&lt;a href=&#34;http://code.google.com/p/activemessaging/&#34;&gt;ActiveMessaging&lt;/a&gt; and classes
silently failing to load outside the development environment. The fix wound up
being requiring &lt;code&gt;config/messaging.rb&lt;/code&gt; before the block above. The lesson
learned is to make sure that any dependent resources for models are loaded
&lt;strong&gt;first&lt;/strong&gt; and to look for them if things are acting strangely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; The above primarily relevant to Rails 1.1. 1.2 exhibits similar
behavior, but it gets even weirder, as the class loading mechanism was
rewritten. There, you&#39;ll find that STI works properly the first time (because
all classes have been explicitly loaded), but if &lt;code&gt;cache_classes&lt;/code&gt; is on (the
default in development mode), they&#39;ll be unloaded after the first request and
thus will fail to work on subsequent requests.&lt;/p&gt;
&lt;p&gt;Our rather hackish solution is to trigger a &lt;code&gt;before_filter&lt;/code&gt; in
&lt;code&gt;ApplicationController&lt;/code&gt; when &lt;code&gt;cache_classes&lt;/code&gt; is determined to be on (when
&lt;code&gt;Dependencies.mechanism == :load&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;before_filter &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
  model_path &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;app&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;models&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token builtin&#34;&gt;Dir&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;glob&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;model_path&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;**&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;*.rb&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;m&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
    class_name &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; m&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;sub&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;model_path &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SEPARATOR&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;sub&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token regex-literal&#34;&gt;&lt;span class=&#34;token regex&#34;&gt;/\\.rb$/&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;camelize
    klass &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; class_name&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;split&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#39;::&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inject&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token builtin&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;klass&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;part&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; klass&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;const_get&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;part&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;rescue&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; Dependencies&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;mechanism &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:load&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>Searchable: Annotation-Driven Indexing and Searching with Lucene</title>
    <link href="https://mojodna.net/2006/10/02/searchable-annotation-driven-indexing-and-searching-with-lucene.html"/>
    <updated>2006-10-02T00:00:00Z</updated>
    <id>https://mojodna.net/2006/10/02/searchable-annotation-driven-indexing-and-searching-with-lucene.html</id>
    <content type="html">&lt;h2 id=&#34;overview&#34; tabindex=&#34;-1&#34;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://github.com/mojodna/searchable&#34;&gt;Searchable&lt;/a&gt; is a toolkit for Lucene
that harnesses the power of annotations to specify what properties to index
and how to treat them.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;basics&#34; tabindex=&#34;-1&#34;&gt;Basics&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mojodna/searchable&#34;&gt;Searchable&lt;/a&gt; requires JDK 1.5 and Lucene
1.9+.&lt;/p&gt;
&lt;p&gt;At its core, Searchable provides a set of annotations that can be used to
instruct its indexer how to deal with properties present in an annotated bean.
It can also be used without the annotations (by writing a custom
&lt;strong&gt;Indexer&lt;/strong&gt;/&lt;strong&gt;Searcher&lt;/strong&gt; combination). However, Searchable really shines when
custom &lt;strong&gt;Indexers&lt;/strong&gt; and &lt;strong&gt;Searchers&lt;/strong&gt; are combined with the annotations.&lt;/p&gt;
&lt;p&gt;Searchable 0.8-SNAPSHOT can use &lt;a href=&#34;https://lucene.apache.org/solr/&#34;&gt;Apache Solr&lt;/a&gt;
for indexing and searching via &lt;strong&gt;SolrIndexer&lt;/strong&gt; and &lt;strong&gt;SolrSearcher&lt;/strong&gt;
(&lt;strong&gt;SchemaGenerator&lt;/strong&gt; will create an appropriate Solr schema for you; some
tweaks may be necessary).&lt;/p&gt;
&lt;p&gt;Searchable is available on GitHub here:
&lt;a href=&#34;https://github.com/mojodna/searchable&#34;&gt;https://github.com/mojodna/searchable&lt;/a&gt;
Do with it what you will.&lt;/p&gt;
&lt;p&gt;Please see the examples below for pointers on usage.&lt;/p&gt;
&lt;h3 id=&#34;annotations&#34; tabindex=&#34;-1&#34;&gt;Annotations&lt;/h3&gt;
&lt;p&gt;In order for the annotations to have any effect, they must annotate a class
that extends &lt;strong&gt;Searchable&lt;/strong&gt;. With the exception of @DefaultFields, they must
all be placed on the reader method of a property (&lt;code&gt;getXXX()&lt;/code&gt;). Unlike
traditional Annotation behavior, they may be placed on an interface method or
a method that is overridden.&lt;/p&gt;
&lt;h4 id=&#34;%40id&#34; tabindex=&#34;-1&#34;&gt;@ID&lt;/h4&gt;
&lt;p&gt;This allows a property other than &lt;em&gt;id&lt;/em&gt; to be specified as the id field. There
should only be one per class hierarchy.&lt;/p&gt;
&lt;p&gt;e.g.:&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@ID&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Integer&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getKey&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;%40indexed&#34; tabindex=&#34;-1&#34;&gt;@Indexed&lt;/h4&gt;
&lt;p&gt;This specifies that a field should be indexed. The following attributes are
available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;aliases - Array of aliases to also use as field names. Default: none.&lt;/li&gt;
&lt;li&gt;boost - Boost factor (as a float) for this field. Default: 1.0.&lt;/li&gt;
&lt;li&gt;name - Field name to use for this property. Default: property name.&lt;/li&gt;
&lt;li&gt;nested - Whether to index this field in a nested context (i.e. a
&lt;em&gt;Searchable&lt;/em&gt; as a property of another &lt;em&gt;Searchable&lt;/em&gt;). Default: false.&lt;/li&gt;
&lt;li&gt;stored - Store this property in the index. Default: false.&lt;/li&gt;
&lt;li&gt;storeTermVector - Store term vectors for this field. Default: false.&lt;/li&gt;
&lt;li&gt;tokenized - Tokenize the value of this property before adding it to the index. Default: true.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;e.g.:&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Indexed&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;boost&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;%40stored&#34; tabindex=&#34;-1&#34;&gt;@Stored&lt;/h4&gt;
&lt;p&gt;This specifies that a field should be stored in the index. In typical usage,
this should not be necessary, although you may find that it comes in hand from
time to time. The following attributes are available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;aliases - Array of aliases to also use as field names. Default: none.&lt;/li&gt;
&lt;li&gt;name - Field name to use for this property. Default: property name.&lt;/li&gt;
&lt;li&gt;nested - Whether to store this field in a nested context (i.e. a
&lt;em&gt;Searchable&lt;/em&gt; as a property of another &lt;em&gt;Searchable&lt;/em&gt;). Default: false.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;e.g.:&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Stored&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getDescription&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;%40sortable&#34; tabindex=&#34;-1&#34;&gt;@Sortable&lt;/h4&gt;
&lt;p&gt;This specifies that a custom Keyword field should be created to use for
sorting results in Lucene. The following attribute is available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nested - Whether to create a sorted field for this property in a nested
context (i.e. a &lt;em&gt;Searchable&lt;/em&gt; as a property of another &lt;em&gt;Searchable&lt;/em&gt;).
Default: false.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;e.g.:&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Indexed&lt;/span&gt;
&lt;span class=&#34;token annotation punctuation&#34;&gt;@Sortable&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;%40defaultfields&#34; tabindex=&#34;-1&#34;&gt;@DefaultFields&lt;/h4&gt;
&lt;p&gt;This contains an array of field names that should be used as the default list.
If this annotation is not present and no fields are specified when searching,
the default is to use all fields present in the index.&lt;/p&gt;
&lt;p&gt;Used during the search process.&lt;/p&gt;
&lt;p&gt;e.g.:&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@DefaultFields&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;name&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;address.city&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Searchable&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;%40excerptable&#34; tabindex=&#34;-1&#34;&gt;@Excerptable&lt;/h4&gt;
&lt;p&gt;This specifies that the annotated property should be used when creating a
search extract. &lt;strong&gt;NOTE&lt;/strong&gt;: The resultant object must be hydrated &lt;strong&gt;in your
code&lt;/strong&gt; before it can be excerpted. In many cases, this involves reloading the
object from Hibernate Session by using &lt;em&gt;session.load()&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;e.g.:&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Indexed&lt;/span&gt;
&lt;span class=&#34;token annotation punctuation&#34;&gt;@Excerptable&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getDescription&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;extension-points&#34; tabindex=&#34;-1&#34;&gt;Extension Points&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;AbstractSearcher&lt;/em&gt;, &lt;em&gt;AbstractMultiSearcher&lt;/em&gt;, &lt;em&gt;AbstractIndexer&lt;/em&gt;, and
&lt;em&gt;AbstractBeanIndexer&lt;/em&gt; are provided as abstract base classes with the majority
of necessary functionality provided as protected methods. &lt;em&gt;AbstractSearcher&lt;/em&gt;
exposes multiple signatures for certain methods that allow alternate
implementations of Lucene _IndexReader_s and _Searcher_s.
&lt;em&gt;AbstractMultiSearcher&lt;/em&gt; makes use of these to implement cross-index searching
(an example is provided below), but a similar approach could be used to
implement remote searching.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;setAnalyzer(Analyzer)&lt;/code&gt; and &lt;code&gt;setIndexPath(String)&lt;/code&gt; should be used to override
the default behavior of Searchable. Both are best called in your constructor.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getIndexReader()&lt;/code&gt;, &lt;code&gt;getIndexModifier()&lt;/code&gt;, and &lt;code&gt;getIndexSearcher()&lt;/code&gt; provide
shared access to _IndexReader_s, _IndexModifier_s, and _IndexSearcher_s over
the index specified using &lt;code&gt;setIndexPath(String)&lt;/code&gt;. In certain circumstances,
you may wish to override these methods to provide alternate implementations (a
&lt;em&gt;MultiSearcher&lt;/em&gt; for example; if you wish to provide a &lt;em&gt;RemoteSearchable&lt;/em&gt;, you
must define an additional method, as &lt;code&gt;getIndexSearcher()&lt;/code&gt; returns a
&lt;em&gt;Searcher&lt;/em&gt;, not a &lt;em&gt;Searchable&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Searcher&lt;/em&gt; and &lt;em&gt;Indexer&lt;/em&gt; are provided as interfaces that may be extended to
expose additional functionality to your application in a generic fashion.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Result&lt;/em&gt; and &lt;em&gt;ResultImpl&lt;/em&gt; have been split into an interface and an
implementation in order to hide the &lt;code&gt;add(Result)&lt;/code&gt; and &lt;code&gt;replace(Result, Result)&lt;/code&gt; methods as well as to provide additional flexibility for alternate
implementations. One such alternate implementation would also implement
&lt;a href=&#34;http://displaytag.sf.net/&#34;&gt;DisplayTag&lt;/a&gt;&#39;s &lt;em&gt;PaginatedList&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;AbstractResult&lt;/em&gt; is made available as a base class suitable for extension by
objects that implement &lt;em&gt;Result&lt;/em&gt; and are not required to extend anything else.&lt;/p&gt;
&lt;h3 id=&#34;batch-indexing&#34; tabindex=&#34;-1&#34;&gt;Batch Indexing&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;BatchIndexer&lt;/em&gt; extends the &lt;em&gt;Indexer&lt;/em&gt; interface by introducing three methods:
&lt;code&gt;flush()&lt;/code&gt; (to be implemented by the indexer), &lt;code&gt;setBatchMode(boolean)&lt;/code&gt;, and
&lt;code&gt;isBatchMode()&lt;/code&gt;, of which the latter two are provided by the indexing
infrastructure. &lt;code&gt;flush()&lt;/code&gt; is executed during &lt;code&gt;close()&lt;/code&gt; immediately before the
index is optimized. A typical implementation calls &lt;code&gt;flushDeletes()&lt;/code&gt;. which
flushes any document deletions that had previously been queued (rather than
flushing them immediately, as in a non-batch indexer). The hybrid example
below demonstrates a &lt;em&gt;BatchIndexer&lt;/em&gt; in action.&lt;/p&gt;
&lt;h3 id=&#34;limitations&#34; tabindex=&#34;-1&#34;&gt;Limitations&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;AbstractSearcher&lt;/em&gt; does not yet support default field arrays as arguments to
the various &lt;code&gt;doSearch()&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;The @DefaultFields and @Excerptable annotations are only available on objects
that implement &lt;em&gt;Searchable&lt;/em&gt;. Ideally, they would be available for any @Result,
as there&#39;s nothing that necessarily limits their use to _Searchable_s.&lt;/p&gt;
&lt;p&gt;Fields that are stored in the index are returned in a storedFields Map
(specified in the &lt;em&gt;Result&lt;/em&gt; interface) regardless of whether or not the names
correspond to properties on the object being returned. In the future, Lucene
could be used as a persistence tool of sorts by reconstituting the object as
much as possible. Thus, if all properties of an object were indexed (and
stored), the object could be fully reconstituted and no external persistence
mechanism would be necessary. The trade-off is index size, so this would not
be feasible for large datasets.&lt;/p&gt;
&lt;h3 id=&#34;examples&#34; tabindex=&#34;-1&#34;&gt;Examples&lt;/h3&gt;
&lt;h4 id=&#34;simple-example&#34; tabindex=&#34;-1&#34;&gt;Simple Example&lt;/h4&gt;
&lt;p&gt;This example demonstrates how to use Searchable without using the annotations.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;AddressIndexer.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;/**
 * Indexes Addresses.
 */&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AddressIndexer&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractIndexer&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Indexer&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AddressIndexer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token comment&#34;&gt;// use /tmp/addresses as the index path&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;setIndexPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/tmp/addresses&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Address&lt;/span&gt; address&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IndexingException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token comment&#34;&gt;// create a document with &#34;address&#34; as the type&lt;/span&gt;
        &lt;span class=&#34;token class-name&#34;&gt;Document&lt;/span&gt; doc &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;createDocument&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;address&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; address&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

        &lt;span class=&#34;token comment&#34;&gt;// add fields for each property of Address&lt;/span&gt;
        &lt;span class=&#34;token comment&#34;&gt;// implementation of Address is left to your imagination&lt;/span&gt;
        doc&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Field&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;UnStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;street&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; address&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getStreet&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
        doc&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Field&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;UnStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;city&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; address&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getCity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
        doc&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Field&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;UnStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;state&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; address&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getState&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
        doc&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Field&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;UnStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;zip&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; address&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getZip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

        &lt;span class=&#34;token comment&#34;&gt;// save the document to the index&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; doc &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;delete&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Address&lt;/span&gt; address&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IndexingException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;delete&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;address&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; address&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;AddressSearcher.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;/**
 * Searches Addresses.
 */&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AddressSearcher&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractSearcher&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Searcher&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AddressSearcher&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token comment&#34;&gt;// use /tmp/addresses as the index path&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;setIndexPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/tmp/addresses&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; query&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SearchException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;doSearch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; query &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;AddressIndexTest.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;// ...&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;AddressIndexer&lt;/span&gt; indexer &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AddressIndexer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    indexer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;makeAddress&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    indexer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;token class-name&#34;&gt;AddressSearcher&lt;/span&gt; searcher &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AddressSearcher&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt; rs &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; searcher&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;city:Cambridge&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;Result&lt;/span&gt; result &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; rs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;iterator&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;// Address does not implement Result, nor was it indexed with its&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;// fully qualified class name, so the result is a GenericResult&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertTrue&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; result &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GenericResult&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;GenericResult&lt;/span&gt; gr &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GenericResult&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; result&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;address&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; gr&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;simple-annotation-driven-example&#34; tabindex=&#34;-1&#34;&gt;Simple Annotation-Driven Example&lt;/h4&gt;
&lt;p&gt;This example demonstrates the basics of annotation-driven indexing.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;SearchableBean.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;/**
 * Implementation of Searchable to be indexed.
 *
 * Extends AbstractResult to avoid needing to implement Result methods
 * (inherited from Searchable).
 */&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SearchableBean&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractResult&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Searchable&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Integer&lt;/span&gt; id&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; name&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SearchableBean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Integer&lt;/span&gt; id&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;id &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; id&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; name&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Integer&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; id&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;setId&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Integer&lt;/span&gt; id&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;id &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; id&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Indexed&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; name&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;setName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; name&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;IndexManager.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;// ...&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;SearchableBean&lt;/span&gt; bean&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IndexingException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;BeanIndexer&lt;/span&gt; bi &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BeanIndexer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    bi&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; bean &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    bi&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; query&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SearchException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;BeanSearcher&lt;/span&gt; bs &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BeanSearcher&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;// searching will attempt to reconstitute objects and set their ids&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;// this allows the calling layer to know a) the type and b) the id&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;// in order to refresh it properly&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; bs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; query &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;IndexManagerTest.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;// ...&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;testSearch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;IndexManager&lt;/span&gt; im &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IndexManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    im&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SearchableBean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt; rs &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; im&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;name:seth&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; rs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;Result&lt;/span&gt; result &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; rs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;iterator&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertTrue&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; result &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SearchableBean&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; result&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;// name has not been set&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertNull&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; result&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;hybrid-example&#34; tabindex=&#34;-1&#34;&gt;Hybrid Example&lt;/h4&gt;
&lt;p&gt;This example demonstrates how to use Searchable in a hybrid mode.
&lt;em&gt;TeapotIndexer&lt;/em&gt; can operate in batch mode.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Teapot.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;/**
 * A representation of a teapot.  When searching the Teapot index, only the
 * &#34;name&#34; and &#34;description&#34; fields will be searched by default (if
 * @DefaultFields were not specified, all indexed fields would be searched).
 */&lt;/span&gt;
&lt;span class=&#34;token annotation punctuation&#34;&gt;@DefaultFields&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;name&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;description&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Searchable&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;/**
     * Name of the Teapot.  This serves as the id, is indexed, and can be
     * sorted by.  This is the only field that is indexed when a Teapot is
     * nested in another object (a TeaSet, for example).
     */&lt;/span&gt;
    &lt;span class=&#34;token annotation punctuation&#34;&gt;@ID&lt;/span&gt;
    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Indexed&lt;/span&gt;
    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Sortable&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;/**
     * Color of the Teapot.  Aliased to &#34;colour&#34; for Brits.
     */&lt;/span&gt;
    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Indexed&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;aliases&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;colour&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; nested&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getColor&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;/**
     * Material the Teapot is made of.  Indexed and stored.
    @Index(stored=true, nested=false)
    public String getMaterial();

    /**
     * The type of tea this Teapot contains.  Not searchable, but stored.
     */&lt;/span&gt;
    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Stored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;nested&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getTeaType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;/**
     * Description.  Indexed and used when creating an excerpt.
     */&lt;/span&gt;
    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Indexed&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;nested&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Excerptable&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getDescription&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;TeapotIndexer.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;/**
 * Indexes teapots.
 */&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TeapotIndexer&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractBeanIndexer&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BatchIndexer&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TeapotIndexer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token comment&#34;&gt;// use /tmp/teapots as the index path&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;setIndexPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/tmp/teapots&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt; tp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IndexingException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token class-name&#34;&gt;Document&lt;/span&gt; doc &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;doCreate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; tp &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

        &lt;span class=&#34;token function&#34;&gt;processBean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; tp &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;postProcessTeapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; tp &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

        &lt;span class=&#34;token comment&#34;&gt;// save the document to the index&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; doc &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;delete&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt; tp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IndexingException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;doDelete&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; tp &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IndexException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token comment&#34;&gt;// flush any pending deletes&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;flushDeletes&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Document&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;postProcessTeapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt; tp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token comment&#34;&gt;// add an &#34;owner&#34; field not specified by the teapot&lt;/span&gt;
        doc&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Field&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;UnStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;owner&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;Nathan&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;TeapotSearcher.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;/**
 * Searches teapots.
 */&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TeapotSearcher&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractSearcher&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Searcher&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TeapotSearcher&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token comment&#34;&gt;// use /tmp/teapots as the index path&lt;/span&gt;
        &lt;span class=&#34;token function&#34;&gt;setIndexPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/tmp/teapots&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; query&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SearchException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;excerpt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;refresh&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;doSearch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; query &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; results&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token class-name&#34;&gt;ResultSetImpl&lt;/span&gt; rs &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ResultSetImpl&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; results&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt; tp &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; results&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;token comment&#34;&gt;// session is a HibernateSession&lt;/span&gt;
            &lt;span class=&#34;token comment&#34;&gt;// your actual implementation may involve a DAO&lt;/span&gt;
            &lt;span class=&#34;token comment&#34;&gt;// replaces stub Teapot with a fully loaded one&lt;/span&gt;
            &lt;span class=&#34;token comment&#34;&gt;// ResultSetImpl handles transferring of Result properties&lt;/span&gt;
            rs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; tp&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; session&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; tp &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
        &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; results&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;TeapotTest.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;// ...&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Exception&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;BatchIndexer&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; indexer &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TeapotIndexer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;// run TeapotIndexer in batch mode&lt;/span&gt;
    indexer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setBatchMode&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    indexer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;makeTeapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    indexer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;token class-name&#34;&gt;Searcher&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; searcher &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TeapotSearcher&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; rs &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; searcher&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;material:china OR wireframe&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;Iterator&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; rs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;iterator&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt; firstResult &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt; secondResult &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertEquals&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;china&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; firstResult&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertTrue&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; secondResult&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getDescription&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;wireframe&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token function&#34;&gt;assertTrue&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; secondResult&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getSearchExtract&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;wireframe&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;searching-multiple-indexes-example&#34; tabindex=&#34;-1&#34;&gt;Searching Multiple Indexes Example&lt;/h4&gt;
&lt;p&gt;This example demonstrates how to use Searchable to search multiple indexes for
different types of objects. This assumes a 1 class/index breakdown, but that&#39;s
not strictly necessary.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;AddressAndTeapotSearcher.java:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;/**
 * Searches Addresses and Teapots.
 */&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AddressAndTeapotSearcher&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractMultiSearcher&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Searcher&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;/**
     * Constructs this as a MultiSearcher for Addresses and Teapots.  The order and length
     * of both arrays must be equivalent.
     */&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AddressAndTeapotSearcher&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;/tmp/addresses&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;/tmp/teapots&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
               &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Address&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Teapot&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;/**
     * Searches the teapot index using default fields determined by @DefaultFields
     * annotation on the Teapot.  Also searches the address index using all available
     * fields as defaults.  If Address implemented Searchable and contained a
     * @DefaultField annotation, those fields would be used instead.
     */&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResultSet&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; query&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;throws&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SearchException&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;doSearch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; query &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>Sprout: Annotation-Powered Simplicity for Struts</title>
    <link href="https://mojodna.net/2005/11/30/sprout-annotation-powered-simplicity-for-struts.html"/>
    <updated>2005-11-30T00:00:00Z</updated>
    <id>https://mojodna.net/2005/11/30/sprout-annotation-powered-simplicity-for-struts.html</id>
    <content type="html">&lt;h2 id=&#34;overview&#34; tabindex=&#34;-1&#34;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Sprout aims to significantly simplify development with Struts by reducing the
amount of configuration required through the use of annotations and sensible
defaults.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;basics&#34; tabindex=&#34;-1&#34;&gt;Basics&lt;/h3&gt;
&lt;p&gt;Sprout requires JDK 1.5. Without that, you&#39;re out of luck.&lt;/p&gt;
&lt;p&gt;Sprout is an extension of a Struts &lt;code&gt;MappingDispatchAction&lt;/code&gt;, which allows for
multiple actions to be defined within the same &lt;em&gt;Action&lt;/em&gt; class. In this case,
the name of the method is mapped to the URL (after converting method names;
&lt;em&gt;methodName&lt;/em&gt; is exposed as &lt;em&gt;/method_name.do&lt;/em&gt;). Paths are determined based on
the package name of the action. For example,
&lt;em&gt;net.mojodna.sprout.action.HomeAction&lt;/em&gt; corresponds to &lt;em&gt;/&lt;/em&gt;, while
&lt;em&gt;net.mojodna.sprout.action.example.ExampleAction&lt;/em&gt; corresponds to &lt;em&gt;/example/&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Sprout also uses a custom &lt;em&gt;RequestProcessor&lt;/em&gt;--&lt;code&gt;SproutRequestProcessor&lt;/code&gt;, which
extends Spring&#39;s &lt;em&gt;DelegatingRequestProcessor&lt;/em&gt;. This means that you can specify
dependencies within your actions using setter-injection.&lt;/p&gt;
&lt;p&gt;Sprout is also &lt;em&gt;completely&lt;/em&gt; backward-compatible with legacy Struts
applications.. It was built for use in a legacy Struts application; many of
the older Actions are untouched--new development is done using Sprout (I often
find myself &lt;em&gt;removing&lt;/em&gt; action-mappings while adding new functionality).&lt;/p&gt;
&lt;p&gt;Sprout is available on GitHub here:
&lt;a href=&#34;https://github.com/mojodna/sprout&#34;&gt;https://github.com/mojodna/sprout&lt;/a&gt; Do with
it what you will.&lt;/p&gt;
&lt;h3 id=&#34;annotations&#34; tabindex=&#34;-1&#34;&gt;Annotations&lt;/h3&gt;
&lt;p&gt;All annotations are optional.&lt;/p&gt;
&lt;h4 id=&#34;%40formname&#34; tabindex=&#34;-1&#34;&gt;@FormName&lt;/h4&gt;
&lt;p&gt;Allows the developer to override the name of the form-bean (defined in
&lt;em&gt;struts-config.xml&lt;/em&gt;) used for this method. This is equivalent to setting the
&lt;em&gt;name&lt;/em&gt; attribute within an &lt;em&gt;action&lt;/em&gt; mapping.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Defaults to ${action-name}Form; e.g. for &lt;strong&gt;AdminAction&lt;/strong&gt; the default
ActionForm name would be &lt;strong&gt;AdminActionForm&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;%40forward&#34; tabindex=&#34;-1&#34;&gt;@Forward&lt;/h4&gt;
&lt;p&gt;Specifies additional forwards. Multiple forwards may be specified by providing
arrays as arguments to &lt;em&gt;name&lt;/em&gt;, &lt;em&gt;path&lt;/em&gt;, and &lt;em&gt;redirect&lt;/em&gt;. &lt;em&gt;redirect&lt;/em&gt; defaults to
&lt;em&gt;false&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A default redirect is provided; the key is &lt;code&gt;Sprout.FWD_SUCCESS&lt;/code&gt; and the path
is the converted path + .jsp. E.g., &lt;em&gt;AdminAction.methodName()&lt;/em&gt; corresponds to
&lt;em&gt;method_name.jsp&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;e.g. @Forward(name=&amp;quot;failure&amp;quot;, path=&amp;quot;/failure.jsp&amp;quot; redirect=&amp;quot;true&amp;quot;)&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;%40input&#34; tabindex=&#34;-1&#34;&gt;@Input&lt;/h4&gt;
&lt;p&gt;This annotation is required if this action is validating the output from a
different action. In that case, the argument to &lt;em&gt;@Input&lt;/em&gt; should be the path to
the JSP containing the form whose input is being validated.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;e.g @Input(&amp;quot;login.jsp&amp;quot;) if this is &lt;strong&gt;not&lt;/strong&gt; login() and the action that
initiated this request &lt;strong&gt;is&lt;/strong&gt; login().&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;%40scope&#34; tabindex=&#34;-1&#34;&gt;@Scope&lt;/h4&gt;
&lt;p&gt;Specifies the &lt;em&gt;scope&lt;/em&gt; attribute for the generated action mapping. This exists
primarily for completeness; it is likely that you may never use this
annotation.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;As with &lt;strong&gt;struts-config.xml&lt;/strong&gt;, the default is &lt;strong&gt;request&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;%40validate&#34; tabindex=&#34;-1&#34;&gt;@Validate&lt;/h4&gt;
&lt;p&gt;Specifies the &lt;em&gt;validate&lt;/em&gt; attribute for the generated action mapping. Set this
to &lt;em&gt;true&lt;/em&gt; if you desire the output of this action to be validated. For this to
have any effect, you must have specified rules in &lt;em&gt;validator-rules.xml&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sprout does not contain anything to ease the actual validation process at
this time.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;example&#34; tabindex=&#34;-1&#34;&gt;Example&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;src/java/net/mojodna/sprout/action/example/ExampleAction.java&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;// URL should be /example/*&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;mojodna&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;sprout&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;action&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;example&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// ...&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ExampleAction&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Sprout&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;token comment&#34;&gt;// overrides Sprout.index()&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActionForward&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;// do something&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;// redirect to index.jsp&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; mapping&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;findForward&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;FWD_SUCCESS&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;src/java/applicationContext.xml&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-xml&#34;&gt;&lt;code class=&#34;language-xml&#34;&gt;...
&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;bean&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;ExampleAction&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;net.mojodna.sprout.action.example.ExampleAction&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;singleton&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;true&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;src/web/WEB-INF/struts-config.xml&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-xml&#34;&gt;&lt;code class=&#34;language-xml&#34;&gt;...
&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;form-bean&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;ExampleActionForm&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;org.apache.struts.validator.DynaValidatorForm&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;form-property&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;...&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;...&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  ...
&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;form-bean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;&amp;lt;!-- No action-mappings!!! --&gt;&lt;/span&gt;
&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;action-mappings&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;&amp;lt;!-- Define an alternate RequestProcessor --&gt;&lt;/span&gt;
&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;controller&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;processorClass&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;net.mojodna.sprout.SproutRequestProcessor&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;&amp;lt;!-- Sprout plug-in --&gt;&lt;/span&gt;
&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;plug-in&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;className&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;net.mojodna.sprout.SproutAutoLoaderPlugIn&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
...&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;shorthand&#34; tabindex=&#34;-1&#34;&gt;Shorthand&lt;/h3&gt;
&lt;h4 id=&#34;index-actions&#34; tabindex=&#34;-1&#34;&gt;Index Actions&lt;/h4&gt;
&lt;p&gt;Sprout contains an &lt;em&gt;index()&lt;/em&gt; method to speed up the process of getting
something working. To use this, subclass Sprout (no methods necessary),
register the action-bean in &lt;em&gt;applicationContext.xml&lt;/em&gt; and create a
corresponding &lt;em&gt;index.jsp&lt;/em&gt;. When you need to add logic to the action, override
&lt;em&gt;index()&lt;/em&gt; in your Sprout sub-class and add it there.&lt;/p&gt;
&lt;h4 id=&#34;dynaactionforms&#34; tabindex=&#34;-1&#34;&gt;DynaActionForms&lt;/h4&gt;
&lt;p&gt;Helper methods have been added to ease development using &lt;em&gt;DynaActionForms&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; key &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;foo&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; value &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;bar&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;// returns a String&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; key &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;DynaActionForm&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; form&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getString&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; key &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// returns an Object&lt;/span&gt;
&lt;span class=&#34;token class-name&#34;&gt;F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; key &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;DynaActionForm&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; form&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; key &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// sets a value&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; key&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; value &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;DynaActionForm&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; form&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; key&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; value &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;actionmessage-handling&#34; tabindex=&#34;-1&#34;&gt;ActionMessage handling&lt;/h3&gt;
&lt;p&gt;Sprout contains adaptations to the traditional way Struts handles
&lt;em&gt;ActionMessages&lt;/em&gt;. &lt;code&gt;getMessages()&lt;/code&gt; and &lt;code&gt;getErrors()&lt;/code&gt; have been modified to
store and retrieve messages from the session rather than the request. This
means that messages and errors will be displayed (and subsequently cleared) on
the next invocation of &lt;code&gt;&amp;lt;html:messages /&amp;gt;&lt;/code&gt; or a variant (such as
&lt;code&gt;&amp;lt;ui:notifications /&amp;gt;&lt;/code&gt;), regardless of whether either of the &lt;em&gt;get&lt;/em&gt; methods
have been called or if the invocation occurs during a separate request.&lt;/p&gt;
&lt;p&gt;Sample message / error handling code (within an Action):&lt;/p&gt;
&lt;pre class=&#34;language-java&#34;&gt;&lt;code class=&#34;language-java&#34;&gt;&lt;span class=&#34;token class-name&#34;&gt;ActionMessages&lt;/span&gt; msgs &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMessages&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; request &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token class-name&#34;&gt;ActionMessages&lt;/span&gt; errors &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getErrors&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; request &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// Add a message&lt;/span&gt;
msgs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// Add an error&lt;/span&gt;
errors&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;// Save messages and errors&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;saveMessages&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; request&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; msgs &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;saveErrors&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt; request&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; errors &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;ui:notifications /&amp;gt;&lt;/code&gt; (&lt;em&gt;src/web/WEB-INF/tags/ui/notifications.tag&lt;/em&gt;) is an
alternative tag file that can be modified for your use. The primary difference
between &lt;code&gt;&amp;lt;html:messages /&amp;gt;&lt;/code&gt; is that it will display both messages and errors
(and will discriminate between them, allowing you to style them differently
depending on your application&#39;s needs).&lt;/p&gt;
&lt;h3 id=&#34;servlets-and-taglibs&#34; tabindex=&#34;-1&#34;&gt;Servlets and Taglibs&lt;/h3&gt;
&lt;p&gt;Spring&#39;s Struts integration lacks the ability to autowire servlets and
taglibs. Sprout contains classes (&lt;code&gt;Sproutlet&lt;/code&gt; and &lt;code&gt;SproutTag&lt;/code&gt;) that can be
subclassed to provide auto-wiring capability. They will be wired during their
initialization process.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;These support classes only support the &lt;strong&gt;byName&lt;/strong&gt; auto-wiring mechanism.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;q-%2B-a&#34; tabindex=&#34;-1&#34;&gt;Q + A&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Q: Why the Spring dependencies?&lt;/li&gt;
&lt;li&gt;A: I&#39;m already using Spring. There&#39;s a good chance that you are as well.
Removing the dependency makes reflection upon the registered actions (or
finding them in the first place) significantly more difficult.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  <entry>
    <title>Introducing Sprout - Pure annotation goodness</title>
    <link href="https://mojodna.net/2005/10/31/struts-spring-java-5-sprout.html"/>
    <updated>2005-10-31T00:00:00Z</updated>
    <id>https://mojodna.net/2005/10/31/struts-spring-java-5-sprout.html</id>
    <content type="html">&lt;p&gt;Summary: &lt;a href=&#34;https://github.com/mojodna/sprout&#34;&gt;Sprout&lt;/a&gt;. Check it out.&lt;/p&gt;
&lt;p&gt;I have a &lt;a href=&#34;https://struts.apache.org/&#34;&gt;Struts&lt;/a&gt; application that I work on every
day. It has been a Struts application for about 3 years. It will likely
continue to be a Struts application for the foreseeable future (it&#39;s now 2009
and it&#39;s a Rails app). I imagine some of you may be in the same position.&lt;/p&gt;
&lt;p&gt;Ever since I started working on web applications in Java, I&#39;ve been poking
around the other web frameworks that are out there.
&lt;a href=&#34;http://www.opensymphony.com/webwork/&#34;&gt;WebWork&lt;/a&gt; is interesting (especially now
that it&#39;s to merge with Struts). &lt;a href=&#34;http://springframework.org/&#34;&gt;Spring MVC&lt;/a&gt; is
promising, if only because I like having my services wired together and to my
actions. &lt;a href=&#34;http://jakarta.apache.org/tapestry/&#34;&gt;Tapestry&lt;/a&gt; seemed too complex,
and &lt;a href=&#34;http://www.opensymphony.com/sitemesh/&#34;&gt;SiteMesh&lt;/a&gt;, while not a framework,
gave me everything that Tapestry was promising (granted, I haven&#39;t looked at
Tapestry a whole lot and should probably have another look).
&lt;a href=&#34;http://wicket.sf.net/&#34;&gt;Wicket&lt;/a&gt; and especially
&lt;a href=&#34;http://java.sun.com/j2ee/javaserverfaces/&#34;&gt;JSF&lt;/a&gt;, ick. The web is not a client
application and does not lend itself well to the Swing approach to components
and widgets (yet). JSF, in particular, strikes me as great for both sides of
the Toolbuilder / Application developer divide, but the overhead involved in
creating custom components (a requirement for me, as I still like reinventing
my HTML constructs) is simply too great. JSF in its optimal form also takes an
event-based view of the web, which just doesn&#39;t work so well yet (POSTs
everywhere!). This isn&#39;t about Java frameworks; this is about Struts.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href=&#34;https://rubyonrails.org/&#34;&gt;Ruby on Rails&lt;/a&gt;. Nifty. It fits the small
development team model much better (I am a development team of 1; Struts and
its ilk assume and thrive in an environment where tasks can and are divided
up, such as the one my app was originally written in). It was not created as a
framework, but more extracted from an application because it worked and made
its creator&#39;s life simpler. Convention over configuration (rather than
conventions AND configuration). Embedded opinions that just happen to jive
with my own view of the world. Looking at Rails and gaining an understanding
of how it fits together changed the way I think about web application
development. But this isn&#39;t about Rails.&lt;/p&gt;
&lt;p&gt;I have a Struts application that I work on every day. I have a minor Rails
application that I rarely work on. I like Ruby. But I also use Java.&lt;/p&gt;
&lt;p&gt;I recently spent some time focused on making Struts work for me rather than
accepting the tedium of its status quo. Out grew a
&lt;a href=&#34;https://github.com/mojodna/sprout&#34;&gt;Sprout&lt;/a&gt;. The extract of something that
makes my life easier. It might do the same for yours. Let me know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Richard Harms took this work a couple years after I abandoned it
and maintained it for a year or so as a &lt;a href=&#34;http://code.google.com/p/struts-sprout/&#34;&gt;Google Code
project&lt;/a&gt;. It lives on GitHub for
posterity (and portfolio) and includes the work that Richard contributed.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Ongoing MBTA Work</title>
    <link href="https://mojodna.net/2005/04/21/ongoing-mbta-work.html"/>
    <updated>2005-04-21T00:00:00Z</updated>
    <id>https://mojodna.net/2005/04/21/ongoing-mbta-work.html</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I haven&#39;t worked on this in many moons and it has ceased to work.
&lt;a href=&#34;http://www.thrall.net/maps/mbta.html&#34;&gt;Here is a currently working MBTA map.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The coverage area has increased to cover Boston, Charlestown, South Boston,
and Brookline (the 4 downtown maps). Rather than creating the slices for tiles
by hand in Photoshop, I whipped up a PHP script to do it for me -
&lt;a href=&#34;http://maps.mojodna.net/mbta/slicer.phps&#34;&gt;slicer.php&lt;/a&gt; (requires GD w/ GIF
support, an increased memory_limit in php.ini, and a GIF version of full.psd
(same dimensions)).&lt;/p&gt;
&lt;p&gt;I added zoom level 1 by importing the PDFs as 3500px wide images and providing
alternate coordinates to the slicer. Hint: it&#39;s much faster to work with them
after converting them to Indexed Color (via RGB, as they get imported as CMYK)
(and obviates the need to Export to Web).&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Continuing to map the MBTA</title>
    <link href="https://mojodna.net/2005/04/20/continuing-to-map-the-mbta.html"/>
    <updated>2005-04-20T00:00:00Z</updated>
    <id>https://mojodna.net/2005/04/20/continuing-to-map-the-mbta.html</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I haven&#39;t worked on this in many moons and it has ceased to work. &lt;a href=&#34;http://www.thrall.net/maps/mbta.html&#34;&gt;Here is a currently working MBTA map.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ok, so &lt;a href=&#34;/2005/04/19/mbta-maps/&#34;&gt;it&lt;/a&gt;&#39;s not that useful right now, since it only
covers a small part of Cambridge at one zoom level. And since I don&#39;t really
have the time to keep working on it right now, I&#39;ll post what I&#39;ve got so
anyone else can continue.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.mbta.com/traveling_t/schedules_pdfmaps_system.asp&#34;&gt;Maps in PDF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://maps.mojodna.net/mbta/mbta-images.tgz&#34;&gt;Tiles so far (x=5378, y=-740 (top left) to x=5386, y=-733 (bottom
right))&lt;/a&gt; (these are 128x128
each)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://maps.mojodna.net/mbta/tiles.phps&#34;&gt;Tile server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://maps.mojodna.net/mbta/mbta_google_maps.user.js&#34;&gt;Greasemonkey script&lt;/a&gt;
(change mbta.baseURL to your own copy of the tiles)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://maps.mojodna.net/mbta/full.psd.gz&#34;&gt;Cambridge/Brookline/Charlestown/Boston, rasterized and stitched together
(12MB)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The 4 downtown maps work best when rasterized to a 1710px wide canvas (before
cropping) to create the tiles for zoom=2. I experimented with different sizes
and even figured out the pixel/mile ratio (more below), but that didn&#39;t help.&lt;/p&gt;
&lt;p&gt;The PDFs are nice enough that it should be possible to create tiles for
multiple zoom levels.&lt;/p&gt;
&lt;p&gt;The MBTA also has a nice &lt;a href=&#34;http://trip.mbta.com/cgi-bin/itin_page.pl&#34;&gt;Trip
Planner&lt;/a&gt; that could potentially be
scraped to provide transfers on the sidebar (where driving directions would
usually be placed). It appears to use lat/lon pairs internally, which is
helpful.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>MBTA Maps</title>
    <link href="https://mojodna.net/2005/04/19/mbta-maps.html"/>
    <updated>2005-04-19T00:00:00Z</updated>
    <id>https://mojodna.net/2005/04/19/mbta-maps.html</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I haven&#39;t worked on this in many moons and it has ceased to work.
&lt;a href=&#34;http://www.thrall.net/maps/mbta.html&#34;&gt;Here is a currently working MBTA map&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Following on the heels of &lt;a href=&#34;https://www.holovaty.com/&#34;&gt;Adrian Holovaty&lt;/a&gt;&#39;s work
with &lt;a href=&#34;https://www.holovaty.com/blog/archive/2005/04/19/0216&#34;&gt;Chicago Transit
Authority&lt;/a&gt; maps, I dug
around on the &lt;a href=&#34;https://www.mbta.com/&#34;&gt;MBTA&lt;/a&gt;&#39;s website (Boston&#39;s T) for some
decent &lt;a href=&#34;https://www.mbta.com/traveling_t/schedules_pdfmaps_system.asp&#34;&gt;maps&lt;/a&gt;.
These are very clean and to-scale, so they were ripe for a Boston version.&lt;/p&gt;
&lt;p&gt;After some fiddling around, I discovered that the Cambridge / Somerville inset appears to correspond with Google&#39;s tiles when the PDF is imported into Photoshop with a width of 1710px. After some slicing and dicing, I give you:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://maps.mojodna.net/mbta/mbta.png&#34;&gt;&lt;img src=&#34;http://maps.mojodna.net/mbta/mbta-thumb.png&#34; border=&#34;0&#34; alt=&#34;MBTA on Google Maps&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a screenshot of &lt;a href=&#34;http://maps.mojodna.net/mbta/mbta_google_maps.user.js&#34;&gt;the Greasemonkey
version&lt;/a&gt; (with
&lt;strong&gt;minor&lt;/strong&gt; alterations to Adrian&#39;s).&lt;/p&gt;
&lt;p&gt;Building on top of some experimentation I&#39;d done last week with &lt;a href=&#34;http://stuff.rancidbacon.com/gmaps-standalone/&#34;&gt;Google Maps
stand-alone&lt;/a&gt;, I made a
non-Greasemonkey version by adding in a hook that effectively runs the
Greasemonkey script. The default zoom-level is wrong, and my quick and dirty
tile-serving code uses redirects (slowing things down and not handling missing
tiles gracefully), so I&#39;ll post that later once I get a chance to clean it up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I don&#39;t have enough time to keep going on my own, so I&#39;ve &lt;a href=&#34;https://mojodna.net/2005/04/20/continuing-to-map-the-mbta/&#34;&gt;posted
my work and a basic knowledge
dump&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Ok, so I found a bit more time and added &lt;code&gt;zoom=1&lt;/code&gt; and &lt;code&gt;zoom=3&lt;/code&gt;
(the latter with the bigger map, thus covering the suburbs, though the North
Shore is a bit off). Adding additional zoom levels (&lt;code&gt;zoom=4&lt;/code&gt;, &lt;code&gt;zoom=5&lt;/code&gt;?) is
probably worthwhile to get more of an overview. Beyond that, what&#39;s next is
probably merging maps together to increase the coverage area and using the
insets for additional zoom levels.&lt;/p&gt;
</content>
  </entry>
  

</feed>
