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

 <title>void * blog</title>
 <link href="http://gravitext.com/atom.xml" rel="self"/>
 <link href="http://gravitext.com/"/>
 <updated>2024-11-21T11:09:28-08:00</updated>
 <id>http://gravitext.com/</id>
 <author>
   <name>David Kellum</name>
   <email>dek-blog@gravitext.com</email>
 </author>

 
 <entry>
   <title>Important Crates Graph</title>
   <link href="http://gravitext.com/2021/01/25/important-crate-dependencies.html"/>
   <updated>2021-01-25T00:00:00-08:00</updated>
   <id>id:/2021/01/25/important-crate-dependencies</id>
   <content type="html">&lt;p&gt;Vertically rendered so it fits the blog format and is mobile friendly,
below. Guide level key features of the graph:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;All (rounded box) nodes are (rust lang., crates.io) crates.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Colors of nodes represent rough ownership groups and common release practices;&lt;br /&gt;
&lt;code style=&quot;padding: 0.2rem; border: 0.1rem solid black; border-radius: 0.3rem; background-color: #eed35b&quot; class=&quot;highlighter-rouge&quot;&gt;mine&lt;/code&gt;,
&lt;code style=&quot;padding: 0.2rem; border: 0.1rem solid black; border-radius: 0.3rem; background-color: #b34d3b&quot; class=&quot;highlighter-rouge&quot;&gt;hyperium&lt;/code&gt;,
&lt;code style=&quot;padding: 0.2rem; border: 0.1rem solid black; border-radius: 0.3rem; background-color: #3bb379&quot; class=&quot;highlighter-rouge&quot;&gt;tokio-rs&lt;/code&gt;,
&lt;code style=&quot;padding: 0.2rem; border: 0.1rem solid black; border-radius: 0.3rem; background-color: #e93d19&quot; class=&quot;highlighter-rouge&quot;&gt;libs-teamish&lt;/code&gt;,
etc.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Dashed lines are dev-dependencies (only).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt; label means the dependency is feature dependent, and with &lt;code class=&quot;highlighter-rouge&quot;&gt;(f)&lt;/code&gt; its a
non-default feature.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A red edge is one that is under review—we may be able to remove it in the
future, which is almost always a net benefit.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;svg-object&quot;&gt;
  &lt;object data=&quot;/svg/dep-graph.svg&quot; type=&quot;image/svg+xml&quot; width=&quot;&quot; height=&quot;&quot;&gt;
    &lt;img src=&quot;/svg/dep-graph.png&quot; width=&quot;&quot; height=&quot;&quot; /&gt;
  &lt;/object&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Pro-Tip&lt;/strong&gt;: We can produce an over-sized PNG to approximate the above vector
graphics (SVG) quality, if hosting this directly in the github host repository
proves necessary, though it really seems like github should have implemented
an SVG security scanner by now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the graph, so far, dot has managed to keep the colored groups mostly
together simply by dependency association (weight of intra-group edges.) In more
complex cases, we might need to use sub-graphs to achieve the same effect.&lt;/p&gt;

&lt;p&gt;There are many more crates &lt;em&gt;not&lt;/em&gt; shown. Just the &lt;a href=&quot;https://github.com/dekellum/body-image#readme&quot;&gt;body-image&lt;/a&gt; project lock file
has 109 crates total (some platform specific, non-default, etc.), so the above
is less than ⅕ of the total. Most of them don’t cause &lt;em&gt;enough trouble&lt;/em&gt; to be
shown here, possibly just because they are infrequently updated, or they are
leaf nodes of crates we don’t control. By selecting the &lt;em&gt;important&lt;/em&gt;
dependencies, we can better customize and read the graph. Compare to an
automated tool showing &lt;em&gt;all&lt;/em&gt; dependencies. Perhaps a more general interpretation
would be the that the &lt;em&gt;whole&lt;/em&gt; graph is too big for my best 4k monitor, but
there are potentially &lt;em&gt;many&lt;/em&gt; sub-graphs which can be extracted and custom
formatted to make them both readable and useful.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I’ve been refining this almost daily for maybe two weeks now as I work
  through new releases of most all of these crates, and some other private and
  unrelated ones.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or, on a large monitor, open the horizontally rendered version (frame) below in
a separate window, or maximize the page and zoom in:&lt;/p&gt;

&lt;div class=&quot;svg-object&quot;&gt;
  &lt;object data=&quot;/svg/dep-graph-h.svg&quot; type=&quot;image/svg+xml&quot; width=&quot;&quot; height=&quot;&quot;&gt;
    &lt;img src=&quot;/svg/dep-graph-h.png&quot; width=&quot;&quot; height=&quot;&quot; /&gt;
  &lt;/object&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Pro-Tip&lt;/strong&gt;: Node label line breaks (with &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;br/&amp;gt;&lt;/code&gt; xhtml) are the key for
  orienting the graph horizontally in particular, making the nodes more square,
  reducing width and giving more direct paths for the edge splines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;use-cases&quot;&gt;Use cases&lt;/h3&gt;

&lt;p&gt;Be honest, you want this—in order to get organized and more easily:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Update&lt;/em&gt; crate versions and dependency version ranges based on a consistent,
interior compatible strategy, by visually walking the graph in reverse arrow
direction, from dependencies to dependees.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Identify&lt;/em&gt; subsets and make new releases in reverse dependency order (by
visually walking the graph).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Intuit&lt;/em&gt; the best strategy for upgrading to originally incompatible
new releases (or releases falsely claiming Semver compatibility, e.g. MSRV
increases in patch releases of transitive deps, an all too common blight)
from dependencies outside your control.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Find&lt;/em&gt; the root cause of duplicate dependencies in the overall graph, and
infer the best strategy for removing them where possible, since they are
either breaking (at worst) or a cosmetic and build time/size disadvantage (at
best).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;potential-future-work&quot;&gt;Potential Future Work&lt;/h3&gt;

&lt;p&gt;I’d like to see if an existing automated tool (ala &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo depgraph&lt;/code&gt;) maintainer
would be interested in adding enough cli flags, optional features, or
configuration to allow me to better automate the above without loss of
fidelity, even if subsequent customization to the output dot files remains
required.  This is the default output of current &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo depgraph --all-deps&lt;/code&gt; on
&lt;em&gt;body-image&lt;/em&gt; as a point comparison:&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;width: 7.5in&quot; src=&quot;/images/cargo-depgraph-all-deps.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;(Don’t squint—I can’t see anything either.)&lt;/p&gt;

&lt;p&gt;Even with my current manually produced version, it might be revealing if it
made the distinction between &lt;em&gt;private&lt;/em&gt; (hidden) and &lt;em&gt;public&lt;/em&gt; (visible in the API)
dependency edges. (Yet another 3 year old, unstable, incomplete cargo RFC.)&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Blocking Permit</title>
   <link href="http://gravitext.com/2020/01/13/blocking-permit.html"/>
   <updated>2020-01-13T00:00:00-08:00</updated>
   <id>id:/2020/01/13/blocking-permit</id>
   <content type="html">&lt;p&gt;No one &lt;a href=&quot;https://www.reddit.com/r/rust/comments/duvdzz/announcing_asyncstd_10/f79d6ca/&quot;&gt;ever gets in trouble&lt;/a&gt; for posting micro benchmarks
and making broad assumptions about the cause of observed results!  This post
will focus on a couple of such benchmarks pertaining to blocking operations on
otherwise asynchronous runtimes. Along the way I’ll give only sparse background
on these projects I’ve been working on, but plenty of links if you are
interested in reading further. This blog post is sort of a followup to an URLO
post: &lt;a href=&quot;https://users.rust-lang.org/t/futures-0-3-async-await-experience-snapshot/29278&quot;&gt;Futures 0.3, async♯await experience snapshot&lt;/a&gt;, and
I’ll &lt;a href=&quot;https://users.rust-lang.org/t/blocking-permit/36865&quot;&gt;cross-post this one to URLO&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;I don’t care much for laptop benchmark results for software not intended to
run on laptops. So before I forget, all tests below were conducted on:&lt;/p&gt;

&lt;p&gt;ec2 m5dn.large instances &lt;br /&gt;
2x CPU: Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz &lt;br /&gt;
ubuntu 18.04 amd64 hvm-ssd 20191113 &lt;br /&gt;
kernel 4.15.0-1054-aws &lt;br /&gt;
rustc 1.42.0-nightly (da3629b05 2019-12-29) &lt;br /&gt;
tokio 0.2.8 &lt;br /&gt;
hyper 0.3.1 &lt;br /&gt;
&lt;a href=&quot;https://github.com/dekellum/blocking-permit/commit/923bddda886b96eeb3ae26afc41ec0de5282a2ce&quot;&gt;blocking-permit@923bddda&lt;/a&gt; &lt;br /&gt;
&lt;a href=&quot;https://github.com/dekellum/body-image/commit/3bc3760ecaf4f9017569605372933d82cf4f2563&quot;&gt;body-image-futio@3bc3760e&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started the &lt;em&gt;&lt;a href=&quot;https://docs.rs/crate/blocking-permit/&quot;&gt;blocking-permit&lt;/a&gt;&lt;/em&gt; crate as a prototype while &lt;em&gt;tokio’s&lt;/em&gt; much
anticipated 0.2 didn’t have any comparable facility (and I wasn’t sure how to
constructively contribute). I continued and released the crate for a few
features I want, and which the latest tokio version doesn’t yet have. That gap
could certainly be closed, but perhaps others will find these same features useful:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;for a &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatchPool&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;BlockingPermit&lt;/code&gt; that are portable across &lt;em&gt;tokio&lt;/em&gt;
and &lt;em&gt;async-std&lt;/em&gt; runtimes; or&lt;/li&gt;
  &lt;li&gt;simply to add &lt;em&gt;additional&lt;/em&gt; pool(s), specific to particular
operations, within one of these runtimes; or&lt;/li&gt;
  &lt;li&gt;for its current performance advantages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I provide some comparative micro-benchmarks via &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo bench&lt;/code&gt; with this crate,
below. In these benchmarks I compare different strategies for running blocking
operations. These include:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;direct&lt;/dt&gt;
  &lt;dd&gt;Operations are run directly on a reactor thread without any coordination.&lt;/dd&gt;
  &lt;dt&gt;permit&lt;/dt&gt;
  &lt;dd&gt;A &lt;code class=&quot;highlighter-rouge&quot;&gt;BlockingPermit&lt;/code&gt; is requested via &lt;a href=&quot;https://docs.rs/blocking-permit/1.*/blocking_permit/fn.blocking_permit_future.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;blocking_permit_future&lt;/code&gt;&lt;/a&gt; (this crate)
and a fixed size &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt;, to limit concurrency of running the operation
on a reactor thread, in the tokio case via &lt;code class=&quot;highlighter-rouge&quot;&gt;tokio::task::block_in_place&lt;/code&gt;, or
otherwise by running directly.&lt;/dd&gt;
  &lt;dt&gt;dispatch_rx&lt;/dt&gt;
  &lt;dd&gt;Operations are dispatched to a &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatchPool&lt;/code&gt; via &lt;a href=&quot;https://docs.rs/blocking-permit/1.*/blocking_permit/fn.dispatch_rx.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dispatch_rx&lt;/code&gt;&lt;/a&gt; (this crate)
and results are awaited.&lt;/dd&gt;
  &lt;dt&gt;spawn_blocking&lt;/dt&gt;
  &lt;dd&gt;Operations are dispatched via &lt;code class=&quot;highlighter-rouge&quot;&gt;tokio::task::spawn_blocking&lt;/code&gt; to a similarly
dedicated set of tokio managed threads.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;On a second axis is the type of the blocking workload under test:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;noop&lt;/dt&gt;
  &lt;dd&gt;The operation does nothing. This attempts to measure the overhead of any
coordination.&lt;/dd&gt;
  &lt;dt&gt;expensive&lt;/dt&gt;
  &lt;dd&gt;A CPU intensive operation. An array of 300 usize integers is randomly
shuffled then sorted in place.&lt;/dd&gt;
  &lt;dt&gt;sleep&lt;/dt&gt;
  &lt;dd&gt;Sleeps for an exponentially distributed semi-random sleep time of 1 to 49
microseconds. This best simulates a blocking remote operation.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;The total thread count is kept consistent across all strategies.  The
dispatching strategies (&lt;em&gt;dispatch_rx&lt;/em&gt; and &lt;em&gt;spawn_blocking&lt;/em&gt;) employ 4 core
threads and 4 “extra” dispatch-dedicated threads.  The &lt;em&gt;direct&lt;/em&gt; and &lt;em&gt;permit&lt;/em&gt;
strategies employ 8 core threads, and 4 &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; permits if applicable.  For
the &lt;em&gt;sleep&lt;/em&gt; operations, this is extended to 40 “extra” threads or permits.
Note these tests were run on a virtual host with two virtual CPUs.&lt;/p&gt;

&lt;p&gt;To simulate concurrency, batches of 100 operations (200 for &lt;em&gt;sleep&lt;/em&gt;) are
spawned and then awaited concurrently via &lt;code class=&quot;highlighter-rouge&quot;&gt;futures::stream::FuturesUnordered&lt;/code&gt;,
for each iteration of the benchmark. Some overhead could likely be attributed
to the latter’s bookkeeping.&lt;/p&gt;

&lt;h3 id=&quot;results&quot;&gt;Results&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;cargo bench --features=tokio-omnibus

test noop_threaded_direct                ... bench:      52,220 ns/iter (+/- 10,434)
test noop_threaded_dispatch_rx           ... bench:     159,234 ns/iter (+/- 57,529)
test noop_threaded_permit                ... bench:      72,940 ns/iter (+/- 3,534)
test noop_threaded_spawn_blocking        ... bench:     147,804 ns/iter (+/- 8,325)
test r_expensive_threaded_direct         ... bench:   1,253,295 ns/iter (+/- 189,310)
test r_expensive_threaded_dispatch_rx    ... bench:   1,544,520 ns/iter (+/- 164,754)
test r_expensive_threaded_permit         ... bench:   1,272,118 ns/iter (+/- 89,466)
test r_expensive_threaded_spawn_blocking ... bench:   1,574,170 ns/iter (+/- 162,377)
test sleep_threaded_direct               ... bench:     649,913 ns/iter (+/- 8,448)
test sleep_threaded_dispatch_rx          ... bench:     992,531 ns/iter (+/- 61,243)
test sleep_threaded_permit               ... bench:     800,128 ns/iter (+/- 41,226)
test sleep_threaded_spawn_blocking       ... bench:   1,624,739 ns/iter (+/- 377,409)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When comparing benchmarks for our &lt;em&gt;dispatch_rx&lt;/em&gt; vs. &lt;em&gt;spawn_blocking&lt;/em&gt;, &lt;em&gt;noop&lt;/em&gt;
and &lt;em&gt;expensive&lt;/em&gt; are fairly close, but &lt;em&gt;sleep&lt;/em&gt; (at 40 threads) using &lt;em&gt;tokio’s&lt;/em&gt;
&lt;em&gt;spawn_blocking&lt;/em&gt; is considerably slower.  As the higher thread counts are
likely representative of real world use cases with blocking APIs, I find this
noteworthy.&lt;/p&gt;

&lt;p style=&quot;font-size:smaller; font-style:italic; padding: 0em 3em;&quot;&gt;The previous &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatchPool&lt;/code&gt; of &lt;em&gt;blocking-permit&lt;/em&gt; release 0.1.0 used crossbeam’s
MPMC channel, which was much slower then the above (released as 1.0.0)
results and tokio’s &lt;em&gt;spawn_blocking&lt;/em&gt; pool, particularly at higher thread counts,
as with &lt;em&gt;sleep&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Comparing &lt;em&gt;permit&lt;/em&gt; to &lt;em&gt;direct&lt;/em&gt; strategies gives some sense of &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt;
overhead. Comparing &lt;em&gt;permit&lt;/em&gt; to either dispatching strategy is likely to be of
limited value due to limitations of these benchmarks—most importantly, there is no
actual I/O for which reactor threads need to be kept available.&lt;/p&gt;

&lt;h3 id=&quot;whats-the-motivation-behind-the-permit-strategy-anyway&quot;&gt;What’s the motivation behind the &lt;em&gt;permit&lt;/em&gt; strategy anyway?&lt;/h3&gt;

&lt;p&gt;Note that &lt;code class=&quot;highlighter-rouge&quot;&gt;tokio::task::block_in_place&lt;/code&gt; not only informs &lt;em&gt;tokio&lt;/em&gt; of an imminent
blocking operation but actively starts the process to enlist a new reactor
thread. Without some fixed number of &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; permits, the number of threads
is effectively unbounded. While &lt;em&gt;tokio 0.1&lt;/em&gt; had a configurable cap on the total
number of threads, this was found to be a liability as a dead- or live-lock
potential, and was removed in &lt;em&gt;tokio 0.2&lt;/em&gt;. If you want your process to have a
known maximum (and small) number of threads like I do, then you need to either
not use &lt;code class=&quot;highlighter-rouge&quot;&gt;block_in_place&lt;/code&gt; or constrain the blocking concurrency by some other
means. If the sum of such semaphore(s) permits is less than the configured
total number of reactor (core) threads, then we are guaranteed to always have
a reactor thread, with disused blocking threads will be kept alive and rotated
in as subsequent reactors. This turns out to be reasonably (but not perfectly)
efficient.&lt;/p&gt;

&lt;p style=&quot;font-size:smaller; font-style:italic; padding: 0em 3em;&quot;&gt;I’m surprised that something like the &lt;em&gt;permit&lt;/em&gt; strategy wasn’t mentioned in
&lt;a href=&quot;https://async.rs/blog/stop-worrying-about-blocking-the-new-async-std-runtime/&quot;&gt;Stop worrying about blocking: the new async-std runtime, inspired by
Go&lt;/a&gt; (&lt;em&gt;async-std blog&lt;/em&gt;, by Stejepan Glavina), or in
&lt;a href=&quot;https://github.com/async-rs/async-std/pull/631&quot;&gt;async-std#631&lt;/a&gt; as it would seem to be directly applicable as a strategy there
as well. (Much more on “worrying about blocking” in the conclusion.)&lt;/p&gt;

&lt;h3 id=&quot;picking-an-async-semaphore-implementation&quot;&gt;Picking an async Semaphore implementation&lt;/h3&gt;

&lt;p&gt;The above &lt;em&gt;permit&lt;/em&gt; benchmarks use &lt;em&gt;tokio’s&lt;/em&gt; async &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt;, which was
originally available in 0.2 alphas, &lt;a href=&quot;https://github.com/tokio-rs/tokio/issues/1825&quot;&gt;made private in the 0.2.0
release&lt;/a&gt;, and finally re-exported with interface changes in an
0.2.5 PATCH release. Our &lt;code class=&quot;highlighter-rouge&quot;&gt;BlockingPermitFuture&lt;/code&gt; wraps the underlying permit
future to provide a consistent, extended interface. In Tokio’s case, that
future is an unnamed type returned from an an &lt;code class=&quot;highlighter-rouge&quot;&gt;async fn&lt;/code&gt;, so I need to move it
to the heap with &lt;code class=&quot;highlighter-rouge&quot;&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;_&amp;gt;&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While the fate of tokio’s &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; was uncertain I also integrated the
&lt;em&gt;futures-intrusive&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; and this is retained for compatibility with
other runtimes. A different complication with this &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; is that its
future type isn’t &lt;code class=&quot;highlighter-rouge&quot;&gt;Sync&lt;/code&gt;, but in my usage with &lt;em&gt;hyper&lt;/em&gt;, &lt;a href=&quot;https://github.com/Matthias247/futures-intrusive/issues/23&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Sync&lt;/code&gt; becomes an
unexpected requirement&lt;/a&gt;.  The &lt;em&gt;permit&lt;/em&gt; strategy benchmarks are re-run with
this &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt;-type, below:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;cargo bench --features=futures-intrusive,tokio-threaded permit

test noop_threaded_permit                ... bench:      74,282 ns/iter (+/- 3,821)
test r_expensive_threaded_permit         ... bench:   1,304,930 ns/iter (+/- 80,482)
test sleep_threaded_permit               ... bench:   6,811,144 ns/iter (+/- 1,446,636)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that the &lt;em&gt;sleep&lt;/em&gt; workload is significantly deteriorated with the
&lt;em&gt;futures-intrusive&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt;. All &lt;em&gt;permit&lt;/em&gt; using benchmarks are currently
slower with this &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; than with &lt;em&gt;tokio’s&lt;/em&gt;.  Removing the &lt;code class=&quot;highlighter-rouge&quot;&gt;Sync&lt;/code&gt; wrapper in
these benchmarks does not measurably improve them.&lt;/p&gt;

&lt;h2 id=&quot;body-image&quot;&gt;body-image&lt;/h2&gt;

&lt;p&gt;Since the prior &lt;a href=&quot;https://users.rust-lang.org/t/futures-0-3-async-await-experience-snapshot/29278&quot;&gt;URLO post&lt;/a&gt;, the massive async♯await upgrade
has been completed, including a reasonably pleasant experience of re-writing
all its tests and benchmarks in generic, async♯await style (here with a total
of 34 uses of &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; and test LoC changes: 1636 insertions, 622
deletions). I’ve released (besides &lt;em&gt;blocking-permit&lt;/em&gt;) a new set of
body-image-* 2.0.0 crates using the latest &lt;em&gt;tokio&lt;/em&gt;, &lt;em&gt;hyper&lt;/em&gt;, and &lt;em&gt;futures&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Firstly, its worth summarizing that an original set of stream-to-sink
forwarding benchmarks, available in the &lt;em&gt;&lt;a href=&quot;https://docs.rs/body-image-futio/1.3.0/body_image_futio/&quot;&gt;body-image-futio 1.3.0&lt;/a&gt;&lt;/em&gt; release
(using tokio 0.1), have all improved at least somewhat in the 2.0.0 release
(using tokio 0.2). But I want to focus on a new set of benchmarks, added in
2.0.0, which better simulate my use case for the project.&lt;/p&gt;

&lt;p&gt;A new set of &lt;em&gt;client&lt;/em&gt; benchmarks compares the performance of making HTTP
requests to a server which returns 8MiB chunked responses and caching the body
in the various transparent ways that the body-image crate allows: scattered
allocations in ram, or written to the file system as received, and optionally
memory mapped. The client then summarizes a stream of the response body (either
in memory of read back from the filesystem) by finding the highest byte value
(always 0xFF, as proof of reading) and verifying the total length.&lt;/p&gt;

&lt;p&gt;Like in the prior &lt;em&gt;blocking-permit&lt;/em&gt; benchmarks, concurrency is simulating by
batching, in this case 16 request-response-summarize operations per benchmark
iteration.  The total thread count is kept to 4 threads (2 core, 2 “extra”
dispatch-dedicated threads) and 2 (tokio) &lt;code class=&quot;highlighter-rouge&quot;&gt;Semaphore&lt;/code&gt; permits. The tests were
run on the same ec2 host configuration described above.&lt;/p&gt;

&lt;p&gt;In results below, a separate server is run via &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo run --release --example
server&lt;/code&gt; on a separate virtual host of identical type, provided to the
benchmarks via a &lt;code class=&quot;highlighter-rouge&quot;&gt;BENCH_SERVER_URL&lt;/code&gt; environment variable. If the benchmark is
run without this variable, a server is run in process but still communicates
with the clients via the localhost TCP-stack.&lt;/p&gt;

&lt;p&gt;Terms used in the benchmark names:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;ram&lt;/dt&gt;
  &lt;dd&gt;Response &lt;code class=&quot;highlighter-rouge&quot;&gt;Bytes&lt;/code&gt; chunks are left scattered and pushed to a &lt;code class=&quot;highlighter-rouge&quot;&gt;Vec&amp;lt;Bytes&amp;gt;&lt;/code&gt;
without copying. Once the body is complete it is summarized by reading each
chunk in turn, without copying.&lt;/dd&gt;
  &lt;dt&gt;ram_gather&lt;/dt&gt;
  &lt;dd&gt;As per &lt;em&gt;ram&lt;/em&gt; above, but before processing the body, it is first gathered into
a single contiguous &lt;code class=&quot;highlighter-rouge&quot;&gt;Bytes&lt;/code&gt; buffer in one allocation.  Note this resembles many
&lt;em&gt;hyper&lt;/em&gt; body handling examples and may be an unfortunate requirement of parsing
APIs, etc.&lt;/dd&gt;
  &lt;dt&gt;fs&lt;/dt&gt;
  &lt;dd&gt;All response chunks are written directly to the filesystem, un-buffered, as
they are received. When later readying the body (once it is completely
written), 64KiB (uninitialized) buffers are used.&lt;/dd&gt;
  &lt;dt&gt;mmap&lt;/dt&gt;
  &lt;dd&gt;As per &lt;em&gt;fs&lt;/em&gt; above, but before processing, the entire body is first memory
mapped (into virtual memory). On unix, madvise(2) is then called on the
memory region with &lt;code class=&quot;highlighter-rouge&quot;&gt;MADV_SEQUENTIAL&lt;/code&gt; to suggest aggressive read-ahead, before
it is read as a single contiguous &lt;code class=&quot;highlighter-rouge&quot;&gt;UniBodyBuf&lt;/code&gt; item, with no additional
copying.&lt;/dd&gt;
  &lt;dt&gt;mmap_*_copy&lt;/dt&gt;
  &lt;dd&gt;As per above &lt;em&gt;mmap&lt;/em&gt; but copies the memory mapped region to a new &lt;code class=&quot;highlighter-rouge&quot;&gt;Bytes&lt;/code&gt; item
before processing it. The extra copy isn’t recommended but could possibly
justify the mmap if there are multiple read passes and one requires &lt;code class=&quot;highlighter-rouge&quot;&gt;Bytes&lt;/code&gt;.&lt;/dd&gt;
  &lt;dt&gt;direct&lt;/dt&gt;
  &lt;dd&gt;Blocking filesystem write/read operations are run directly on a reactor
thread without any coordination.&lt;/dd&gt;
  &lt;dt&gt;permit&lt;/dt&gt;
  &lt;dd&gt;A &lt;code class=&quot;highlighter-rouge&quot;&gt;BlockingPermit&lt;/code&gt; is obtained via &lt;a href=&quot;https://docs.rs/blocking-permit/1.*/blocking_permit/fn.blocking_permit_future.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;blocking_permit_future&lt;/code&gt;&lt;/a&gt; before running
the write and read operations on a reactor thread, in this tokio case via
&lt;code class=&quot;highlighter-rouge&quot;&gt;tokio::task::block_in_place&lt;/code&gt;.&lt;/dd&gt;
  &lt;dt&gt;dispatch&lt;/dt&gt;
  &lt;dd&gt;Write/read operations are dispatched to a &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatchPool&lt;/code&gt; via &lt;a href=&quot;https://docs.rs/blocking-permit/1.*/blocking_permit/fn.dispatch_rx.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dispatch_rx&lt;/code&gt;&lt;/a&gt;.&lt;/dd&gt;
&lt;/dl&gt;

&lt;h3 id=&quot;results-1&quot;&gt;Results&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;% BENCH_SERVER_URL=http://10.0.0.115:9087 cargo bench client

test client_01_ram              ... bench:  58,528,073 ns/iter (+/- 3,776,825)
test client_01_ram_gather       ... bench:  81,692,816 ns/iter (+/- 3,984,386)
test client_10_fs_direct        ... bench: 119,846,890 ns/iter (+/- 1,999,313)
test client_10_fs_permit        ... bench: 141,334,426 ns/iter (+/- 3,529,708)
test client_12_fs_dispatch      ... bench: 151,809,679 ns/iter (+/- 6,100,962)
test client_15_mmap_direct_copy ... bench: 137,397,757 ns/iter (+/- 2,643,698)
test client_15_mmap_permit_copy ... bench: 157,148,683 ns/iter (+/- 6,222,127)
test client_16_mmap_direct      ... bench: 110,491,801 ns/iter (+/- 2,796,833)
test client_17_mmap_permit      ... bench: 124,647,965 ns/iter (+/- 4,390,988)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The aggregate mean transfer and processing rate, in terms of the original body
size, for the fastest, &lt;em&gt;ram&lt;/em&gt; case is 2.14 GiB/sec or 17.1 Gib/sec. Amazon AWS
suggests that one can get “up to 25 Gbps” of (raw TCP) bandwidth for these
instances, independent of EBS traffic, and made possible because client and
server instances are in the same VPC and availability zone. Since the client
instance’s 2 CPUs are approximately saturated in these tests (mostly system
time actually), I would expect results to be slower if TLS was also involved
(its not).&lt;/p&gt;

&lt;p style=&quot;font-size:smaller; font-style:italic; padding: 0em 3em;&quot;&gt;An interesting if entirely anecdotal comparison, is the maximum ~70 MB/s
reported (reading off graph y axis) in that
&lt;a href=&quot;https://async.rs/blog/stop-worrying-about-blocking-the-new-async-std-runtime/#benchmarks&quot;&gt;same async-std blog article&lt;/a&gt;
running on much larger “m5a.16xlarge” and
“m5a.8xlarge” instances? Perhaps that benchmark wasn’t so optimized with large
body payloads or has other constraints?&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;fs&lt;/em&gt; (and &lt;em&gt;mmap&lt;/em&gt;) results are based an ext4 filesystem on EBS “gp2” SSD
network attached storage. The “m5dn.large” ec2 instance type also has access to
directly attached NVMe SSD (instance store). However, mounting and using that
storage for the body-image temp. files was not found to significantly change the
results. At the observed rate, gp2 storage for these &lt;em&gt;sequential&lt;/em&gt; writes and
sequential (and mapped) reads, does not appear to be a significant bottleneck,
but see below for additional optimizations.&lt;/p&gt;

&lt;h2 id=&quot;using-tmpfs&quot;&gt;Using tmpfs&lt;/h2&gt;

&lt;p&gt;Yes, tmpfs can behave as a RAM disk, so let me explain the use case lest I’m
accused of cheating just for the benchmarks.  As async♯await can be described
as &lt;em&gt;cooperative multi-tasking&lt;/em&gt;, this use of tmpfs can be described as
&lt;em&gt;cooperative swapping&lt;/em&gt;.  Linux tmpfs will start using available swap space if
the instance experiences memory pressure.  By writing body chunks to tmpfs, I’m
telling the kernel that these are currently low-priority pages that it can
remove them from RAM if needed. Once the body is downloaded, and processing
begins by reading or memory mapping, I’m informing the kernel that these pages
are again high-priority.  This &lt;em&gt;cooperative swapping&lt;/em&gt; will behave much better
than the uncooperative swapping that could occur if I just keep all bodies in
RAM and cause memory pressure that way.  Then &lt;em&gt;any&lt;/em&gt; operation in the entire
process, reactor or other threads, becomes a potential blocking operation as it
may need to wait for executable or heap pages to swap back in!  The kernel
doesn’t really have much to go on for page prioritization purposes.&lt;/p&gt;

&lt;p&gt;Another nice feature of tmpfs on Linux is that it supports Transparent Huge
Pages (default 2MiB) without any other required configuration. Huge pages
drastically reduce kernel virtual memory bookkeeping overhead for memory mapping,
at the cost of over-allocating up to 1 page of memory per mapped file.  There
may be workarounds for space efficiency as well, but I’m currently not worried
much about it.&lt;/p&gt;

&lt;p&gt;Below shows the setup for tmpfs with huge pages and then the updated benchmark
results:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;% sudo mount -t tmpfs -o size=500M,huge=always,uid=1001,gid=1001 tmpfs \
    ../target/testmp

% BENCH_SERVER_URL=http://10.0.0.115:9087 cargo bench client

test client_01_ram              ... bench:  58,392,758 ns/iter (+/- 16,045,924)
test client_01_ram_gather       ... bench:  85,527,831 ns/iter (+/- 18,187,177)
test client_10_fs_direct        ... bench:  94,056,654 ns/iter (+/- 2,915,506)
test client_10_fs_permit        ... bench: 120,463,257 ns/iter (+/- 2,742,582)
test client_12_fs_dispatch      ... bench: 130,157,569 ns/iter (+/- 5,993,078)
test client_15_mmap_direct_copy ... bench: 104,351,507 ns/iter (+/- 2,596,867)
test client_15_mmap_permit_copy ... bench: 129,762,411 ns/iter (+/- 6,165,487)
test client_16_mmap_direct      ... bench:  79,705,427 ns/iter (+/- 3,013,775)
test client_17_mmap_permit      ... bench:  99,289,803 ns/iter (+/- 4,002,980)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As expected the &lt;em&gt;fs&lt;/em&gt; and &lt;em&gt;mmap&lt;/em&gt; results are all improved. Here &lt;em&gt;mmap_direct&lt;/em&gt;
actually outperforms &lt;em&gt;ram_gather&lt;/em&gt;, and is only a 36% latency overhead beyond
&lt;em&gt;ram&lt;/em&gt; (without gather). Now, what if I only need to use &lt;em&gt;fs&lt;/em&gt; (and &lt;em&gt;mmap&lt;/em&gt;) on
10% or 1% of responses exceed some configured size in the &lt;code class=&quot;highlighter-rouge&quot;&gt;Tunables&lt;/code&gt;, the
remainder staying in ram? That net overhead should be more like 3.6% or
0.36%. With tmpfs, this seems like an effective way to have my cake and eat it
too—a very limited resource cost for piece of mind that my process isn’t going
to exceed RAM or thrash if it requires any swap space at all, and no need to
enforce some low maximum http body size or low concurrency.&lt;/p&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p style=&quot;font-size:larger; font-style:italic;&quot;&gt;&lt;br /&gt;
Dear Reader, &lt;br /&gt;
&lt;br /&gt;
Having made it this far, did you notice that in all cases tested, the &lt;strong&gt;direct&lt;/strong&gt;
strategy (blocking a reactor thread) performs best?&lt;/p&gt;

&lt;p&gt;Its true. I have &lt;em&gt;almost&lt;/em&gt; nothing positive performance-wise to show for a
significant amount of effort spent implementing the &lt;em&gt;permit&lt;/em&gt; and &lt;em&gt;dispatch&lt;/em&gt;
strategies employed in these tests.  One minor win is that from preliminary
benchmarking I made the decision (in the 3rd rewrite, &lt;em&gt;sigh&lt;/em&gt;) to compose
&lt;em&gt;permit&lt;/em&gt; and &lt;em&gt;dispatch&lt;/em&gt; strategies as wrappers over &lt;em&gt;direct&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncBodyImage&lt;/code&gt;
(our &lt;code class=&quot;highlighter-rouge&quot;&gt;Stream&lt;/code&gt;) and &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncBodySink&lt;/code&gt; types, so the latter may be directly
selected at compile time, without further overhead or complication.&lt;/p&gt;

&lt;p style=&quot;font-size:smaller; font-style:italic; padding: 0em 3em;&quot;&gt;One other possible advantage of &lt;strong&gt;permit&lt;/strong&gt;, as hinted in
these benchmark results, is that the &lt;em&gt;variance&lt;/em&gt; (if not the mean) of
request/response handling is reduced with &lt;em&gt;permit&lt;/em&gt;.  That &lt;strong&gt;might&lt;/strong&gt; be
important in some use cases, for example for fairness to multiple users.&lt;/p&gt;

&lt;p&gt;How could &lt;em&gt;direct&lt;/em&gt; be best performing? What have I done wrong, if the
&lt;em&gt;demigods of rust async&lt;/em&gt; (well, some of them) tell us that blocking (their?
our?) reactor threads is a bad idea? I speculate that this is both true and
that I’ll be using the &lt;em&gt;direct&lt;/em&gt; strategy in production, for all of the
following reasons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Memory cache locality and coherence is rather important in I/O heavy
applications, so moving a read or write operation with a pointer to a buffer
in some CPU’s cache or NUMA zone to another thread that might get scheduled
anywhere is inherently costly. And this is typically exacerbated on
virtualized instances. I believe this is one reason why &lt;em&gt;permit&lt;/em&gt; tends to
outperform the &lt;em&gt;dispatch&lt;/em&gt; strategy in the above client benchmarks.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;8KiB or 512KiB filesystem sequential writes to SSDs (network attached or
local) simply aren’t &lt;em&gt;blocking enough&lt;/em&gt; to matter, at least as compared with
the overhead of any other strategy.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Tokio’s threaded runtime, with sufficient number of reactor threads and its
work stealing abilities, is quiet capable of handling an occasionally blocked
reactor thread without replacement or slowdown, at least for this tested work load.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think there may still be plenty of &lt;em&gt;other&lt;/em&gt; use cases for either the
&lt;code class=&quot;highlighter-rouge&quot;&gt;BlockingPermit&lt;/code&gt; or the &lt;code class=&quot;highlighter-rouge&quot;&gt;DispatchPool&lt;/code&gt;, but they will tend to be much more
course grain. For example, I envision dispatching a one-step blocking operation
to uncompress HTTP body payloads enmasse, in the background of a particular
server, once bodies are completely downloaded.&lt;/p&gt;

&lt;p&gt;These findings similarly make me suspect that efforts to provide a direct
drop-in &lt;code class=&quot;highlighter-rouge&quot;&gt;std&lt;/code&gt; replacement in the form of &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncRead&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncWrite&lt;/code&gt; and
associated types and functions, is probably attacking the problem at too low a
level.  At the very least, I don’t suspect those traits as currently conceived
will be useful for &lt;em&gt;body-image-futio&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Some related questions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Does &lt;em&gt;hyper-tls&lt;/em&gt; or &lt;em&gt;native-tls&lt;/em&gt; or &lt;em&gt;tokio-tls&lt;/em&gt; offload packet decryption
from the reactor thread? If any of these do, I haven’t been able to find
where it happens. Please point it out for me.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Does hyper’s HTTP 1 &lt;em&gt;chunked transfer encoding&lt;/em&gt; implementation attempt to
offload Base64 encode/decode from the reactor thread? Should it?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Does the newer &lt;em&gt;&lt;a href=&quot;https://docs.rs/async-compression/0.2.0/async_compression/&quot;&gt;async-compression&lt;/a&gt;&lt;/em&gt; worry about blocking a reactor thread?
Should it?&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or is the general strategy with these just that only a small block, say 8KiB or
64KiB, is decrypted/decoded/decompressed for each &lt;code class=&quot;highlighter-rouge&quot;&gt;Stream::poll_next()&lt;/code&gt;?  That
would sound a lot like the above best performing &lt;em&gt;direct&lt;/em&gt; strategy with
&lt;em&gt;blocking&lt;/em&gt; filesystem I/O.&lt;/p&gt;

&lt;p style=&quot;font-size:larger; font-style:italic&quot;&gt;Why use the new &lt;em&gt;body-image-*&lt;/em&gt; 2.0.0 crates?&lt;/p&gt;

&lt;p&gt;If you can do all of your dynamic response body production, POST request body
consumption, or client request/response body handling in a purely streaming,
chunk-at-a-time fashion, then that’s going to be the best thing to do. Fire and
forget. The pattern is generally limited to HTTP proxies and load balancers.&lt;/p&gt;

&lt;p style=&quot;font-size:larger; font-style:italic&quot;&gt;If however, you need to do things like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;parse body payloads, producing large parse graphs in RAM, or&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;mutate the start of a body based on content in the middle or end of the body, or&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;produce dynamic content bodies much faster then your clients, or server POST
endpoints, can consume them over the network&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…then you might find the above tested setups and tunable options with
&lt;em&gt;body-image&lt;/em&gt; actually gain you efficiencies and/or make you sleep better at
night not worrying over memory consumption, unbounded thread growth, or
uncontrolled swapping.  Let me know what you find!&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Cargo build.rs recipes</title>
   <link href="http://gravitext.com/2019/04/25/cargo-build-rs-recipes.html"/>
   <updated>2019-04-25T00:00:00-07:00</updated>
   <id>id:/2019/04/25/cargo-build-rs-recipes</id>
   <content type="html">&lt;p&gt;Rust is very open, which is great in comparison to some other language
communities. I’m reminded in particular of one run by Sun and later
Oracle. Ever tried filing an issue on Java’s public bug tracker?  But its hard
to look at the Rust RFC process, and in particular at the RFCs one feels they
&lt;em&gt;need&lt;/em&gt; accepted, without finding some delay or failure evident from the sheer
human scale and attempting to achieve such broad consensus.&lt;/p&gt;

&lt;p&gt;Take for example, &lt;a href=&quot;https://github.com/rust-lang/rfcs/pull/2495&quot;&gt;RFC 2495: &lt;em&gt;Minimum Supported Rust Version&lt;/em&gt;&lt;/a&gt;. Unlike
many other RFCs, this one is particularly simple: Add a field to &lt;code class=&quot;highlighter-rouge&quot;&gt;Cargo.toml&lt;/code&gt;
to express the minimum version of the rust compiler that a project needs to
successfully build.  Stepping back, its really hard to even imagine, given all
the great progress and features of cargo, that it doesn’t yet have this basic
feature as found in other languages with &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo&lt;/code&gt; like tools. Why wouldn’t we
want to at least collect that data point when a crate is released?  Why
wouldn’t we use this data point for warning messages or refined dependency
resolution for users that, for one reason or another, are stuck on older rust
releases?  RFC 2495 remains open after 10 months, apparently stalled with a
notable lack of core team engagement.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/rust-lang/rfcs/pull/2523&quot;&gt;RFC 2523: &lt;em&gt;#[cfg(accessible(..) / version(..))]&lt;/em&gt;&lt;/a&gt; is closely related
and could eventually offer a superset of features. It has had the engagement,
but suffers from being overly broad in scope. The &lt;code class=&quot;highlighter-rouge&quot;&gt;accessible(..)&lt;/code&gt; tests have
unresolved implementation questions and &lt;code class=&quot;highlighter-rouge&quot;&gt;version(..)&lt;/code&gt; tests are (to my
surprise) controversial.&lt;/p&gt;

&lt;h2 id=&quot;enforcing-msrv-with-buildrs&quot;&gt;Enforcing MSRV with build.rs&lt;/h2&gt;

&lt;p&gt;Without these RFCs, Cargo’s &lt;a href=&quot;https://doc.rust-lang.org/cargo/reference/build-scripts.html&quot;&gt;build.rs&lt;/a&gt; feature allows us to take matters into
our own hands. Here is a self contained recipe for enforcing a project’s
MSRV. You’ll need to change just the &lt;code class=&quot;highlighter-rouge&quot;&gt;PACKAGE&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;msrv&lt;/code&gt; lines for your own
projects:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cfg_attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;feature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cargo-clippy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;allow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;clippy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PACKAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;'static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;tao-log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msrv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;'static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;env!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CARGO_PKG_VERSION&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;M_V&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;'static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;minimum supported rust version (MSRV)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rustv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rustc_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rustv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msrv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;{} v{} {} is {} &amp;gt; {} (this rustc)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PACKAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;M_V&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msrv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rustv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.is_empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'.'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Parse `rustc --version` and return as vector of integers, or panic.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rustc_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rustc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RUSTC&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap_or&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rustc&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_owned&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rustc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_utf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.starts_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rustc &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vp&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;.filter_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vss&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;.collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vs&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.is_empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rustc version not found&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above and subsequent code snippets are from &lt;a href=&quot;https://github.com/dekellum/tao-log&quot;&gt;&lt;em&gt;tao-log&lt;/em&gt;&lt;/a&gt;, released
under dual MIT and Apache licenses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2019-6-4:&lt;/strong&gt; Applied a &lt;a href=&quot;https://github.com/dekellum/tao-log/commit/b764a2f77&quot;&gt;fix&lt;/a&gt; to above function &lt;code class=&quot;highlighter-rouge&quot;&gt;rustc_version&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2021-1-19:&lt;/strong&gt; Contextually silence clippy lints (line 1), assuming
  newish clippy while avoiding breakage on old rustc.&lt;/p&gt;

&lt;p&gt;Without the much more pleasant, declarative, RFC proposal, we are forced to
carefully write (and test) forward/backward compatible rust code that will work
with versions as old as might be attempted. This build script will work with
rust as old as 1.0.0.  Note the use of pre-2018-edition syntax, like the
generous helping of &lt;code class=&quot;highlighter-rouge&quot;&gt;'static&lt;/code&gt; lifetimes. We must include the project name
directly because &lt;code class=&quot;highlighter-rouge&quot;&gt;env!(&quot;CARGO_PKG_NAME&quot;)&lt;/code&gt; isn’t available until cargo 0.10.0
and rust 1.9.0.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;version_check&lt;/em&gt; crate, or the more elaborate &lt;em&gt;rustc_version&lt;/em&gt; or &lt;em&gt;autocfg&lt;/em&gt;
crates, could be used to minimize the per-project build.rs, at the expense of 1
or more additional build time dependencies.  I had started with &lt;em&gt;version_check&lt;/em&gt;
but replaced it with the above, adding only 22 code lines, and with simpler
error handling for this use case.&lt;/p&gt;

&lt;p&gt;Don’t forget to add a &lt;code class=&quot;highlighter-rouge&quot;&gt;build&lt;/code&gt; field to the &lt;code class=&quot;highlighter-rouge&quot;&gt;[package]&lt;/code&gt; section of your
&lt;code class=&quot;highlighter-rouge&quot;&gt;Cargo.toml&lt;/code&gt; so that older cargo releases will always run this build
script. Prior to rust/cargo 1.17.0, specifying this field was
&lt;a href=&quot;https://github.com/rust-lang/cargo/pull/3364&quot;&gt;required&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[package]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;build&lt;/span&gt;         &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;build.rs&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With this in place, for &lt;em&gt;tao-log&lt;/em&gt; master branch, if you attempt to compile with
say 1.16.0, this is the output:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  Compiling tao-log v0.2.0 (file:///home/david/src/tao-log)
error: failed to run custom build command for
`tao-log v0.2.0 (file:///home/david/src/tao-log)`
process didn't exit successfully: `/home/david/src/tao-log/target/debug/build/
tao-log-318f175d5ac13527/build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at 'tao-log v0.2.0 minimum supported rust version (MSRV)
is 1.31.0 &amp;gt; 1.16.0 (this rustc)', build.rs:20
note: Run with `RUST_BACKTRACE=1` for a backtrace.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without this, you would get the following, as the first of many fatal errors
using 1.16.0:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;warning: unused manifest key: package.edition
   Compiling tao-log v0.2.0 (file:///home/david/src/tao-log)
error[E0432]: unresolved import
   --&amp;gt; src/lib.rs:171:9
    |
171 | pub use ::log;
    |         ^^^^^

error: aborting due to previous error

error: Could not compile `tao-log`.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In considering the value of this, please put yourself in the shoes of
prospective new rust users, and new users of your project. Would seeing the
latter error breed confidence in either?  I think I would have assumed I needed
to add another dependency. But that really won’t help here.&lt;/p&gt;

&lt;p&gt;For 1.27.0 through 1.30.0 the build will fail before the build script, as the
included cargo is &lt;em&gt;aware&lt;/em&gt; of the &lt;code class=&quot;highlighter-rouge&quot;&gt;edition&lt;/code&gt; field, but not ready for the 2018
edition. These rust versions never reach the build script stage. With 1.30.0:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;error: Edition 2018 is unstable and only available for nightly builds of rustc.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…which is confusing, from our current vantage point, because the 2018 edition
was released in stable 1.31.0. Even a &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;rustc&lt;/code&gt; as new as 1.30.0 is not
going to offer the more useful and current truth.  With 1.27.0 it is slightly
worse, with a suggestion that is distracting at best:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Caused by:
  editions are unstable

Caused by:
  feature `edition` is required

this Cargo does not support nightly features, but if you switch to nightly
channel you can add `cargo-features = [&quot;edition&quot;]` to enable this feature
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These last issues are unique to the 2018 Edition, however, and only for 1.27.0
through 1.30.0.&lt;/p&gt;

&lt;p&gt;Another potential problem is that your dependencies can fail prior to the
build script running. Worse, since builds are run in parallel, they may fail
inconsistently based on timing (though they do always fail.) For &lt;em&gt;tao-log&lt;/em&gt;, the
&lt;em&gt;log&lt;/em&gt; dep requires 1.16.0, so below that version &lt;em&gt;log&lt;/em&gt; will fail in its own
perplexing way, for example with 1.15.0:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;error: borrowed value does not live long enough
   --&amp;gt; /home/david/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.6/src/lib.rs:830:36
    |
830 |                 args: format_args!(&quot;&quot;),
    |                                    ^^ does not live long enough
...
837 |     }
    |     - temporary value only lives until here
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If dependencies are updated to enforce MSRV themselves, then this could also be
avoided.&lt;/p&gt;

&lt;p&gt;The following table summaries the compile outcomes for &lt;em&gt;tao-log&lt;/em&gt; using various
rust versions, with or without the above build.rs. The first row case depends on
the MSRV of dependencies.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;rust version&lt;/th&gt;
      &lt;th&gt;with build.rs&lt;/th&gt;
      &lt;th&gt;without&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;—1.15.0&lt;br /&gt;(MSRV of deps)&lt;/td&gt;
      &lt;td&gt;perplexing (deps) or&lt;br /&gt;clear MSRV error&lt;/td&gt;
      &lt;td&gt;perplexing (ours or deps)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.16.0—1.26.0&lt;/td&gt;
      &lt;td&gt;clear MSRV error&lt;/td&gt;
      &lt;td&gt;perplexing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.27.0—1.29.0&lt;/td&gt;
      &lt;td&gt;editions unstable&lt;/td&gt;
      &lt;td&gt;editions unstable&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.30.0&lt;/td&gt;
      &lt;td&gt;edition 2018 unstable&lt;/td&gt;
      &lt;td&gt;edition 2018 unstable&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.31.0&lt;/td&gt;
      &lt;td&gt;success&lt;/td&gt;
      &lt;td&gt;success&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.32.0—&lt;/td&gt;
      &lt;td&gt;success (optimized)&lt;/td&gt;
      &lt;td&gt;success&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;As rust versions progress and we decide to raise our MSRV or otherwise use new
version features when available (see below), this recipe will continue to work
correctly, and the various problem cases should become less frequent.  Even for
some future new rust edition, we might optimistically hope for better considered
error messages.&lt;/p&gt;

&lt;h2 id=&quot;configuring-for-versions-beyond-msrv&quot;&gt;Configuring for versions beyond MSRV&lt;/h2&gt;

&lt;p&gt;With the above build.rs in place, it becomes trivial to add additional &lt;code class=&quot;highlighter-rouge&quot;&gt;cfg&lt;/code&gt;
flags based on versions beyond MSRV, on the end of the &lt;code class=&quot;highlighter-rouge&quot;&gt;main&lt;/code&gt; function:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rustv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cargo:rustc-cfg=tao_log_trailing_comma&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…then in the code:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;#[macro_export]&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;#[cfg(tao_log_trailing_comma)]&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;macro_rules!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__tao_logv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lvl:expr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$exp:expr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(,)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;$(,)?&lt;/code&gt; syntax is for ignoring a trailing comma in macro rule patterns. It
is first available in rust 1.32.0. In our MSRV 1.31.0, we settle for &lt;code class=&quot;highlighter-rouge&quot;&gt;$(,)*&lt;/code&gt;
which is more lenient then we really want.&lt;/p&gt;

&lt;p&gt;Note the “tao_log_” prefix as namespace. There are other cargo/rustc defined
flags, and its unclear if these cfg flags are applied globally. Regardless, the
prefix makes it clear that this is defined in &lt;em&gt;tao-log&lt;/em&gt; itself.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>SyncWrap for Engineers</title>
   <link href="http://gravitext.com/2014/03/20/syncwrap-for-engineers.html"/>
   <updated>2014-03-20T00:00:00-07:00</updated>
   <id>id:/2014/03/20/syncwrap-for-engineers</id>
   <content type="html">&lt;p&gt;Not too long ago, it was common for an internet company’s &lt;em&gt;dedicated
operations staff&lt;/em&gt; to manually provision non-virtualized hosts, by
following formalized instructions maintained in &lt;em&gt;runbooks&lt;/em&gt;.  Things
changed at a glacial pace by today’s standards, and developers
frequently encountered resistance to deploying anything with new
provisioning requirements. These days, startups and other kinds of
small companies are flocking to virtualized environments, and Amazon
EC2 in particular.  All too commonly, non-technical management
actually believes (or prefers to pretend) that there is no longer any
need for operations effort and budget in these environments.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/jakarta_slum.jpg&quot; alt=&quot;jakara&quot; /&gt;&lt;sup id=&quot;fnref:cc&quot;&gt;&lt;a href=&quot;#fn:cc&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;A dirty little secret is that the majority of these companies are not
currently using &lt;em&gt;any&lt;/em&gt; provisioning automation and many are not even
using any application deployment automation.&lt;sup id=&quot;fnref:np&quot;&gt;&lt;a href=&quot;#fn:np&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; That’s not really
surprising given no time or budget for learning projects, classes,
paid support, a dedicated “expert” consultant, or hire.&lt;/p&gt;

&lt;p&gt;Without automation, hosts are manually maintained. Some manage to
adopt a modicum of discipline in maintaining virtual machine
images. While images are better than nothing, as an approach this is
essentially cut-and-paste reuse, with very similar
limitations.&lt;sup id=&quot;fnref:images&quot;&gt;&lt;a href=&quot;#fn:images&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Ultimately:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Images end up collecting and propagating cruft, abandoned
modifications and additions&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Hosts are not reproducible, say for staging or testing, at least not
without down time for imaging&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Hosts can not be reproduced if unexpectedly lost&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Fear, loathing, and practical inability to perform general
maintenance and updates (i.e. closing security holes, updating to
the latest base-OS, experimental disk storage optimization)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Silos of host ownership and access (often driven by the same fears)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Failed and abandoned attempts to document the manual
provisioning/deployment process (runbook style); where the document
is never quite accurate and up-to-date.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;provisioning-and-deployment-automation&quot;&gt;Provisioning and Deployment Automation&lt;/h2&gt;

&lt;p&gt;Anyone who has experienced well maintained and fully automated
provisioning and deployment knows what its worth in avoiding the above
described problems and as a general enabler to rapid system and
application improvements.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;provisioning system&lt;/strong&gt; needs to take us from the current Linux
distribution base image to hosts ready for deployment as any of the
numerous different roles we need: database servers, web application
servers, etc. As provisioning requirements change (i.e new developers
need access, ruby needs an upgrade) the provisioning system needs to
be able to (1) reliably update existings hosts; and/or (2) enable us
to quickly build replacement hosts, test them, rotate them in, and
eventually shutdown the old.  We need to be able to trust running it
repeatable on our most critical production hosts.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;deployment system&lt;/strong&gt; needs to support continuous release while not
adversely impacting current users and stability of the applications.
It may need to orchestrate incremental or rolling updates to achieve
this.&lt;/p&gt;

&lt;p&gt;Its questionable if &lt;em&gt;provisioning&lt;/em&gt; vs. &lt;em&gt;deployment&lt;/em&gt; is more historic
artifact than useful distinction. At the very least, its often quite
useful to provision and deploy in a single step within a unified
system.&lt;/p&gt;

&lt;h2 id=&quot;the-case-for-syncwrap&quot;&gt;The Case for SyncWrap&lt;/h2&gt;

&lt;p&gt;Current commercially-backed provisioning tools Chef and Puppet are too
complex and abstract to enable the majority of small
projects/companies to even &lt;em&gt;start&lt;/em&gt; automating provisioning, and
are frequently found cumbersome or inadequate for deployment.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/&quot;&gt;SyncWrap&lt;/a&gt; aims to provide a unified provisioning and deployment tool
in plain Ruby, with minimal learning curve for Ruby programmers.  See
&lt;a href=&quot;/2014/03/19/provision-programming.html&quot;&gt;Provision Programming&lt;/a&gt; for some examples of style. The following
conditions might also help to distinguish SyncWrap:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;If you already have Chef or Puppet working, and are happy with it
for both provisioning and deployment, then kudos for achieving full
automation. You have most certainly made a significant investment
and will not likely want to abandon those efforts.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you are an engineer, have Ruby and a little Bash scripting
ability, and want to automate quickly, with minimal learning curve.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you expect the first provisioning task to go nearly as fast as
the manual alternative. Otherwise you won’t be able to justify
automation with yourself or management.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you can’t afford to learn and maintain two different tools for
each of provisioning and deployment, and can’t accept the lack of
speed or fine grain control with the alternatives for deployment.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If for that one hour you had to try and automate provisioning, you
bounced off the &lt;em&gt;abstraction envelope&lt;/em&gt; of Chef or Puppet.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you previously used &lt;a href=&quot;http://rubyhitsquad.com/Vlad_the_Deployer.html&quot;&gt;Vlad&lt;/a&gt; or &lt;a href=&quot;http://capistranorb.com/&quot;&gt;Capistrano&lt;/a&gt;, like the direct
approach, but want a more powerful and provisioning capable tool.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If provisioning a correct system Ruby is important. The alternatives
that run Ruby on the host being provisioned can have a bit of a
chicken-and-egg problem. &lt;sup id=&quot;fnref:preruby1&quot;&gt;&lt;a href=&quot;#fn:preruby1&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:preruby2&quot;&gt;&lt;a href=&quot;#fn:preruby2&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you’d prefer programming in Python instead of Ruby, you might try
&lt;a href=&quot;http://docs.fabfile.org/&quot;&gt;Fabric&lt;/a&gt; or &lt;a href=&quot;http://stackful-dev.com/cuisine-the-lightweight-chefpuppet-alternative&quot;&gt;Cuisine&lt;/a&gt; which have a similar design philosophy to
SyncWrap.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;commercial-tools-target-adminstrators&quot;&gt;Commercial Tools Target Adminstrators&lt;/h3&gt;

&lt;p&gt;While I don’t wish to single out Puppet, they did recently provide
some material which offers some useful points for comparison:&lt;/p&gt;

&lt;p&gt;From &lt;a href=&quot;http://puppetlabs.com/blog/puppet-executable-documentation&quot;&gt;Puppet is Executable Documentation&lt;/a&gt;
(January 7, 2014 Puppet Labs Blog):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Don’t Re-Invent bash in Puppet&lt;/em&gt;: If you have a series of exec
resources chained together, you’re Doing It Wrong. While you may be
used to thinking of things as iterative steps, that’s thinking like
a programmer instead of like a documenter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The author has apparently observed a programmer committing certain
crimes of programming with Puppet. Perhaps from the programmer’s
perspective, a series of Bash commands was more straightforward than
using Puppet’s DSL and declarative features?  Later in the same post:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Don’t be afraid to write a little Ruby to create a custom function
for that logic. Even if you don’t know Ruby, you’ll find that — 99
percent of the time — googling how to do what you want doesn’t
require any previous knowledge of the language. Also, check to make
sure the function doesn’t already exist in the stdlib module. There
are currently 84 functions available in stdlib alone. For example,
the pick function in stdlib allows you take a series of variables
and select the first one that has a valid value. The strip function
in stdlib will take an array of strings and remove any leading and
trailing spaces from each string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Puppet, or at the very least this post, is not targeting a Ruby
developer who would naturally use Ruby core &lt;code class=&quot;highlighter-rouge&quot;&gt;map(&amp;amp;:strip)&lt;/code&gt; without
blinking or looking for the matching &lt;em&gt;Puppet&lt;/em&gt; “stdlib” function. There
is an implicit tension to the idea of programming here.&lt;/p&gt;

&lt;p&gt;Ultimately, Puppet’s modules and Chef’s cookbooks designs show their
roots in the &lt;em&gt;runbook&lt;/em&gt; style.  In an environment where there is a
whole other team &lt;em&gt;doing the programming&lt;/em&gt;, it may have been a practical
or tactical necessity to abstract out and declare as much as possible.
Both Puppet and Chef seem more geared for a classic system
administrator role, than for engineers who prefer to directly program.&lt;/p&gt;

&lt;h3 id=&quot;capistrano&quot;&gt;Capistrano&lt;/h3&gt;

&lt;p&gt;From &lt;a href=&quot;http://capistranorb.com/2013/06/01/release-announcement.html&quot;&gt;Capistrano Version 3 Release Announcement&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Applicability.&lt;/strong&gt; We’ve always maintained that Capistrano is a terrible
tool for system provisioning, and that more often than not servers
are better being setup with Chef, Puppet or similar, whilst we
still agree with that, the new features in Capistrano really lend
themselves to integrating with these kinds of tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Despite the author’s position, we’ve seen numerous attempts to use
Capistrano for provisioning, many of which have been generally
successful at providing some reasonable degree of automation.
However, Capistrano also seems to suffer from over-abstraction,
framework, and barriers to using basic Ruby language features like
classes, methods, inheritance and overrides. V3 attempts to hide all
command shell invocation behind another new DSL, embedded in the Rake
DSL.&lt;/p&gt;

&lt;p&gt;In a blog post from the same month, &lt;a href=&quot;http://lee.hambley.name/2013/06/11/using-capistrano-v3-with-chef.html&quot;&gt;Using Capistrano v3 With Chef&lt;/a&gt;
the same author demonstrates using Capistrano to &lt;em&gt;deploy&lt;/em&gt; (or is it
provision?) Chef Solo (the free version of Chef) which in turn
provisions the host, before Capistrano deploys to it.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We’ve been working really hard to push Capistrano v3, after years of
languishing behind the cutting edge, and in an environment where
Chef and Puppet (which should really restrict themselves to working
on infrastructure) encroaching on deploying applications, as well;
I’ve been really happy to have had the chance to code a really nice
way bridge the gaps, and make Capistrano work better with Chef for
deployments where repeatable infrastructure is important, but fast
deployments of application code are essential…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/&quot;&gt;SyncWrap&lt;/a&gt; takes a different approach and enables clean, direct
programming of the low-level details in Bash, which effectively &lt;em&gt;is&lt;/em&gt;
the Domain Specific Language for low-level remote interaction.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:cc&quot;&gt;
      &lt;p&gt;From Wikipedia &lt;a href=&quot;http://en.wikipedia.org/wiki/File:Jakarta_slumlife66.JPG&quot;&gt;Jakarta_slumlife66.JPG&lt;/a&gt;; Jonathan McIntosh, 2004, under the Creative Commons attribution 2.0 generic license&amp;nbsp;&lt;a href=&quot;#fnref:cc&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:np&quot;&gt;
      &lt;p&gt;From my own recent experience and an informal survey of colleagues.&amp;nbsp;&lt;a href=&quot;#fnref:np&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:images&quot;&gt;
      &lt;p&gt;This is not to say that there isn’t still an important role and utility for images; more later.&amp;nbsp;&lt;a href=&quot;#fnref:images&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:preruby1&quot;&gt;
      &lt;p&gt;“Most Vagrant baseboxes install Ruby for use by Chef and Puppet, but the latest ones do this into /opt/ruby rather than using the system packages.” Stackoverflow: &lt;a href=&quot;http://stackoverflow.com/questions/7859698/installing-ruby-1-9-2-on-ubuntu-using-chef-and-vagrant&quot;&gt;Installing Ruby 1.9.2 on Ubuntu using Chef and Vagrant&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;#fnref:preruby1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:preruby2&quot;&gt;
      &lt;p&gt;“Changing the system ruby when you depend on Chef is always a bootstrappy web of emotion and anti-gravity, and moving to Ruby 2 was even more scary given how many dependencies are assumed on Chef’s end.” StatusPage.io: &lt;a href=&quot;http://blog.statuspage.io/moving-to-ruby-2-on-mac-rvm-and-ubuntu-12-04&quot;&gt;Moving to ruby-2.0.0-p0 on Mac+RVM and Ubuntu 12.04&lt;/a&gt;&amp;nbsp;&lt;a href=&quot;#fnref:preruby2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Provision Programming</title>
   <link href="http://gravitext.com/2014/03/19/provision-programming.html"/>
   <updated>2014-03-19T00:00:00-07:00</updated>
   <id>id:/2014/03/19/provision-programming</id>
   <content type="html">&lt;p&gt;Enabling direct programming of your provisioning and deployment
automation, rather than obscuring it, is a principal goal of
&lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/&quot;&gt;SyncWrap&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;Lets consider, as an example, provisioning users (aka roles) for use
with a PostgreSQL database. The database engine may be installed via
the existing &lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/SyncWrap/PostgreSQL.html&quot;&gt;SyncWrap::PostgreSQL&lt;/a&gt; component. If doing this manually
we might start by adding the first database user we need, simply by
running (PostgreSQL CLI) &lt;code class=&quot;highlighter-rouge&quot;&gt;createuser bob&lt;/code&gt; on each host. Lets start by
simply doing the same with SyncWrap.  We create a new &lt;code class=&quot;highlighter-rouge&quot;&gt;MyDatabase&lt;/code&gt;
component (here directly in the sync.rb, but it could also be
require’d from lib/my_database.rb, see &lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/examples/LAYOUT_rdoc.html&quot;&gt;LAYOUT&lt;/a&gt;) and add it to the
appropriate hosts (or roles):&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyDatabase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SyncWrap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Component&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;install&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;user: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'postgres'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      createuser bob
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    SH&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'localhost'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;-SH&lt;/code&gt; &lt;em&gt;here-document&lt;/em&gt; is arguably overkill for this simple Bash
command, but the syntax will prove helpful as the Bash fragments
become more complex. The above assumes we are testing this on a
localhost (where PostgreSQL already installed), but you might also
want to try plugging this into something like &lt;a href=&quot;https://github.com/dekellum/syncwrap/blob/dev/examples/ec2.rb#L28&quot;&gt;examples/ec2.rb&lt;/a&gt; from
the SyncWrap git repo and test with a temporary EC2 host.&lt;/p&gt;

&lt;p&gt;Initial output using syncwrap -v (–verbose) flag:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;syncwrap -f db_setup.rb -v
== localhost #&amp;lt;Module:0x0000000321f6a8&amp;gt;::MyDatabase#install: enqueue
&amp;lt;-- sh localhost (-v coalesce user:postgres live)
createuser bob
--&amp;gt; Exit 0 (success)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;repeatability&quot;&gt;Repeatability&lt;/h2&gt;

&lt;p&gt;Now if we run the above a second time, we’ll get an error:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;syncwrap -f db_setup.rb -v
== localhost #&amp;lt;Module:0x000000028a7620&amp;gt;::MyDatabase#install: enqueue
&amp;lt;-- sh localhost (-v coalesce user:postgres live)
createuser bob
createuser: creation of new role failed: ERROR:  role &quot;bob&quot; already exists
== localhost error [1]:
CommandFailure: sudo exit code: 1
/home/david/src/syncwrap/lib/syncwrap/context.rb:261:in `capture_stream'
/home/david/src/syncwrap/lib/syncwrap/context.rb:189:in `run_shell'
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In general, when automating provisioning and deployment, we need to
make the process &lt;em&gt;idempotent&lt;/em&gt;, meaning that we can run it multiple
times and get the same desired result.  Here’s the simplest fix:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyDatabase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SyncWrap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Component&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;install&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;user: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'postgres'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      createuser bob || true
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    SH&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This continues to show the error on verbose output but returns success
and allows the automation to continue. In general this is preferable
to no automation. If this is checked into version control, basic
repeatable automation and self documentation has been achieved. A
remaining problem is that &lt;code class=&quot;highlighter-rouge&quot;&gt;createuser&lt;/code&gt; could fail for more reasons
than just that we already created the user, but our script fragment
ignores all errors. A more robust approach would be to first check if
the user already exists:&lt;sup id=&quot;fnref:alter&quot;&gt;&lt;a href=&quot;#fn:alter&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyDatabase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SyncWrap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Component&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;install&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sql_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT count(*) FROM pg_user WHERE usename = 'bob'&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;user: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'postgres'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      if [[ $(psql -tA -c &quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql_test&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;) == &quot;0&quot; ]]; then
        createuser bob
      fi
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    SH&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;de-composition-in-ruby&quot;&gt;De-/Composition in Ruby&lt;/h2&gt;

&lt;p&gt;If at some point in the future we need to add a second PostgreSQL
user, then the script fragment becomes a good candidate for a Ruby
method:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyDatabase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SyncWrap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Component&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;install&lt;/span&gt;
    &lt;span class=&quot;sx&quot;&gt;%w[ bob joanne ]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_create_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Create the PostgreSQL user if not already present&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pg_create_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sql_test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT count(*) FROM pg_user WHERE usename = '&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;'&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;user: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'postgres'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      if [[ $(psql -tA -c &quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql_test&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;) == &quot;0&quot; ]]; then
        createuser &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      fi
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    SH&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The SyncWrap command queue will effectively combine any number of
&lt;code class=&quot;highlighter-rouge&quot;&gt;pg_create_user&lt;/code&gt; calls into a script to be executed in a single shell
session, so there is no significant overhead vs. implementing the loop
in Bash. Indeed we can see this by running it with verbose output
enabled:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;syncwrap -f db_setup.rb -v
== localhost #&amp;lt;Module:0x0000000300aed0&amp;gt;::MyDatabase#install: enqueue
&amp;lt;-- sh localhost (-v coalesce user:postgres live)
if [[ $(psql -tA -c &quot;SELECT count(*) FROM pg_user WHERE usename = 'bob'&quot;) == &quot;0&quot; ]]; then
  createuser  bob
fi
psql -tA -c &quot;SELECT count(*) FROM pg_user WHERE usename = 'bob'&quot;)
psql -tA -c &quot;SELECT count(*) FROM pg_user WHERE usename = 'bob'&quot;
if [[ $(psql -tA -c &quot;SELECT count(*) FROM pg_user WHERE usename = 'joanne'&quot;) == &quot;0&quot; ]]; then
  createuser  joanne
fi
psql -tA -c &quot;SELECT count(*) FROM pg_user WHERE usename = 'joanne'&quot;)
psql -tA -c &quot;SELECT count(*) FROM pg_user WHERE usename = 'joanne'&quot;
--&amp;gt; Exit 0 (success)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that the script echoing is via Bash’s own &lt;code class=&quot;highlighter-rouge&quot;&gt;set -v&lt;/code&gt;. Since the
psql test command is run in a subshell it is echoed more than
once. If we drop users “bob” and “joanne” and run it again with the -x
flag, which uses Bash’s &lt;code class=&quot;highlighter-rouge&quot;&gt;set -x&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;syncwrap -f db_setup.rb -vx
== localhost #&amp;lt;Module:0x0000000345ac60&amp;gt;::MyDatabase#install: enqueue
&amp;lt;-- sh localhost (-x coalesce user:postgres live)
++ psql -tA -c 'SELECT count(*) FROM pg_user WHERE usename = '\''bob'\'''
+ [[ 0 == \0 ]]
+ createuser bob
++ psql -tA -c 'SELECT count(*) FROM pg_user WHERE usename = '\''joanne'\'''
+ [[ 0 == \0 ]]
+ createuser joanne
--&amp;gt; Exit 0 (success)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This shows that createuser is indeed run based on the matching
condition. If run again, we see that the conditional is working as
intended and createuser is not run if the users already exist:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;syncwrap -f db_setup.rb -vx
== localhost #&amp;lt;Module:0x000000028a2c60&amp;gt;::MyDatabase#install: enqueue
&amp;lt;-- sh localhost (-x coalesce user:postgres live)
++ psql -tA -c 'SELECT count(*) FROM pg_user WHERE usename = '\''bob'\'''
+ [[ 1 == \0 ]]
++ psql -tA -c 'SELECT count(*) FROM pg_user WHERE usename = '\''joanne'\'''
+ [[ 1 == \0 ]]
--&amp;gt; Exit 0 (success)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As a final example of composition, the fragment below taken from
&lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/SyncWrap/PostgreSQL.html&quot;&gt;SyncWrap::PostgreSQL&lt;/a&gt; demonstrates the block form of &lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/SyncWrap/Component.html#method-i-sh&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sh&lt;/code&gt;&lt;/a&gt; (here
the &lt;code class=&quot;highlighter-rouge&quot;&gt;sudo&lt;/code&gt; variant) where Ruby function &lt;code class=&quot;highlighter-rouge&quot;&gt;dist_service&lt;/code&gt; enqueues
commands from within an outer Bash conditional.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;if [ ! -d '&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg_data_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/base' ]; then&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;close: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;fi&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_data_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_default_data_dir&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SH&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      mkdir -p &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg_data_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      chown postgres:postgres &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg_data_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      chmod 700 &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg_data_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    SH&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dist_service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'postgresql'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'initdb'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;The Bash used above can be thought of as low-level anonymous
functions&lt;sup id=&quot;fnref:closure&quot;&gt;&lt;a href=&quot;#fn:closure&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, where all higher level logic is implemented in
Ruby. The remote pre-provisioning requirements are limited to sudo
access and the bash shell, which comes by default on target Linux
distributions.  Since provisioning tasks are typically composed from
system commands, Bash is a reasonable choice at this level.  To access
remote state, we need to be able to write bash conditionals and some
simple pipelines. The (Wooledge) &lt;a href=&quot;http://mywiki.wooledge.org/BashGuide/TestsAndConditionals&quot;&gt;BashGuide&lt;/a&gt; and &lt;a href=&quot;http://wiki.bash-hackers.org/doku.php&quot;&gt;Bash Hackers Wiki&lt;/a&gt;
may be useful resources. As shown, syncwrap provides convenient
diagnostic options in order to debug your bash fragments.&lt;/p&gt;

&lt;p&gt;Should every PostgreSQL user need to implement &lt;code class=&quot;highlighter-rouge&quot;&gt;pg_create_user&lt;/code&gt; like
methods?. The &lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/SyncWrap/PostgreSQL.html&quot;&gt;SyncWrap::PostgreSQL&lt;/a&gt; component can and should (&lt;a href=&quot;https://github.com/dekellum/syncwrap/issues/1&quot;&gt;#1&lt;/a&gt;) be
extended to provide these kinds of general utility methods, freeing
MyDatabase of needing to implement the same. Given dynamic component
binding, the &lt;code class=&quot;highlighter-rouge&quot;&gt;pg_create_user&lt;/code&gt; method can simply be moved to the
&lt;a href=&quot;http://rdoc.gravitext.com/syncwrap/SyncWrap/PostgreSQL.html&quot;&gt;SyncWrap::PostgreSQL&lt;/a&gt; component class, with the install method
remaining unchanged.&lt;/p&gt;

&lt;p&gt;The example demonstrates the low barrier from manual provisioning in
an interactive shell, to basic automation in SyncWrap using similar
bash fragments, unobscured.  For flexibility and reuse, functional
decomposition and re-composition can be done in Ruby with real
Component objects–no need to deal with Bash functions.  Add the
ability to push templated configuration files, coordinated in Ruby,
and we have a complete and direct system for provisioning and
deployment.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:alter&quot;&gt;
      &lt;p&gt;More intelligent provisioning might also check if the user privileges have changed since initially created. PostgreSQL’s &lt;code class=&quot;highlighter-rouge&quot;&gt;ALTER ROLE&lt;/code&gt; could be used to later promote bob to a super-user, for example.&amp;nbsp;&lt;a href=&quot;#fnref:alter&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:closure&quot;&gt;
      &lt;p&gt;But be careful with leaking Bash variables, as there is no real &lt;em&gt;closure&lt;/em&gt;.&amp;nbsp;&lt;a href=&quot;#fnref:closure&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Versioning</title>
   <link href="http://gravitext.com/2012/07/22/versioning.html"/>
   <updated>2012-07-22T00:00:00-07:00</updated>
   <id>id:/2012/07/22/versioning</id>
   <content type="html">&lt;p&gt;A colleague recently pointed out that version &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; was invalid
&lt;a href=&quot;http://semver.org/spec/v1.0.0.html&quot;&gt;Semantic Versioning&lt;/a&gt; as referenced in the Iūdex developers
guide.  I’ll show that SemVer has been a moving target, and the tools
involved don’t entirely support it.  But at minimum, the intent and
meaning of &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; may not be as clear as I had hoped.  I’ll
document the pitfalls found along the way, and suggest a clearer
strategy moving forward.&lt;/p&gt;

&lt;p&gt;tl/dr: Given the complexities, incompatibilities and pitfalls
involved, don’t bother with &lt;em&gt;pre-&lt;/em&gt;release version notations. See
&lt;a href=&quot;#conclusion&quot;&gt;conclusion&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;ελληνικά&quot;&gt;Ελληνικά&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; in &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; was for &lt;em&gt;beta&lt;/em&gt;. The Greek letter names &lt;em&gt;alpha&lt;/em&gt;
and &lt;em&gt;beta&lt;/em&gt; have long been used to designate &lt;em&gt;pre-release&lt;/em&gt; software.  I
would define this further and pragmatically as software made available
with either known problems or without sufficient testing for
confidence that it will meet the same quality levels as current or
future &lt;em&gt;release&lt;/em&gt; versions.&lt;sup id=&quot;fnref:aa&quot;&gt;&lt;a href=&quot;#fn:aa&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; I had wanted a short form comparable
with the decimal versions, but perhaps &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.α.0&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.β.0&lt;/code&gt; would
have been more clear?  I joke, but it offers a fitting segue to the
underlying issue here: tool compatibility and interoperability. I
simply don’t have the fortitude to use &lt;code class=&quot;highlighter-rouge&quot;&gt;~&amp;gt; 1.2.β&lt;/code&gt; in a Rubygems
dependency or the equivalent Maven version range
&lt;code class=&quot;highlighter-rouge&quot;&gt;[1.2.β,1.3)&lt;/code&gt;. Fortunately in all specifications and tools discussed
below &lt;code class=&quot;highlighter-rouge&quot;&gt;beta&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt;; &lt;code class=&quot;highlighter-rouge&quot;&gt;alpha&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; are interchangable.&lt;/p&gt;

&lt;h2 id=&quot;semantic-versioning&quot;&gt;Semantic Versioning&lt;/h2&gt;

&lt;p&gt;The version &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; is invalid, with different possible corrections,
with three different versions of &lt;a href=&quot;http://semver.org/spec/v1.0.0.html&quot;&gt;SemVer&lt;/a&gt;. Since commit &lt;a href=&quot;https://github.com/mojombo/semver/commit/05a00df02db3d4ba83e0caaff6b31c10c77f7d3d&quot;&gt;05a00df0&lt;/a&gt;
last year, a pre-release modifier/component is required to be
delimited with a &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt; hyphen instead of the &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt; dot used in prior
versions. The following table shows corrected examples based on each:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;SemVer Version&lt;/th&gt;
      &lt;th&gt;Example Corrections&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;before &lt;a href=&quot;https://github.com/mojombo/semver/commit/05a00df02db3d4ba83e0caaff6b31c10c77f7d3d&quot;&gt;05a00df0&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;1.2.0.b1&lt;br /&gt;1.2.0.b-1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.0.0&lt;/td&gt;
      &lt;td&gt;1.2.0-b1&lt;br /&gt;1.2.0-b-1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2.0.0-rc.1&lt;/td&gt;
      &lt;td&gt;1.2.0-b.1&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Note that only SemVer 2.0.0-rc.1 offers numeric sort order for the &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;
following the &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt;. All other versions have the potential pitfall of
lexicographic sorting, e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;b10&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;b9&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;b-10&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;b-9&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But returning to the practical: No release version of SemVer is
compatible with Rubygems.&lt;/p&gt;

&lt;h2 id=&quot;rubygems&quot;&gt;Rubygems&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; version is compliant with the rubygems version notation
which currently supports the following rule set:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Strict dot delimited notation with no additional modifiers.&lt;/li&gt;
  &lt;li&gt;Dot delimited versions tokens sort in numeric order if numeric.&lt;/li&gt;
  &lt;li&gt;Tokens containing non-numeric characters sort in lexicographic
order&lt;sup id=&quot;fnref:ac&quot;&gt;&lt;a href=&quot;#fn:ac&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and always before numeric values.&lt;/li&gt;
  &lt;li&gt;For the purpose of ordering, it is as if each version is postfixed
by an infinite series of &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; tokens. Thus &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b&lt;/code&gt; is less than &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2&lt;/code&gt;
because it is less than &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.0.0…&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Any non-numeric characters indicate the version is a &lt;em&gt;pre-release&lt;/em&gt;,
and thus won’t be installed by default unless &lt;code class=&quot;highlighter-rouge&quot;&gt;gem --prerelease&lt;/code&gt; is
specified.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My intent with &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; was to release a 1.2 beta series followed by
a &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.0&lt;/code&gt; release. The third position would sort as the progression
&lt;code class=&quot;highlighter-rouge&quot;&gt;a b 0 1 …&lt;/code&gt; The problem with this is that the rubygems authors had a
different progression in mind, given the behavior of “pessimistic” or
“spermy” version specifiers with pre-releases. The table shows the
intended progression and what some specifiers match:&lt;/p&gt;

&lt;table style=&quot;font-size:smaller; line-height:1.1em&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Version&lt;/th&gt;
      &lt;th&gt;~&amp;gt; 1.1.0&lt;/th&gt;
      &lt;th&gt;~&amp;gt; 1.2.b&lt;/th&gt;
      &lt;th&gt;~&amp;gt; 1.2.0&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1.1.0&lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.1.1&lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.a.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.b.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.b.1&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.1&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.3.b.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;¤&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.3.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;¤&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.4.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;¤&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;In my own usage, I had expected &lt;code class=&quot;highlighter-rouge&quot;&gt;~&amp;gt; 1.2.b&lt;/code&gt; to accept the beta series
and subsequent 1.2.x releases but &lt;em&gt;not&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.3.b.0&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;1.3.0&lt;/code&gt; and beyond
(&lt;code class=&quot;highlighter-rouge&quot;&gt;¤&lt;/code&gt; above).&lt;/p&gt;

&lt;p&gt;The actual implemented behavior of pessimistic specifiers with
pre-release tokens is obscure and undocumented.  Through my own
&lt;a href=&quot;https://github.com/rubygems/rubygems/pull/350&quot;&gt;testing&lt;/a&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;~&amp;gt; 1.2.b&lt;/code&gt; is effectively equivalent to
&lt;code class=&quot;highlighter-rouge&quot;&gt;[ '~&amp;gt; 1.2', '&amp;gt;= 1.2.b' ].&lt;/code&gt; The closest alternative specification for
my original intent is &lt;code class=&quot;highlighter-rouge&quot;&gt;[ '&amp;gt;= 1.2.b', '&amp;lt; 1.3.a' ]&lt;/code&gt; which is awkward at
best.&lt;/p&gt;

&lt;p&gt;The reason for this seamingly over-complicated behavior is that the
rubygems authors opted to support an alternative progression for beta
releases, as shown in the following table:&lt;/p&gt;

&lt;table style=&quot;font-size:smaller; line-height:1.1em&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Version&lt;/th&gt;
      &lt;th&gt;~&amp;gt; 1.1.0&lt;/th&gt;
      &lt;th&gt;~&amp;gt; 1.2.0.b&lt;/th&gt;
      &lt;th&gt;~&amp;gt; 1.2.0&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1.1.0&lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.1.1&lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.0.a.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.0.b.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.0.b.1&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.1&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
      &lt;td&gt;•&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.3.0.b.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.3.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;So for example, if you intend to put out a pre-release for what will
become a final release of &lt;code class=&quot;highlighter-rouge&quot;&gt;1.4.0&lt;/code&gt;, then you had better version it like
&lt;code class=&quot;highlighter-rouge&quot;&gt;1.4.0.rc.0&lt;/code&gt; (replace &lt;code class=&quot;highlighter-rouge&quot;&gt;rc&lt;/code&gt; as desired) and not &lt;code class=&quot;highlighter-rouge&quot;&gt;1.4.rc.0&lt;/code&gt;. As this is
rather obscure and confusing, several prominent projects have done it
incorrectly, for example, the &lt;code class=&quot;highlighter-rouge&quot;&gt;1.1.pre&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;1.1.rc&lt;/code&gt; series of
&lt;a href=&quot;https://rubygems.org/gems/bundler/versions&quot;&gt;bundler&lt;/a&gt;, and similar versions of &lt;a href=&quot;https://rubygems.org/gems/i18n/versions&quot;&gt;i18n&lt;/a&gt;, &lt;a href=&quot;https://rubygems.org/gems/pg/versions&quot;&gt;pg&lt;/a&gt;, and &lt;a href=&quot;https://rubygems.org/gems/mongo/versions&quot;&gt;mongo&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;maven&quot;&gt;Maven&lt;/h2&gt;

&lt;p&gt;If you’re not yet acquainted with my use of Java for performance and
Ruby for elegant brevity: I’m using Maven &lt;em&gt;only&lt;/em&gt; as a &lt;code class=&quot;highlighter-rouge&quot;&gt;javac&lt;/code&gt; with
dependencies tool and packing jars in gems. But in
&lt;a href=&quot;/2008/12/07/maven-must-die.html#gemjar-dependencies&quot;&gt;some cases&lt;/a&gt; there are combined java/ruby dependencies which
must be expressed in a compatible way between the two systems.&lt;/p&gt;

&lt;p&gt;In a similar fashion, &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; is accepted by Maven but
non-&lt;a href=&quot;http://www.sonatype.com/books/mvnref-book/reference/pom-relationships-sect-pom-syntax.html&quot;&gt;standard&lt;/a&gt; and has its own set of pitfalls. The most
significant of these is that Maven, according to documentation, orders
such versions as follows:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.0&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.1&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.2&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.a.1&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.2&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yet in practice Maven appears, at least in certain contexts to order as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.0&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.a.1&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.1&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.b.2&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.1&lt;/code&gt; &amp;lt; &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.2&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So don’t make the mistake of using versions like this in
Maven. Fortunately, a Maven version range specifier of
&lt;code class=&quot;highlighter-rouge&quot;&gt;[1.2,1.2.9999)&lt;/code&gt; filters out the pre-releases despite the above
ordering.  I will be moving on from this particular issue by making
&lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.1&lt;/code&gt; the next (non-pre) release of Iudex (which &lt;em&gt;appears&lt;/em&gt; to avoid
issues.)&lt;/p&gt;

&lt;p&gt;There is a somewhat comparable mapping of pre-release versions
available across the SemVer v2, Rubygems and Maven:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;SemVer 2&lt;/th&gt;
      &lt;th&gt;Rubygems&lt;/th&gt;
      &lt;th&gt;Maven&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1.2.0-b.2&lt;/td&gt;
      &lt;td&gt;1.2.0.b.2&lt;/td&gt;
      &lt;td&gt;1.2.0-b.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.3.0&lt;/td&gt;
      &lt;td&gt;1.3.0&lt;/td&gt;
      &lt;td&gt;1.3.0&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;However, &lt;code class=&quot;highlighter-rouge&quot;&gt;b.2&lt;/code&gt; is still compared lexicographical in Maven, so with
this scheme, you would need to avoid &lt;code class=&quot;highlighter-rouge&quot;&gt;b.10&lt;/code&gt;. Furthermore, range
specifiers aren’t particularly pre-release aware, so the Maven range
equivalent to Rubygems specifier &lt;code class=&quot;highlighter-rouge&quot;&gt;~&amp;gt; 1.2.0.b.2&lt;/code&gt; would presumably need
to be &lt;code class=&quot;highlighter-rouge&quot;&gt;[1.2.0-b.2, 1.2.999)&lt;/code&gt; though this isn’t documented and I
haven’t tested it.&lt;/p&gt;

&lt;p&gt;It may also be worth noting that &lt;a href=&quot;http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Transitive_Dependencies&quot;&gt;Maven dependency mediation&lt;/a&gt;
allows users to avoid version ranges while muddling the vary intent of
library authors in specifying compatible dependencies.&lt;/p&gt;

&lt;p&gt;Did you notice how relatively simple that &lt;code class=&quot;highlighter-rouge&quot;&gt;1.3.0&lt;/code&gt; version is looking
in the above table?&lt;/p&gt;

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

&lt;p&gt;Despite best intentions by spec. and tool authors, pre-release
notations and behaviors are obscure, often conflicting, and with many
potential pitfalls.&lt;/p&gt;

&lt;p&gt;Consider also that many pre-release progressions end with a production
release with no functional change from a prior candidate. Extra
release work to support an often arbitrary distinction.  Release
beauty is ultimately in the eye of the beholder. Perhaps in general,
and most certainly with this particular set of tools, we would be
better served to dispense with the notion that the highest major.minor
release number is necessarily the most stable. And as a corollary,
continue to release patches to the previous major.minor branch.&lt;/p&gt;

&lt;p&gt;The next Iūdex preview/candidate/beta release will be versioned:
&lt;code class=&quot;highlighter-rouge&quot;&gt;1.3.0&lt;/code&gt; and will be experimental by comparision to &lt;code class=&quot;highlighter-rouge&quot;&gt;1.2.2&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:aa&quot;&gt;
      &lt;p&gt;I’m not sure how this notion of &lt;em&gt;pre-release&lt;/em&gt; relates to
   test-driven development concepts, where any new feature would
   of course be sufficiently unit tested.&amp;nbsp;&lt;a href=&quot;#fnref:aa&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:ac&quot;&gt;
      &lt;p&gt;What locale you might ask? Greek?&amp;nbsp;&lt;a href=&quot;#fnref:ac&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Bundler for gem development</title>
   <link href="http://gravitext.com/2012/02/19/bundler-for-gem-development.html"/>
   <updated>2012-02-19T00:00:00-08:00</updated>
   <id>id:/2012/02/19/bundler-for-gem-development</id>
   <content type="html">&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;/h2&gt;

&lt;p&gt;We build a lot of gems, both public and private. In projects involving
5 or 10 different kinds of service daemons, it is useful for both
consistency and efficiency of install and updates to package each
daemon in its own gem. Now anything to be reused across multiple
services should also be packaged as a gem. Before you know it, you
have a lot of gems in play. For example, a sub-graph of &lt;a href=&quot;http://iudex.gravitext.com/&quot;&gt;Iūdex&lt;/a&gt;
project dependencies:&lt;/p&gt;

&lt;div class=&quot;svg-object&quot;&gt;
  &lt;object data=&quot;/svg/iudex.svg&quot; type=&quot;image/svg+xml&quot; width=&quot;&quot; height=&quot;&quot;&gt;
    &lt;img src=&quot;/svg/iudex.png&quot; width=&quot;&quot; height=&quot;&quot; /&gt;
  &lt;/object&gt;
&lt;/div&gt;

&lt;p&gt;Where several of these gems are actually dependencies of other
internal service gems. Some might assume this is a recipe for a speedy
descent into &lt;em&gt;dependency hell&lt;/em&gt;.  I would agree that some care in
dependency management is definitely required. For example, maintaining
these dependency visualizations, and avoiding loops or unnecessary
coupling.  What is the return on this effort?  Some advantages
include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;A patch release for a critical fix to a service daemon, or a single
shared gem used by many daemons, does not expand into the need to
release everything all at once. (The implication here is, yes, we
are using semantic versioning and allowing controlled patch updates
in production.)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Testing updates is similarly much less invasive to arrange in your
development environment.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Efficiency of upgrade over the network: Deploying an update does not
involve pushing monolithic 10’s of megabytes of statically linked
software from your development hosts to production. Instead rubygems
figures our what incremental gem updates are actually required,
dynamically.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above approach &lt;em&gt;is&lt;/em&gt; quite different from the prototypical Ruby on
Rails application, deployed direct from git, and using &lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt; to
bring in the gem dependencies, often vendored, or effectively,
statically linked. Bundler grew out of the need to overcome dependency
issues, which had as root cause the failure of many gem publishers to
practice semantic versioning, i.e. interface or behavioral changes in
patch releases breaking your application; or lack of awareness by
application developers on the need to lock down gem dependencies
against potential future breaking changes, ex: the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;= 0&lt;/code&gt; dependency
specification.&lt;/p&gt;

&lt;p&gt;But Bundler is also particularly useful for developing gems, and
managing dependencies in the development environment before release
and deployment. The &lt;a href=&quot;http://rjack.gravitext.com/tarpit&quot;&gt;TarPit&lt;/a&gt; 2 release fully integrates with Bundler.&lt;/p&gt;

&lt;h2 id=&quot;multiple-gem-projects&quot;&gt;Multiple Gem Projects&lt;/h2&gt;

&lt;p&gt;You won’t find much written on this as a project style or Bundler use
case.  &lt;a href=&quot;https://github.com/rails/rails&quot;&gt;Rails&lt;/a&gt; is however one high-profile example, where several
inter-dependent gems are contained in a single git-repo.  Notice the
the top-level Gemfile here which loads all of the internal
gemspecs. In such a project, consider the common development task of
making changes across multiple gems in parallel, for example: new
feature and tests in gem A, with usage and more testing in gem B.&lt;/p&gt;

&lt;p&gt;Before Bundler, you would need to &lt;code class=&quot;highlighter-rouge&quot;&gt;gem install A&lt;/code&gt; after each change,
for integration testing with gem B.  This added a bit of tedium as
well as potential for confusion on the state of your local gem
repo. (Did I install that change yet or not?)&lt;/p&gt;

&lt;p&gt;With Bundler, and in particular using a top-level common Gemfile,
Bundler now arranges for gem B’s tests to load gem A directly from the
git-repo itself. No install step is required, and the current state of
what is being tested is as clear as the current state of your git
development repo.&lt;/p&gt;

&lt;h3 id=&quot;warning-dependencies-may-not-be-as-close-as-they-appear&quot;&gt;Warning: dependencies may not be as close as they appear&lt;/h3&gt;

&lt;p&gt;Bundler currently has a shortcoming in support for multi-gem projects:
gemspec dependency omissions might not be caught when using a
top-level Gemfile. See issue &lt;a href=&quot;https://github.com/carlhuda/bundler/issues/1668&quot;&gt;1668&lt;/a&gt; for full details.  For this
reason, in projects of this form I suggest occasionally testing with
per-gem Gemfile’s, for example prior to release or when making gemspec
changes.  Here is one way to arrange for that without needing to
maintain some sort of parallel git branch with a separate set of
gemfiles:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Generate per-gem Gemfiles and jbundle install each&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:generate_gemfile_per_gem&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gname&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=== Gemfile: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; ===&quot;&lt;/span&gt;

      &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Gemfile'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'w'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RUBY&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
# -*- ruby -*-
source :rubygems
gemspec :path =&amp;gt; '.', :name =&amp;gt; '&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;'
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RUBY&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jbundle install --path &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'HOME'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/.gem --local&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that you likely will want to &lt;code class=&quot;highlighter-rouge&quot;&gt;jrake install&lt;/code&gt; all gems to your
local repo &lt;em&gt;before&lt;/em&gt; using &lt;code class=&quot;highlighter-rouge&quot;&gt;jrake generate_gemfile_per_gem&lt;/code&gt; and then
&lt;code class=&quot;highlighter-rouge&quot;&gt;jrake test&lt;/code&gt; to validate your gemspecs.&lt;/p&gt;

&lt;h2 id=&quot;jbexec&quot;&gt;jbexec&lt;/h2&gt;

&lt;p&gt;Gem project bin (or init) scripts shouldn’t require Bundler, as that
would make Bundler a production dependency. So for ad hoc testing in
development you either need to &lt;code class=&quot;highlighter-rouge&quot;&gt;jgem&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;jrake install&lt;/code&gt; everything as
was done pre-Bundler, or get the bundle activated through some other
means. The &lt;code class=&quot;highlighter-rouge&quot;&gt;jbundle exec&lt;/code&gt; as provided by Bundler ends up booting the
full bundle environment twice, once in an additional process. This is
particularly slow on jruby.&lt;/p&gt;

&lt;p&gt;As an alternative, the following little &lt;code class=&quot;highlighter-rouge&quot;&gt;jbexec&lt;/code&gt; script placed in
your bin directory is much faster and appears adequate for this use
case:&lt;/p&gt;

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

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rubygems'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'bundler/setup'&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;shift&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Iyyov</title>
   <link href="http://gravitext.com/2010/03/13/Iyyov.html"/>
   <updated>2010-03-13T00:00:00-08:00</updated>
   <id>id:/2010/03/13/Iyyov</id>
   <content type="html">&lt;p&gt;Toward the goal of fully self-contained java/ruby service gems and ruby &lt;a href=&quot;/2008/11/09/scripted-server-setup.html&quot;&gt;scripted setup&lt;/a&gt;, some issues have lingered. With the recent releases of &lt;a href=&quot;http://hashdot.sourceforge.net/&quot;&gt;Hashdot&lt;/a&gt; 1.4 and the new &lt;a href=&quot;http://github.com/dekellum/iyyov#readme&quot;&gt;Iyyov&lt;/a&gt; monitor, a complete solution is now available. Here is what it looks like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Package and deploy your service as a versioned rubygem.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Include a version-specific service init script written in ruby and annotated will all necessary JVM/jruby settings (i.e -server -Xmx2g, etc.)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://hashdot.sourceforge.net/&quot;&gt;Hashdot&lt;/a&gt; will reliably bootstrap your service as a fully UNIX compliant fork’d, setsid, and IO redirected daemon with an instance exclusive PID file and recognizable process name.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://github.com/dekellum/iyyov#readme&quot;&gt;Iyyov&lt;/a&gt; will start your service on system boot, keep it running, and automatically restart on config changes or gem upgrades.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;No external bash scripts, user environment settings, or other cruft required.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;packaging-your-service-gem&quot;&gt;Packaging your Service Gem&lt;/h2&gt;

&lt;p&gt;MRI rubygem authors would expect to place a daemon startup script in the gem’s bin directory. On install, rubygems will create a wrapper script for the original that arranges for gem initializing, makes the script available to the user PATH, and provides a convenient version selection mechanism. This is a great mechanism for short lived command-line interaction with gems, but has one very troubling shortcoming for a JRuby service writer:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There is no obvious mechanism to reliably control JVM or jruby settings on a daemon or
version specific basis and package these settings within a gem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jruby defaults to the -client JVM, 500Mb of heap, and no specific GC tunings. This is reasonable default behavior, but most any production-grade service will want and need a mechanism to override these defaults.&lt;/p&gt;

&lt;p&gt;To achieve this we need to package a non-wrapped executable script. Here is a sample from &lt;a href=&quot;http://github.com/dekellum/hashdot-test-daemon&quot;&gt;hashdot-test-daemon&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env jruby&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#-*- ruby -*-&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#. hashdot.profile         += daemon&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#. hashdot.pid_file         = ./hashdot-test-daemon.pid&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#. hashdot.io_redirect.file = ./hashdot-test-daemon.log&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#. hashdot.vm.options += -Xmx64m&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#. hashdot.vm.options += -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rubygems'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hashdot-test-daemon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;= 1.1.0&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rjack-logback'&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;RJack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config_console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:full&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:thread&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'hashdot-test-daemon'&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Hashdot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Daemon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that if a system level &lt;a href=&quot;http://hashdot.sourceforge.net/&quot;&gt;Hashdot&lt;/a&gt; dependency gives you concern, you could achieve an approximately similar and perhaps sufficient result with a more elaborate shell script and/or other daemonizing options less complete for JRuby.&lt;/p&gt;

&lt;p&gt;In any case, we want to be able to launch the correct service version by referencing the full path to the script in the local gem installation. For example if the gems are installed to /opt/jruby/gems, then the following should launch the 1.1.0 version of service in the background with any 1.1.0 specific settings:&lt;/p&gt;

&lt;pre&gt;
/opt/jruby/gems/gems/hashdot-test-daemon-1.1.0-java/init/hashdot-test-daemon
&lt;/pre&gt;

&lt;h2 id=&quot;service-runtime-layout&quot;&gt;Service runtime layout&lt;/h2&gt;

&lt;p&gt;Given java’s native threads its unlikely that it will be necessary to run a large number of separate daemon instances.  However there is still plenty of reasons to want to run different services on the same host or different configurations or versions of the same service.  Toward this end I use a separate directory for each service, and relative path references to instance specific configuration, log, and PID files, i.e:&lt;/p&gt;

&lt;pre&gt;
/opt/var/hashdot-test-daemon-1
├── hashdot-test-daemon.log
├── hashdot-test-daemon.log.1.gz
├── hashdot-test-daemon.pid
└── config.rb
/opt/var/service-test-daemon-2
└── ...
&lt;/pre&gt;

&lt;p&gt;A fully expanded shell based launch of the service might look like:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /opt/var/hashdot-test-daemon-1 &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; /opt/jruby/gems/gems/hashdot-test-daemon-1.1.0-java/init/hashdot-test-daemon &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; config.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And in fact, one approach to boot-time startup or restart would be to encode something like this in an init.d script, inittab, upstart, or crontab. The last three can achieve automatic restart, provided that the service supports reliable instance-specific mutual exclusion (via PID file, etc.) Hashdot supports this reliably.&lt;/p&gt;

&lt;h2 id=&quot;iyyov&quot;&gt;Iyyov&lt;/h2&gt;

&lt;p&gt;Once I arrived at the above solution for system layout and packaging service startup details in the gem, I went looking for a process monitor solution that would handle restarts and I could customize to keep the setup simple.  I first looked at &lt;a href=&quot;http://mmonit.com/monit/&quot;&gt;Monit&lt;/a&gt;, and while I was able to get it to work with these daemons in basic form, configuration was “involved”, and customization impractical.&lt;/p&gt;

&lt;p&gt;Next I tried &lt;a href=&quot;http://god.rubyforge.org/&quot;&gt;God&lt;/a&gt;. While God’s integration with kernel events makes it all powerful, and its ruby based configuration is awesome, it is also a bit of a liability to install.  Furthermore, God doesn’t run on JRuby, due to a combination of heavy native non-FFI extension, and reliance on low level ruby API’s not yet completely supported.  In short, I found &lt;a href=&quot;http://god.rubyforge.org/&quot;&gt;God&lt;/a&gt; to be a bit &lt;em&gt;more&lt;/em&gt; than I needed.  So instead I began work on and created &lt;a href=&quot;http://github.com/dekellum/iyyov#readme&quot;&gt;Iyyov&lt;/a&gt;’s configuration in &lt;a href=&quot;http://god.rubyforge.org/&quot;&gt;God&lt;/a&gt;’s image.  See the &lt;a href=&quot;http://github.com/dekellum/iyyov#readme&quot;&gt;Iyyov&lt;/a&gt; README for a more detailed feature set and examples.  The hashdot-test-daemon in the above example can be configured and launched like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Iyyov&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define_daemon&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hashdot-test-daemon&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 1.0&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log_rotate&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which highlights several Iyyov features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;By default, Iyyov uses the rubygems API to find the highest matching gem version and init script with the same name. Thus in the common case, configuration is absolutely DRY.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Built in log rotation/compression that meshes well with Hashdot’s IO redirection and SIGHUP log reopening support. The end result is a complete logging solution that also redirects the often neglected JVM level output in the event of a JVM crash, uncaught exception, or ‘kill -QUIT’ arranged stack dump.  The external Linux logrotate utility could also be used here.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Iyyov is also a practical cron replacement supporting fixed or periodic time scheduling of supporting tasks. For example:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Iyyov&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Backup service state at 1am local time each weekday&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Run :async, in a &quot;background&quot; thread, since this might take a while.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;schedule_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;backup&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                 &lt;span class=&quot;ss&quot;&gt;:fixed_times&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'1:00'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:fixed_days&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rsync ...&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As a final example, since the configuration is fully scriptable ruby, we might select which service to run by hostname in a single maintained configuration file:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Iyyov&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`hostname -s`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^server-[012]$/&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define_daemon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;widget-factory&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'server-3'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define_daemon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;assembly-line&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;gem-maintenance&quot;&gt;Gem maintenance&lt;/h2&gt;

&lt;p&gt;Because the above described per-gem init script isn’t wrapped by rubygems, the corresponding gem version should be hard coded within to guarantee the resolution of this and other gem dependencies. This presents a maintenance problem which can be solved through various means. My solution is the &lt;a href=&quot;http://rjack.gravitext.com/tarpit&quot;&gt;Tarpit&lt;/a&gt; 1.2 test_line_match feature that may be installed in the gem project’s Rakefile, i.e:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:check_init_version&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;test_line_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'init/hashdot-test-daemon'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;sr&quot;&gt;/^gem.+hashdot-test-daemon/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/= &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A simple error message is generated when attempting to build the gem without this version being set properly. My recent gems use this feature extensively and I think its less net maintenance then the other option I considered: generating such files with ERB templates.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>RJack Updates via Gemcutter</title>
   <link href="http://gravitext.com/2009/12/19/rjack-updates-via-gemcutter.html"/>
   <updated>2009-12-19T00:00:00-08:00</updated>
   <id>id:/2009/12/19/rjack-updates-via-gemcutter</id>
   <content type="html">&lt;p&gt;Another batch of &lt;a href=&quot;http://rjack.gravitext.com/&quot;&gt;RJack&lt;/a&gt; updates has been accumulating, but was
blocked behind figuring out what has changed with &lt;a href=&quot;http://gems.rubyforge.org/&quot;&gt;Gemcutter&lt;/a&gt; and gem
publishing to the canonical repo. Gemcutter appears to be good forward
progress in modernizing gem distribution.  While the
&lt;a href=&quot;http://update.gemcutter.org/&quot;&gt;blog&lt;/a&gt; posts and FAQ
provide clues, it would help more if there was some canonical status
page (not a twitter timeline) that provides a clear summary of the
current state of accounts, publishing routes, and gem repo behavior.
Somewhere I thought I read that mirroring of the old rubyforge project
gem file hosting has stopped, and a look at
&lt;a href=&quot;http://rubyforge.org&quot;&gt;rubyforge.org&lt;/a&gt; shows the last published gem
recorded there was on 2009-12-10.  So without risking an old style
rubyforge release (Hoe’s :release task) with unknown results, I
decided it was time to start using “gem push”.&lt;/p&gt;

&lt;p&gt;The previous introduction of &lt;a href=&quot;http://rjack.gravitext.com/tarpit/History_rdoc.html&quot;&gt;rjack-tarpit&lt;/a&gt; made the changes for rjack
much simpler. I first released a new rjack-tarpit-1.1.0 which provides
new “push” and “install” tasks as shown below:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Define gem push and install tasks&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;define_gem_tasks&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;gem push (gemcutter)&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:push&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:gem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rubygems'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CommandManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'push'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'-V'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pkg/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.gem&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;gem install (default install dir)&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:install&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:gem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rubygems'&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CommandManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'install'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'--no-ri'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'-V'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pkg/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.gem&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(I hope CommandManager::run is a “supported” approach to running gem
commands in process. This has a speed advantage under jruby by
avoiding a separate interpreter start-up.)&lt;/p&gt;

&lt;p&gt;Using the new gemcutter based :push task I was able to release all of
these accumulated updates:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/slf4j/History_rdoc.html&quot;&gt;rjack-slf4j-1-5.10.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/logback/History_rdoc.html&quot;&gt;rjack-logback-0.9.18.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/httpclient-4/History_rdoc.html&quot;&gt;rjack-httpclient-4-4.0.1.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/jetty/History_rdoc.html&quot;&gt;rjack-jetty-6.1.22.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/jetty-jsp/History_rdoc.html&quot;&gt;rjack-jetty-jsp-6.1.22.2.1.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/commons-dbutils/History_rdoc.html&quot;&gt;rjack-commons-dbutils-1.3.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/commons-pool/History_rdoc.html&quot;&gt;rjack-commons-pool-1.5.4.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This eliminates one Hoe dependency, and with tarpit encapsulating
these concerns, this change feels like a step in the direction of
removing the Hoe dependency (and the quirky Manifest handling) all
together. I like my implementation better than the recently released &lt;a href=&quot;http://seattlerb.rubyforge.org/hoe/Hoe/Gemcutter.html#M000012&quot;&gt;Hoe
2.4 Gemcutter plugin&lt;/a&gt;.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Jekyll</title>
   <link href="http://gravitext.com/2009/11/04/jekyll.html"/>
   <updated>2009-11-04T00:00:00-08:00</updated>
   <id>id:/2009/11/04/jekyll</id>
   <content type="html">&lt;p&gt;A new post for a newly revamped &lt;a href=&quot;/#vblog&quot;&gt;void * blog&lt;/a&gt; and a new personal home page constructed with &lt;a href=&quot;http://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt;, a ruby based static site generator. In relation to my needs, Jekyll offers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;A back-to-basics approach of static site generation from “source” as HTML templates, CSS, and markdown, all easily maintained by me in git and emacs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Supports blog-style dated posts with excerpts and template driven atom feed generation.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Usable as a simple but complete and powerful CMS, not just blog posts. I want this for both maintaining a home page for myself, but also for developing technical site docs (usage, design, reference, etc.) for various projects.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Supports inline code sample syntax highlighting, something that was lacking in the previous blog.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, the generated site remains on my home server and is served via Nginx. But I could easily push it elsewhere, for example, S3; as it requires no dynamic server component.&lt;/p&gt;

&lt;p&gt;A missing feature vs. my previous blog is comment support, which has traditionally required a dynamic server, means of storage, spam detection, and many other complications.  But java script embeddable comment &lt;a href=&quot;http://www.knowliz.com/2009/06/disqus-intensedebate-js-kit-analysis.html&quot;&gt;services&lt;/a&gt; like DISQUS are now winning conversions even from mainstream blog platforms based on superior features and identity integration. Its on my list to integrate one of these.  Its both amusing and convenient to have Web 2.0 handling the last mile, and allowing me to get back to basics with a &lt;em&gt;Web 0.1&lt;/em&gt; static site generator.&lt;/p&gt;

&lt;p&gt;I’ll end the post with an example, the liquid template used to produce this blog’s atom feed:&lt;/p&gt;

&lt;!-- ZWSP U+200B added bellow to avoid interpreting nested liquid tags --&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;feed&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2005/Atom&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

 &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;void * blog&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://gravitext.com/atom.xml&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://gravitext.com/&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;updated&amp;gt;&lt;/span&gt;{​{ site.time | date_to_xmlschema }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/updated&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;http://gravitext.com/&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;author&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;David Kellum&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;email&amp;gt;&lt;/span&gt;dek-blog@gravitext.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/email&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/author&amp;gt;&lt;/span&gt;

 {​% for post in site.posts %}
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;entry&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{​{ post.title }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://gravitext.com{​{ post.url }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;updated&amp;gt;&lt;/span&gt;{​{ post.date | date_to_xmlschema }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/updated&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;id:{​{ post.id }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;content&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;{​{ post.content | xml_escape }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/content&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/entry&amp;gt;&lt;/span&gt;
 {​% endfor %}

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/feed&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>RJack Updates</title>
   <link href="http://gravitext.com/2009/10/24/rjack-updates.html"/>
   <updated>2009-10-24T00:00:00-07:00</updated>
   <id>id:/2009/10/24/rjack-updates</id>
   <content type="html">&lt;p&gt;This week’s addition to the growing set of rjack gems is
&lt;a href=&quot;http://rjack.gravitext.com/rome/&quot;&gt;rjack-rome-1.0.0&lt;/a&gt; based on the ROME RSS/Atom feed reader/generator.&lt;/p&gt;

&lt;p&gt;A new SVG intra-gem dependency diagram, built using the Graphviz Dot
tool, was also added to the rjack project home page. The gems are
linked to the RDoc:&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;
  &lt;object data=&quot;http://rjack.gravitext.com/rjack.svg&quot; type=&quot;image/svg+xml&quot; width=&quot;711&quot; height=&quot;267&quot;&gt;
    &lt;img src=&quot;http://rjack.gravitext.com/rjack.png&quot; width=&quot;711&quot; height=&quot;267&quot; /&gt;
  &lt;/object&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>RJack Updates</title>
   <link href="http://gravitext.com/2009/10/18/rjack-updates.html"/>
   <updated>2009-10-18T00:00:00-07:00</updated>
   <id>id:/2009/10/18/rjack-updates</id>
   <content type="html">&lt;p&gt;The &lt;a href=&quot;http://rjack.gravitext.com/&quot;&gt;RJack&lt;/a&gt; project home page has been further improved. New and
updated gems are detailed below.&lt;/p&gt;

&lt;h2 id=&quot;new&quot;&gt;New&lt;/h2&gt;

&lt;p&gt;The following new gems have been released after consultations with the
upstream project owners:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/jdom/&quot;&gt;rjack-jdom-1.1.0.0&lt;/a&gt; (JDOM XML processor)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/jets3t/&quot;&gt;rjack-jets3t-0.7.1.0&lt;/a&gt; (Amazon S3/CloudFront client)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rjack-jets3t gem in particular is demonstrating returns on the gem
based dependency management approach of rjack. This new gems depends
on rjack-httpclient-3, rjack-commons-codec, and rjack-slf4j.&lt;/p&gt;

&lt;p&gt;With these additions, rjack is up to 31 third-party jars packaged in
13 gems. With the recently released rjack-tarpit making it much easier
to setup and maintain these gems, I am much more inclined to push all
Java third-party jars into rjack gems rather than packing them
directly in the consuming projects.  This makes the consuming projects
easier to maintain and distribute, and avoids version conflicts.&lt;/p&gt;

&lt;p&gt;Next up for inclusion is an rjack-rome packaging of the ROME feed
parser (which will also dependent on rjack-jdom.)&lt;/p&gt;

&lt;h2 id=&quot;updated&quot;&gt;Updated&lt;/h2&gt;

&lt;p&gt;Additional gem updates since the last RJack post include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/tarpit/History_rdoc.html&quot;&gt;rjack-tarpit-1.0.1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/jetty/History_rdoc.html&quot;&gt;rjack-jetty-6.1.21.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/jetty-jsp/History_rdoc.html&quot;&gt;rjack-jetty-6.1.21.2.1.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>TarPit</title>
   <link href="http://gravitext.com/2009/10/11/tarpit.html"/>
   <updated>2009-10-11T00:00:00-07:00</updated>
   <id>id:/2009/10/11/tarpit</id>
   <content type="html">&lt;p&gt;Admission: I was a bit rash to have declared &lt;a href=&quot;/2008/12/07/maven-must-die.html&quot;&gt;Jihad on Maven&lt;/a&gt;
back in December.  (Is it ever wise to write “Jihad” on your blog?)  Now 10 months later I’m still using Maven to build java and manage cross-java dependencies at build time. In the interest of peace and reconciliation let me say that Maven has really been performing amiably in its now reduced responsibility of invoking javac with a proper classpath, and packaging jars from the results.  While this stalemate still seems less than ideal, I think I might continue to be able to get along with Maven, provided I don’t need to write any “plugins” for it, or wrestle with it for any less well trodden tasks.&lt;/p&gt;

&lt;p&gt;Besides the war mongering, the original post does manage to outline the goals and associated set of issues around building gems for JRuby with java jars included.  I’ve had good success with this strategy for releasing componentized systems to both dedicated colo and Amazon EC2 production environments in the last year. Enough so that investing some more time in build automation has become warranted. This comes in the form of a new Rake helper gem called &lt;a href=&quot;http://rjack.gravitext.com/tarpit&quot;&gt;TarPit&lt;/a&gt;, released as part of the &lt;a href=&quot;http://rjack.gravitext.com/&quot;&gt;RJack&lt;/a&gt; project. What was a very wet and copied around jars-in-gem rakefile recipe, can now be expressed in a short and declarative style. For example, the rjack-logback &lt;a href=&quot;http://github.com/dekellum/rjack/blob/master/logback/Rakefile&quot;&gt;Rakefile&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rjack-tarpit'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RJack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TarPit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rjack-logback'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RJack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;specify&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;developer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;David Kellum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dek-oss@gravitext.com&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extra_deps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rjack-slf4j'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 1.5.8'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rubyforge_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rjack&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remote_rdoc_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;logback&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w{ core classic access }&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;logback-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RJack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Logback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;LOGBACK_VERSION&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.jar&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Manifest.txt'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;lib/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/base.rb&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;assembly_version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;define_tasks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;how-it-all-works&quot;&gt;How it all works&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;t.specify()&lt;/code&gt; call wraps &lt;code class=&quot;highlighter-rouge&quot;&gt;Hoe.spec()&lt;/code&gt; (&lt;a href=&quot;http://seattlerb.rubyforge.org/hoe/&quot;&gt;Hoe&lt;/a&gt; 2.x) where &lt;code class=&quot;highlighter-rouge&quot;&gt;h&lt;/code&gt; is the hoe object.  All of the normal Hoe.spec options are still available, but TarPit importantly gains control of when in the Rake setup process &lt;code class=&quot;highlighter-rouge&quot;&gt;Hoe.spec()&lt;/code&gt; is actually called (hint: last in define_tasks, once any needed Manifest.txt update has already been performed.)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;There are some strategy options on the initial &lt;code class=&quot;highlighter-rouge&quot;&gt;TarPit.new&lt;/code&gt; call, including: &lt;code class=&quot;highlighter-rouge&quot;&gt;:jars_from_assembly&lt;/code&gt; (where only Maven knows the versioned jars to be included) and &lt;code class=&quot;highlighter-rouge&quot;&gt;:no_assembly&lt;/code&gt; (when you are only including an internal jar built from source).  In the &lt;code class=&quot;highlighter-rouge&quot;&gt;:jars_from_assembly&lt;/code&gt; case, given that Hoe still insists on a Manifest.txt complete on disk at start of Rake setup, you still need to manually run &lt;code class=&quot;highlighter-rouge&quot;&gt;jrake manifest&lt;/code&gt; as needed.  If instead you can specify the jars, for example, as above from a ruby version number, then Manifest.txt will be built as needed and before any dependent hoe tasks.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In either case TarPit will setup Rake to run “mvn package” as a dependency whenever any java source files (src/**), pom.xml, or assembly.xml have been updated.  The Maven produced jars, including “assembled” external jars are symlinked from the normal Maven target location to your lib/gemname/ directory.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>RJack Expanded</title>
   <link href="http://gravitext.com/2009/10/10/rjack-expanded.html"/>
   <updated>2009-10-10T00:00:00-07:00</updated>
   <id>id:/2009/10/10/rjack-expanded</id>
   <content type="html">&lt;p&gt;The &lt;a href=&quot;http://rjack.gravitext.com/&quot;&gt;RJack&lt;/a&gt; project has expanded its mission in providing common open-source java components as gems for use with JRuby.&lt;/p&gt;

&lt;h2 id=&quot;name-changes&quot;&gt;Name Changes&lt;/h2&gt;

&lt;p&gt;When I started RJack with the slf4j and logback gems, I figured I was pretty safe in using these names verbatim, since the gems were a very minimal and direct packaging of the same named upstream projects. Same with the jetty inclusion. Over time however I found potential inclusions colliding with existing ruby gems, for example: hc-httpclient was so named because httpclient, not surprisingly, was not available. With the later additions a certain &lt;em&gt;rjack style&lt;/em&gt; also started to jell:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;SLF4J is the logging interface: every included rjack gem will use it to resolve java logging dependencies where found, including via legacy interfaces like commons logging, etc.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;RJack will strive to expose every aspect of component setup, which might be referred to as “configuration” in the upstream java project, as easy to use and extend ruby classes, instead of less flexible XML, YAML, or the like.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;When appropriate and useful, RJack gems will include ruby adapters to make these components more amendable to direct use from ruby code.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given these stylistic choices, and in order to better consolidate the effort and prepare for further expansion, I’ve decided at this point to release new versions of the existing gems under a “rjack-“ gem prefix name and ::RJack ruby module. The following table shows old and new names and the current release version. New rjack-gems are also shown.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Old Name&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;New Name&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Updated Version&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;slf4j&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/slf4j/History_rdoc.html&quot;&gt;rjack-slf4j&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1.5.8.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;logback&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/logback/History_rdoc.html&quot;&gt;rjack-logback&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0.9.17.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;hc-httpclient&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/httpclient-3/History_rdoc.html&quot;&gt;rjack-httpclient-3&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3.1.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/httpclient-4/History_rdoc.html&quot;&gt;rjack-httpclient-4&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4.0.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;jetty&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/jetty/History_rdoc.html&quot;&gt;rjack-jetty&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;6.1.20.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;jetty-jsp&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/jetty-jsp/History_rdoc.html&quot;&gt;rjack-jetty-jsp&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;6.1.20.2.1.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/commons-codec/History_rdoc.html&quot;&gt;rjack-commons-codec&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1.4.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/commons-dbcp/History_rdoc.html&quot;&gt;rjack-commons-dbcp&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1.2.2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/commons-dbutils/History_rdoc.html&quot;&gt;rjack-commons-dbutils&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1.2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://rjack.gravitext.com/commons-pool/History_rdoc.html&quot;&gt;rjack-commons-pool&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1.5.3.0&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The rjack-httpclient-[3,4] gems were a special consideration. I maintain continuity between the two versions, and the ruby setup interfaces are compatible, but require’ing both versions in the same process is permissible (like the associated Java code).  This supports incremental transition to 4.x, a reality given lack of backward compatibility in the java interface and lack of significant pressure to migrate from 3.x.&lt;/p&gt;

&lt;p&gt;In my other projects that use these gems, I’ve found converting to the new names a pretty easy grep and replace operation. Since they are also some cross dependencies between these gems, I recommend transition en masse.  The gems with the old names will remain in the RubyForge gem repo, and significant bug fixes can be back ported if needed.&lt;/p&gt;

&lt;h2 id=&quot;new-features&quot;&gt;New Features&lt;/h2&gt;

&lt;p&gt;Finally, there are some other new features included:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The SLF4J gem now has better support for both ruby and java exceptions logged from ruby, and in mapping Ruby Module::Class names into java/SLF4J Logger dot notation.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Mapped Diagnostic Content&lt;/em&gt; support in SLF4J and Logback&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A new reusable jar-in-gem build tool, &lt;a href=&quot;http://rjack.gravitext.com/tarpit/&quot;&gt;rjack-tarpit&lt;/a&gt; the subject of a future post.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>Insidious Whitespace</title>
   <link href="http://gravitext.com/2009/06/28/insidious-whitespace.html"/>
   <updated>2009-06-28T00:00:00-07:00</updated>
   <id>id:/2009/06/28/insidious-whitespace</id>
   <content type="html">&lt;p&gt;Over the past few months I’ve converted just about every active project of mine from Subversion to Git. This now includes the Evri development projects, as well as my open source projects, and even home linux server code (i.e. the ruby script that runs this blog.)&lt;/p&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;I was originally drawn to git based on its merits in supporting free forking of open source projects. Publishing several of my personal open source projects to github has cemented my appreciation. That said, as has been discussed broadly, the learning curve of git is quite steep. With this kind of power must come some complexity.  Its relatively simple to push your sole project contributions to a github repo, but the first time I offered commits and a pull request off a fork of &lt;a href=&quot;http://github.com/dekellum/activerecord-jdbc-adapter/&quot;&gt;activerecord-jdbc-adapter&lt;/a&gt; I was a bit embarrassed to find that I really didn’t yet know what I was doing.  That was a good lesson for me, perhaps learned the only practical way, and since then I’ve put a little extra git education time into just about every project. Milestones crossed:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’m no longer terrified by the unknown that was “git rebase”&lt;/li&gt;
  &lt;li&gt;I’m only mildly leery of “git svn.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes I will make my history look the way it should: nice independent linear patches, forward-only merges when possible, etc.  And I think this is slowly starting to pay dividends in my development process. I’ve re-enabled some development tactics that I had previously decided, over the years of using SVN, CVS, and those before it, just weren’t worth the trouble.  And I’ve learned some new tactics.&lt;/p&gt;

&lt;p&gt;My mode of operation with Subversion had been to avoid branches and merging at all costs:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Don’t check that in until I tag the next release!&lt;/li&gt;
  &lt;li&gt;Lets split this code base up so we can work better in parallel!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While there remains merit in the last approach (appropriate modular decomposition), it shouldn’t be driven by limitations of a tool, and besides I never figured out exactly where to put those “trunk” directories.&lt;/p&gt;

&lt;h2 id=&quot;making-the-tools-work&quot;&gt;Making the tools work&lt;/h2&gt;

&lt;p&gt;What parts of my development tool set need to change with git?&lt;/p&gt;

&lt;p&gt;Maven might have been an issue (isn’t it always), but my recent conversion to java/ruby gems
packaging has the convenient side effect of liberating me from the maven release plugin (though I’m told there is workable maven/git integration).  I use rake for that now, and a simple “rake tag” task covers my git interaction.&lt;/p&gt;

&lt;p&gt;I’m still inclined to use Eclipse, while its not ideal, as a java code editor, primarily for the refactoring support that java seems to need, as a junit test launcher, and occasional debugger. The egit plugin for eclipse works well for basic operations, but the command line is still my main interface to git.&lt;/p&gt;

&lt;p&gt;But with merging in play, I really want my commits to be recognizable as clean atomic patch sets. An important aspect of this is not cluttering up those patches with arbitrary white noise in the form of randomly changing whitespace. With emacs, there is whitespace-mode and surrounding commands. Problem solved for all files accept java, but Eclipse is just not that sophisticated as a text editor.&lt;/p&gt;

&lt;h2 id=&quot;cleanws-script&quot;&gt;cleanws script&lt;/h2&gt;

&lt;p&gt;So I worked up a new tool. Its a simple text file whitespace filter, but I’ve also added some nice git integration and color coding to make it reasonably convenient. Ruby BTW is quite nice for this kind of task. Before doing a “git add”, I run “cleanws -g” to ensure good canonical whitespace in new/updated files. See the following example:&lt;/p&gt;

&lt;div class=&quot;image&quot;&gt;
  &lt;img src=&quot;/images/cleanws_sample_3.png&quot; height=&quot;352&quot; width=&quot;555&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;The second call fixes the file and (-w)rites it to disk. Any detected evil tab characters are left in place to be manually resolved.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href=&quot;/share/cleanws&quot;&gt;full script&lt;/a&gt;. Perhaps it will find its way into a gem and github it there is interest or if I continue to expand on it. Enjoy, and please keep it clean out there.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Hashdot 1.3 Released</title>
   <link href="http://gravitext.com/2009/03/09/hashdot-1.3.html"/>
   <updated>2009-03-09T00:00:00-07:00</updated>
   <id>id:/2009/03/09/hashdot-1.3</id>
   <content type="html">&lt;p&gt;Hashdot 1.3 has been released on SourceForge.&lt;/p&gt;

&lt;h2 id=&quot;13-2009-3-9&quot;&gt;1.3 (2009-3-9)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Added hashdot.user.home property to current real user home directory.&lt;/li&gt;
  &lt;li&gt;Added hashdot.header.comment property in support of languages not
using ‘#’ as a line comment. The groovy.hdp and rhino.hdp profiles
now set this property to “//”. Associated examples demonstrate
alternative header formats.&lt;/li&gt;
  &lt;li&gt;Added hashdot.script.dir. This and hashdot.script properties are now
always absolute paths. Clarified use of java.class.path.&lt;/li&gt;
  &lt;li&gt;Added clojure (clj) profile and example. [Phil Hagelberg]&lt;/li&gt;
  &lt;li&gt;Refined examples, jruby is only default INSTALL_SYMLINK.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Hashdot 1.2 Released</title>
   <link href="http://gravitext.com/2008/12/22/hashdot-1.2-released.html"/>
   <updated>2008-12-22T00:00:00-08:00</updated>
   <id>id:/2008/12/22/hashdot-1.2-released</id>
   <content type="html">&lt;p&gt;Hashdot 1.2 has been released on SourceForge.  Special thanks to Matt Sanford for contributing the initial Mac OS X port and helping me through some additional OS X idiosyncrasies. Details in the change log below:&lt;/p&gt;

&lt;h2 id=&quot;12-2008-12-22&quot;&gt;1.2 (2008-12-22)&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Added HUP signal handler to reopen STDOUT/STDERR when both daemonize and io_redirect are used&lt;/li&gt;
  &lt;li&gt;Added support for hashdot.chdir&lt;/li&gt;
  &lt;li&gt;Ported to Mac OS X. See default.hdp for differences in profile configuration. Note on Mac, the final test/test_cmdline.rb currently fails due to lack of a suitable equivalent to the Linux prctl() for process rename.  However, this is not strictly required for useful operation.&lt;/li&gt;
  &lt;li&gt;Simplified default.hdp and shortlived.hdp using new hashdot.vm.lib delayed expansion property.&lt;/li&gt;
  &lt;li&gt;Added explicit int casts to avoid x64 warnings&lt;/li&gt;
  &lt;li&gt;Improved INSTALL notes.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Maven Must Die</title>
   <link href="http://gravitext.com/2008/12/07/maven-must-die.html"/>
   <updated>2008-12-07T00:00:00-08:00</updated>
   <id>id:/2008/12/07/maven-must-die</id>
   <content type="html">&lt;p&gt;The RubyGems packaging system has several advantages for java-hosted components.  It goes places that Maven won’t go, like all the way into production and managing non-trivial executables from the command line or inittab. In my &lt;a href=&quot;/2008/11/06/rjack-updates.html&quot;&gt;first post on rjack&lt;/a&gt; I make some further case for this approach and describe some considerations in packaging ruby/java components as gems. In this post I’ll describe some of the complexities with building such gems, and workarounds to common problems.&lt;/p&gt;

&lt;h2 id=&quot;gems&quot;&gt;Gems with jars&lt;/h2&gt;

&lt;p&gt;The rjack project is best understood with an install of jruby, optimally hashdot, and for example, the jetty gem:&lt;/p&gt;

&lt;pre&gt;
% jgem install jetty
% jetty-service
&lt;/pre&gt;

&lt;p&gt;This will afford the opportunity to look at how the gem is built, as the full source including the build mechanism is included. The top two levels of the gem directory structure are shown below:&lt;/p&gt;

&lt;pre&gt;
/opt/jruby/gems/gems/jetty-6.1.14.1/
|-- History.txt
|-- Manifest.txt
|-- README.txt
|-- Rakefile
|-- assembly.xml
|-- bin/
|   `-- jetty-service*
|-- lib/
|   |-- jetty/
|   `-- jetty.rb
|-- pom.xml
|-- src/
|   `-- main/
|-- test/
|   `-- test_jetty.rb
`-- webapps/
    |-- test/
    `-- test.war
&lt;/pre&gt;

&lt;p&gt;Note the presence of both a Rakefile, and the Maven pom.xml and assembly.xml. Other projects that produce gems including jars manage the jar files as if they were source, but this becomes a maintenance liability in any of the following scenarios:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Included jar(s) are undergoing frequent updates and the gem should be kept current.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Included jar(s) have additional jar dependencies themselves undergoing updates, and these jars must also be included.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The gem includes original java source code that must be built, packaged as a jar and included in with the gem.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;External components, perhaps themselves packaged as gems, have dependencies on the packaged java.  Thus the internally built java must also be published for resolving external java dependencies.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;hoe-rake-maven-the-unholy-union&quot;&gt;Hoe, Rake, Maven: The unholy union&lt;/h2&gt;

&lt;p&gt;The jetty gem Rakefile (see below) attempts the rather perilous integration of Maven into Rake, with &lt;a href=&quot;http://seattlerb.rubyforge.org/hoe/&quot;&gt;Hoe&lt;/a&gt; for gem building and rubyforge publishing support. Here you will find subtle and not-so-subtle workarounds for a variety of issues encountered, including:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;In standard gem build tradition, the gem version is actually obtained by loading ruby source. This has the advantage of the version being specified in one (fewer) place.  However, there is an unfortunate incompatibility with JRuby and Rake both currently defining Object::import (JRUBY-3171). There is a simple workaround: Include an alternative ruby include that just defines version constants without any java imports. In this case its lib/jetty/base.rb, which defines VERSION and JETTY_VERSION in the Jetty module.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Hoe requires a Manifest.txt file on disk a priori, apparently to protect us from making mistakes in what is included in the gem. The problem with this is that jar names to be included may frequently change, and only maven knows the full dependency details.  The workaround is to generate the Manifest.txt file with a :manifest task, but this task must be run and completed in its own process before any Hoe tasks utilize it as part of task definition.  In practice we need to remember to update the manifest when jar’s change, less than ideal.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In the jetty gem, the full list of JARS (l28) are mapped from the jetty/base.rb included VERSIONs. In other gems the assembly brings in arbitrary dependencies of mixed versions, it becomes necessary to Dir.glob( “..assembly/*.jar” ) for these and the :manifest task acquires a “mvn package” task dependency.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We know to build the assembly via “mvn package” if it doesn’t yet exist, or if the pom.xml or assembly.xml is newer. The assembly is made a dependency of the :gem and :test tasks (l84).  However, for gems packing java source, the dependencies are extended to all java source files as well.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Hoe wants to use a graphics tool called “dot” for rdoc generation.  I don’t. Disabled via a crufty ENV[‘NOTDOT’] setter.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These might appear to constitute a rant, but I’m well aware of the fact that all of this software is free. I’m just fishing for a better solution in a relatively new problem space.&lt;/p&gt;

&lt;h2 id=&quot;gemjar-dependencies&quot;&gt;Gem/jar dependencies&lt;/h2&gt;

&lt;p&gt;For a more complicated example, consider a dependency graph between two gems both containing java source:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/gem_jar_deps.png&quot; alt=&quot;gem jar dependencies&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There is no dependency loops and everything works surprisingly well once gem and maven releases have been made to the respective repositories.  However its complicated, manual, and error prone when implementing new versions of both gems in parallel, and needing to share changes with other developers.  We must abandon Maven’s &lt;code class=&quot;highlighter-rouge&quot;&gt;mvn release:prepare&lt;/code&gt; functionality for tagging a release version and incrementing to the next snapshot.  Instead the following steps are typical:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;For gem-a, &lt;code class=&quot;highlighter-rouge&quot;&gt;jrake clean&lt;/code&gt; to remove older jar versions&lt;/li&gt;
  &lt;li&gt;Update versions numbers in the pom.xml, lib/gem-a/base.rb, and a new version to History.txt&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;jrake manifest&lt;/code&gt; to update Manifest.text with new jars.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;jrake test gem&lt;/code&gt; to build, test and package the gem.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mvn install&lt;/code&gt; to make a.jar available for gem-b java build.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;jgem install pkg/gem-a-VERSION.gem&lt;/code&gt; to make gem-a available for gem-b&lt;/li&gt;
  &lt;li&gt;For gem-b, repeat steps 1-4, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also consider that much of the above will need to be repeated if gem-a undergoes changes that gem-b is dependent on.  One saving feature is that from a Maven perspective, gem-a and gem-b still look like “normal” java jar projects, if gem-a and gem-b java changes can be made in parallel with typical maven means, including use of parent pom declaring gem-a and gem-b as modules. But in general, its clear that a better integrated tool, offering full build automation for gems with jars is needed.  An evaluation of alternatives will be saved for a subsequent post.&lt;/p&gt;

&lt;h2 id=&quot;rakefile-for-jetty-gem&quot;&gt;Rakefile for jetty gem&lt;/h2&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rubygems'&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'NODOT'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;no thank you&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'hoe'&lt;/span&gt;

&lt;span class=&quot;vg&quot;&gt;$LOAD_PATH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'./lib'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'jetty/base'&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;JARS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w{ jetty jetty-util jetty-rewrite-handler }&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jetty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JETTY_VERSION&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.jar&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;JARS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;servlet-api-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jetty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SERVLET_API_VERSION&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jetty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JETTY_VERSION&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.jar&quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;JARS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'gravitext-testservlets-1.0.jar'&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;JAR_FILES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JARS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;lib/jetty/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jar&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Update the Manifest with actual jars&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:manifest&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Manifest.txt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'w'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
History.txt
Manifest.txt
README.txt
Rakefile
pom.xml
assembly.xml
bin/jetty-service
lib/jetty.rb
lib/jetty/base.rb
lib/jetty/rewrite.rb
lib/jetty/test-servlets.rb
src/main/java/com/gravitext/testservlets/PerfTestServlet.java
src/main/java/com/gravitext/testservlets/SnoopServlet.java
test/test_jetty.rb
test/test.txt
webapps/test.war
webapps/test/WEB-INF/web.xml
webapps/test/index.html
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;END&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JAR_FILES&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;ensure&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ASSEMBLY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;target/gravitext-testservlets-1.0-bin.dir&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'webapps/test.war'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'webapps/test/index.html'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                             &lt;span class=&quot;s1&quot;&gt;'webapps/test/WEB-INF/web.xml'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'jar cvf webapps/test.war '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;'-C webapps/test index.html -C webapps/test WEB-INF/web.xml'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ASSEMBLY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'pom.xml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'assembly.xml'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mvn package'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;JARS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;lib/jetty/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jar&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ASSEMBLY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cp_r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ASSEMBLY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'lib/jetty'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JAR_FILES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'webapps/test.war'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:mvn_clean&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;rm_f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JAR_FILES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'webapps/test.war'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mvn clean'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:clean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:mvn_clean&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tag&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jetty-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Jetty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;svn_base&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'svn://localhost/subversion.repo/src/gems'&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;tag_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svn_base&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/tags/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;dname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getwd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dname&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`svn status &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip!&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vg&quot;&gt;$stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Resolve the following before tagging (svn status):&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vg&quot;&gt;$stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;svn cp -m 'tag [&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;]' &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag_url&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;hoe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hoe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jetty&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jetty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;developer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;David Kellum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dek-ruby@gravitext.com&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rubyforge_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rjack&quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rdoc_pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^(lib.*\.(rb|txt))|[^\/]*\.txt$/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>RJack Updates</title>
   <link href="http://gravitext.com/2008/12/06/rjack-updates.html"/>
   <updated>2008-12-06T00:00:00-08:00</updated>
   <id>id:/2008/12/06/rjack-updates</id>
   <content type="html">&lt;p&gt;The &lt;a href=&quot;http://rjack.gravitext.com/&quot;&gt;RJack&lt;/a&gt; project home page has been beautified.&lt;/p&gt;

&lt;p&gt;The gem updates include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;jetty and jetty-jsp 6.1.14&lt;/li&gt;
  &lt;li&gt;slf4j 1.5.6&lt;/li&gt;
  &lt;li&gt;logback 0.9.13&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, as part of this update I tested compatibility with newly
released JRuby 1.1.6-RC1. Assuming the RC status is meaningfully
applied and temporary, all should be in good for the 1.1.6 general
release.&lt;/p&gt;

&lt;p&gt;The void * blog is now running these gems.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Scripted Server Setup</title>
   <link href="http://gravitext.com/2008/11/09/scripted-server-setup.html"/>
   <updated>2008-11-09T00:00:00-08:00</updated>
   <id>id:/2008/11/09/scripted-server-setup</id>
   <content type="html">&lt;p&gt;I’ve made several posts on Hashdot and the RJack gems, but have yet to post on the &lt;em&gt;why?&lt;/em&gt; or &lt;em&gt;what?&lt;/em&gt; of it.  This post should fill that gap by way of a non-trivial example.&lt;/p&gt;

&lt;p&gt;At &lt;a href=&quot;http://www.evri.com/&quot;&gt;Evri&lt;/a&gt; I’m responsible for content acquisition and text extraction. We’re parsing volumes of news and general web content, and separating the &lt;em&gt;kernels&lt;/em&gt; of semantically rich text from the &lt;em&gt;chaff&lt;/em&gt; i.e. site navigational boxes, advertisements, etc. Normally this content is processed “offline”, but I had reason recently to package it as a HTTP-XML service for transforming web content in real time.  As the content processing system already existed as a mixture of java (for the heavy lifting) and ruby, I went looking for a solution for service construction that would be simple, flexible enough to reuse the existing processing code and setup, and well performing.&lt;/p&gt;

&lt;p&gt;As the XML production (output) code was already in java, and because I wanted the performance advantages of the JVM’s native threads, I decided to write two java servlets: one to simply test obtaining the web content by URL as a proxy (using thread-safe and connection pooling commons-httpclient), and the second extending the first to actually transform the content HTML into an internal XML representation. So now I have two servlets that need to be setup in an HTTP server, I want them to share a common HTTPClient setup, and the transforming servlet also needs a reference to a custom setup content processor. How was I going to wire all this together?&lt;/p&gt;

&lt;h2 id=&quot;standards-frameworks-and-xml-configuration&quot;&gt;Standards, Frameworks, and XML configuration&lt;/h2&gt;

&lt;p&gt;Classical java would have me create a Web Application Archive (WAR) which contains the compiled Servlets plus a web.xml Deployment Descripter providing the mapping of servlets to URIs. Since the container will be constructing the servlets, access to the HTTPClient and processor must be arranged by non-direct means, presumably through a ServletContext which allows Object-type attributes. Back in the day. with earlier Servlet specs, this would have been done with some form of initialization servlet.  Now its possible to register a context listener which does the same. I could write all the java classes and XML myself, or depend on something like the Spring Framework’s &lt;a href=&quot;http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#context-create&quot;&gt;ContextLoaderListener&lt;/a&gt;.  In either case it will be a challenge to keep the ruby based content processor setup without resorting to an additionally complex integration, with java setting up a jruby interpreter (via BSF or otherwise).  Note also that none of this complexity helps to address such matters as how to get this all installed in a production environment, how the service will be started, monitored, etc.  The two servlets are themselves pretty simple.  How might we shed some of the surrounding baggage?&lt;/p&gt;

&lt;h2 id=&quot;un-inversion-of-control&quot;&gt;Un-Inversion of Control&lt;/h2&gt;

&lt;p&gt;Enter the beautifully designed &lt;a href=&quot;http://www.mortbay.org/jetty/&quot;&gt;Jetty Web Server&lt;/a&gt; which offers full compliance with all of the committee Servlet/JSP standards, but also offers direct programmatic setup under the guise of &lt;a href=&quot;http://docs.codehaus.org/display/JETTY/Embedding+Jetty&quot;&gt;embedding&lt;/a&gt;. It was quick work to prototype the same embedded HTTP server setup in jruby. I later took up authoring a generic jetty gem packaging and setup façade as a side project.&lt;/p&gt;

&lt;p&gt;As I’ve setup a lot of classic java web applications in the past, reviewing the script for this post gave me an eery feeling that somehow there must be a few more XML configuration files or bash scripts somewhere that were needed to make this all work.  That’s not the case here.  Lets quickly review the prerequisites:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Java JDK or JRE&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2008/10/27/jruby-setup-with-hashdot.html&quot;&gt;Hashdot installed&lt;/a&gt; with JRuby in /opt.&lt;/li&gt;
  &lt;li&gt;RJack &lt;a href=&quot;http://rjack.gravitext.com/logback/&quot;&gt;logback&lt;/a&gt; and &lt;a href=&quot;http://rjack.gravitext.com/jetty/&quot;&gt;jetty&lt;/a&gt; gems:
    &lt;pre&gt;
  % jgem install logback jetty
&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;A commons-httpclient gem which has not yet been published.&lt;/li&gt;
  &lt;li&gt;The Evri proprietary content_processor and proxy_transformer ruby Modules which, sorry to say, I won’t be sharing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(Note: I’ve also changed a few of the setup details of these to avoid giving too many details.)&lt;/p&gt;

&lt;h2 id=&quot;dependency-injection&quot;&gt;Dependency Injection&lt;/h2&gt;

&lt;p&gt;Fancy terminology aye?  The HTTP Client and content processor are simply created as local variables &lt;code&gt;http&lt;/code&gt; (l28) and &lt;code&gt;processor&lt;/code&gt; (l33) in the script, and passed directly on construction of the ProxyServlet and TransformServlet (l46-7).  Isn’t that refreshingly direct?   The &lt;code&gt;Jetty::ServerFactory&lt;/code&gt; provided in the jetty gem is used to &lt;code&gt;create&lt;/code&gt; a the complete HTTP server, with a healthy dose of sensible defaults.  The &lt;code&gt;set_context_servlets&lt;/code&gt; call (l35) uses a ruby hash for mapping paths of the root &lt;code&gt;'/'&lt;/code&gt; context to these servlets.  There is no Web Application, because there is no need for one. (However the jetty gem supports full webapps as well when needed.)&lt;/p&gt;

&lt;h2 id=&quot;lifecycle-events&quot;&gt;Lifecycle Events&lt;/h2&gt;

&lt;p&gt;More fancy terminology.  We have some shutdown work we’d like to do after the server port is closed.  Once Jetty is started via &lt;code&gt;server.start&lt;/code&gt; (l51), the &lt;code&gt;server.join&lt;/code&gt; call (l52) will block until the server exits, for example via SIGTERM.  We then have an opportunity to cleanup, for example, gracefully closing the HTTP client and any kept-alive connections (l55).  Other example might include writing out some final reporting information.&lt;/p&gt;

&lt;h2 id=&quot;code-or-configuration&quot;&gt;Code or Configuration?&lt;/h2&gt;

&lt;p&gt;The original Jetty &lt;a href=&quot;http://docs.codehaus.org/display/JETTY/Embedding+Jetty&quot;&gt;embedding&lt;/a&gt; examples are written in Java and thus need to be compiled.  Any settings such as the HTTP port number are thus &lt;em&gt;hard coded&lt;/em&gt; in the jetty examples.  The distinction is a good bit less clear in the case of this ruby script. The script itself is almost entirely in a declarative style, save perhaps for the final server start, join and cleanup (l51-55). It also uses some local variables, but this isn’t unlike named object references in Jetty or Spring XML configuration. It would certainly be easy enough to break a final set of configuration elements out into a separate XML configuration file (or YAML, or even java proprieties), but what is the gain? Is it not easy enough to change any necessary settings in this script directly? Is there not a clear advantage to having all aspects of the service setup in a single unified file, rather than spread out into multiple XML configuration files and the typically required bash wrapper script?&lt;/p&gt;

&lt;h2 id=&quot;where-is-the-ruby-at-runtime&quot;&gt;Where is the Ruby at Runtime?&lt;/h2&gt;

&lt;p&gt;While ruby is used for setup, wiring, dependency injection, or whatever you’d like to call it, ruby is interestingly absent as an explicit reference in any of the Java code, where there is not a a single JRuby or BSF dependency reference. Furthermore, the &lt;code&gt;ProxyServlet&lt;/code&gt; never utilizes a single line of ruby code in its runtime execution.  We’ve effectively made good use of ruby to setup this servlet but take on absolutely zero ruby performance penalties during runtime.&lt;/p&gt;

&lt;p&gt;The content processor by comparison, as used by the &lt;code&gt;TransformServlet&lt;/code&gt; uses a pipeline or chain-of-responsibility pattern with some links in the chain implemented in ruby.  The ruby links are non-performance intensive processing logic steps, and don’t measurably detract from the performance.  Note that if they did become a performance bottleneck, they could be rewritten in pure-java without needing to modify or recompile the &lt;code&gt;TransformServlet&lt;/code&gt;.  Thus by putting ruby in control of service boot-strapping, ruby can easily be injected into the service and mixed with java components where advantageous and without any complexities incurred on the java side. Some additional example code for this kind of ruby injection can be found in the &lt;a href=&quot;http://rjack.gravitext.com/jetty/RJack/Jetty/ServerFactory.html&quot;&gt;Jetty::ServerFactory&lt;/a&gt; rdoc, where for example, I demonstrate implementing a Servlet in ruby and passing it to Jetty.&lt;/p&gt;

&lt;h2 id=&quot;logging-daemonizing-and-other-oft-ignored-subtleties&quot;&gt;Logging, Daemonizing, and other oft ignored Subtleties&lt;/h2&gt;

&lt;p&gt;The Hashdot launcher and hashdot properties are used to arrange for the service to daemonize, redirect STDOUT/STDERR to a log file, and set a Java heap size.  This will make it simple to add the service to inittab or other UNIX process monitor. The logback gem is loaded (l10,14) which loads the slf4j gem as a dependency.  Jetty in turn detects the presence of SLF4J and will use it for logging. SLF4J via Logback output to STDOUT at INFO level is arranged pragmatically (l22-5), thus avoiding a separate logback.xml.  Below is an example of log output using jetty-service script provided with the jetty gem itself (and with the logback gem available).&lt;/p&gt;

&lt;pre&gt;
1    INFO  org.mortbay.log - Logging to Logger[org.mortbay.log] via org.mortbay.log.Slf4jLog
58   INFO  org.mortbay.log - jetty-6.1.12
280  INFO  org.mortbay.log - NO JSP Support for /, did not find org.apache.jasper.servlet.JspServlet
388  INFO  org.mortbay.log - Started SelectChannelConnector@0.0.0.0:38225
388  INFO  jetty-service - Listening on port: 38225
&lt;/pre&gt;

&lt;p&gt;Its important to redirect STDOUT and STDERR because any fatal errors like uncaught runtime exceptions or java crash dumps will end up here.  Its also convenient when debugging a problem to be able to &lt;code&gt;pkill -HUP proxy-transform-service&lt;/code&gt; and get all of the thread stack dumps into the log. Finally, its quite common for an application which brings in many open-source components to have cases where these components write to STDOUT or STDERR under &lt;em&gt;unusual&lt;/em&gt; circumstances.  An approach that redirects STDOUT/STDERR to &lt;code&gt;/dev/null&lt;/code&gt; or an obscure alternative log file will commonly result in this information being lost.  Thus I find a certain old school elegance in coalescing all the logging output to STDOUT, using Hashdot to redirect this to a file at the system (not just java) level, and using an external tool like logrotate to provide log rotation.&lt;/p&gt;

&lt;h2 id=&quot;proxy-transform-service-script&quot;&gt;proxy-transform-service script&lt;/h2&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/opt/bin/jruby&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#. hashdot.profile += daemon&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#. hashdot.vm.options += -Xmx256m&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#. hashdot.io_redirect.file = /var/log/proxy-transformer.log&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rubygems'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'logback'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 0.9'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'jetty'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 6.1'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'commons-httpclient'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~&amp;gt; 3.1'&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'logback'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'jetty'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'commons-httpclient'&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'content_processor'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'proxy_transformer'&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Logback&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Logback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Logback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_appender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Logback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ConsoleAppender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Logback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Logback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;INFO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# HTTP Client&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CommonsHttpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Facade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;max_total_connections&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#ms&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Content Processor&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ContentProcessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;centroid_weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filter_threads&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# HTTP Server&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jetty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ServerFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;max_threads&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;max_idle_time_ms&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;

&lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ProxyTransformer&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set_context_servlets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/proxy'&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ProxyServlet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'/transform'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TransformServlet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Shutdown, cleanup&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;shutdown&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>RJack Updates</title>
   <link href="http://gravitext.com/2008/11/09/rjack-updates.html"/>
   <updated>2008-11-09T00:00:00-08:00</updated>
   <id>id:/2008/11/09/rjack-updates</id>
   <content type="html">&lt;p&gt;Two gem updates released to the &lt;a href=&quot;http://rjack.gravitext.com/&quot;&gt;RJack&lt;/a&gt; project on RubyForge:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/jetty/&quot;&gt;jetty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rjack.gravitext.com/jetty-jsp/&quot;&gt;jetty-jsp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;based on the latest &lt;a href=&quot;http://www.mortbay.org/jetty/&quot;&gt;Jetty Web Server&lt;/a&gt; 6.1.12.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Hashdot 1.2 Dev</title>
   <link href="http://gravitext.com/2008/11/07/hashdot-1.2-dev.html"/>
   <updated>2008-11-07T00:00:00-08:00</updated>
   <id>id:/2008/11/07/hashdot-1.2-dev</id>
   <content type="html">&lt;p&gt;A quick status update on Hashdot:&lt;/p&gt;

&lt;p&gt;To better facilitate merging changes with a colleague who is
investigating the Mac OS X port, I’ve enabled the SVN repository for
Hashdot on SourceForge, imported the 1.1 source, and then committed a
set of changes for Hashdot 1.2 I had previously made locally.  The
changes are listed below as a preview for interested parties:&lt;/p&gt;

&lt;pre&gt;
* Added change_log starting with version 1.2

svn merge -r 464:HEAD

* Added HUP signal handler to reopen STDOUT/STDERR for logrotate
  compatibility.
* Fixed hashbang.
* Added explicit int casts to avoid x64 warnings
* VERSION -&amp;gt; 1.2
* Added support for hashdot.chdir, incl. test and doc.
&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>RJack Updates</title>
   <link href="http://gravitext.com/2008/11/06/rjack-updates.html"/>
   <updated>2008-11-06T00:00:00-08:00</updated>
   <id>id:/2008/11/06/rjack-updates</id>
   <content type="html">&lt;p&gt;On Monday, I managed to complete the release of two new gems and two gem updates, all part of the &lt;a href=&quot;http://rjack.gravitext.com/&quot;&gt;RJack&lt;/a&gt; project hosted on RubyForge.  As this is my first post on RJack, here is a an introduction without quoting the linked project summary, and some details on jar in gem packaging.&lt;/p&gt;

&lt;p&gt;RJack provides gem packaging of various broadly used Java components for use with JRuby. Included in this release set are upstream updates to the &lt;a href=&quot;http://rjack.gravitext.com/slf4j/&quot;&gt;slf4j&lt;/a&gt; and &lt;a href=&quot;http://rjack.gravitext.com/logback/&quot;&gt;logback&lt;/a&gt; logging system, and the new gems &lt;a href=&quot;http://rjack.gravitext.com/jetty/&quot;&gt;jetty&lt;/a&gt; and &lt;a href=&quot;http://rjack.gravitext.com/jetty-jsp/&quot;&gt;jetty-jsp&lt;/a&gt;, which package the Jetty Web Server. A guiding design principal is that each of these gems may be used simply and solely to deliver java package dependencies to a consuming ruby/java application.  However, three of the four gems in the current set also provide either façades for setup or adapters for direct use from ruby code.  For these inclusions, care is given to avoid incurring any additional external dependencies.&lt;/p&gt;

&lt;h2 id=&quot;why-gems&quot;&gt;Why gems?&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;http://www.rubygems.org/&quot;&gt;RubyGems&lt;/a&gt; package management system offers several advantages for working with server application components in both development and production environments, including a straight forward command line interface, multiple remote and local gem repositories, versioned dependency management, support for multiple versions of the same component, and support for installing command-line accessable executables. While in common use for pure and C-extended ruby components, JRuby provides an opportunity to similarly utilize gems for java-extended ruby or even near-pure java components.  The nearest alternative from the java comunity would be Maven and its derivatives, but these are far more appropriate for handling java dependencies at compile time than for production deployment.&lt;/p&gt;

&lt;p&gt;Once the logical decision to deploy gems is made, the first order of business is determining how to divide the various components and their java jar dependencies into gems for maximum benefit including reuse, reliability, and ease/safety of upgrade with independent release cycles. Java components are ultimately loaded by searching through an order list of jars or directories, the “classpath”.  If multiple versions of the same java package are found on the classpath, the first instance wins.  Problems arise if two utilized components include incompatible versions of the same package.  It is therefore advantageous to break common java dependencies out into individual gems and utilize the version management offered by the rubygems system.&lt;/p&gt;

&lt;p&gt;As a prime example, the SLF4J system is predicated on the application developer selecting a  final logging destination by installing the appropriate logging adapter in the classpath.  If however various gems with some java dependencies each in turn include their own versions of these or other logging system jars, then this logging output flexibility is lost.  As SLF4J is now the best available logging interface for Java, I’ve released an independent slf4j gem, for reuse and in the hope of encouraging other java gem packagers to use the slf4j gem to resolve any java logging dependencies, in lieu of embedding alternative logging components in their gems.&lt;/p&gt;

&lt;h2 id=&quot;gems-package-projects&quot;&gt;Gems package projects&lt;/h2&gt;

&lt;p&gt;Since Maven manages dependencies at the jar level, a one-to-one mapping would suggest a gem per jar. While some discussion has taken place on automating direct jar to gem packing, a significant offering has yet to be published.  I’m also not convinced this is the right mapping. Consider that in ruby, the &lt;code class=&quot;highlighter-rouge&quot;&gt;require&lt;/code&gt; method is included in normal program flow, and its common to use multiple &lt;code class=&quot;highlighter-rouge&quot;&gt;require&lt;/code&gt; calls to selectively bring in different parts of a single gem.  JRuby extends &lt;code class=&quot;highlighter-rouge&quot;&gt;require&lt;/code&gt; to support adding a named jar file to the classpath of its custom class loader.  Its therefore quite reasonable for gems to map directly to a java project which provides more than one jar. A clear example of this is the RJack slf4j gem. See the &lt;a href=&quot;http://rjack.gravitext.com/slf4j/RJack/SLF4J.html&quot;&gt;SLF4J module&lt;/a&gt; documentation for &lt;code class=&quot;highlighter-rouge&quot;&gt;require&lt;/code&gt; mappings to eight additional input/output adapter jars beyond the &lt;code class=&quot;highlighter-rouge&quot;&gt;slf4j-api-version.jar&lt;/code&gt; loaded with the base &lt;code class=&quot;highlighter-rouge&quot;&gt;require 'slf4j'&lt;/code&gt;. There will be other cases where a single jar is best packaged as a gem, but its clearly not necessary to do so when a project releases multiple jar’s in version lock-step.&lt;/p&gt;

&lt;p&gt;A counter argument for finer grain gems is the case where, as with the jetty-jsp gem, large jars are included and some of these are commonly not needed. The jetty gem contains the core server components and servlet-api (4 gems) at 0.7M where as the jetty-jsp gem adds JSP support (4 more gems) for an additional 5.2M.  There are also cases where external project jar dependencies are added to a gem.  The argument for this would be that it is unlikely that a particular dependency will be used in another context.  Of course this could be a mistake, but its one that can be fixed later with a patch release, breaking the dependency out into its own gem.&lt;/p&gt;

&lt;h2 id=&quot;other-considerations&quot;&gt;Other considerations&lt;/h2&gt;

&lt;p&gt;Finally its worth noting that these gems defer to the upstream java project version numbers, adding a single gem package release number to the end of the upstream version. This puts emphasis on version compatibility in upstream project upgrades and will encourage timing any significant ruby layer changes with upstream major releases.&lt;/p&gt;

&lt;p&gt;Along similar lines, each of these gems is released under identical license terms to the upstream project, thereby simplifying license considerations for consuming applications.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>JRuby setup with Hashdot</title>
   <link href="http://gravitext.com/2008/10/27/jruby-setup-with-hashdot.html"/>
   <updated>2008-10-27T00:00:00-07:00</updated>
   <id>id:/2008/10/27/jruby-setup-with-hashdot</id>
   <content type="html">&lt;p&gt;In &lt;a href=&quot;/2008/10/22/introducing-hashdot.html&quot;&gt;Introducing Hashdot&lt;/a&gt; I laid out various problems with using the standard ‘java’ launcher and the ‘jruby’ wrapper bash script provided with the JRuby distribution.   In this post, I’m going to describe in detail an alternative installation layout and setup of JRuby using Hashdot to best advantage.   Some goals for this setup:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Any and all invocations of ‘jruby’ should be launched via hashdot to take advantage of central configuration, better process report, and hashdot script headers where appropriate.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;No user environment dependencies. We want JRuby to function properly for any user on the system, without requiring each user to maintain environment variables, etc. in their own profile.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A separate gem repository for JRuby, independent of the versioned JRuby distribution. This will enable JRuby upgrades without needing to rebuild or copy to a new gem repository.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;MRI-ruby and JRuby need to coexist. This means ‘ruby’ launches MRI and ‘jruby’ launches JRuby.   Such utilities as ‘ri’, ‘gem’, and ‘rake’ should invoke MRI ruby, while we also want convenient access to the same via JRuby as ‘jri’, ‘jgem’, ‘jrake’, etc.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The current latest ‘jruby’ interpreter, ‘jgem’, ‘jri’, ‘jrake’ and any bin scripts installed in the jruby gem repository should be available on a system standard PATH.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;layout-in-opt&quot;&gt;Layout in /opt&lt;/h2&gt;

&lt;p&gt;The ‘/opt’ directory appears most appropriate for this kind of system wide but custom setup. Alternatively ‘/usr/local’ could be used.  The following structure is recommended and will be described in detail below.&lt;/p&gt;

&lt;pre&gt;
/opt
|-- bin
|   |-- hashdot
|   |-- jgem
|   |-- jirb -&amp;gt; ../dist/jruby/bin/jirb
|   |-- jrake -&amp;gt; ../jruby/gems/bin/rake
|   |-- jri -&amp;gt; ../dist/jruby/bin/ri
|   |-- jruby -&amp;gt; hashdot
|   `-- jruby-shortlived -&amp;gt; hashdot
|-- dist
|   |-- jdk_sun_1.6.0_05_x32
|   |-- jdk_sun_1.6.0_07_x32
|   |-- jruby -&amp;gt; jruby-1.1.4
|   |-- jruby-1.1.3
|   `-- jruby-1.1.4
|-- hashdot
|   `-- profiles
`-- jruby
    `-- gems
&lt;/pre&gt;

&lt;h2 id=&quot;create-the-opt-skeleton&quot;&gt;Create the /opt skeleton&lt;/h2&gt;

&lt;p&gt;As root:&lt;/p&gt;

&lt;pre&gt;
% mkdir /opt /opt/bin /opt/dist /opt/jruby
&lt;/pre&gt;

&lt;h2 id=&quot;configure-make-install-hashdot&quot;&gt;Configure, make, install Hashdot&lt;/h2&gt;

&lt;p&gt;Configuring hashdot currently involves editing the Makefile including with the source distribution. Set the following Makefile variables:&lt;/p&gt;

&lt;pre&gt;
INSTALL_BIN=/opt/bin
PROFILE_DIR=/opt/hashdot/profiles
&lt;/pre&gt;

&lt;p&gt;Hashdot may then be built or rebuilt via ‘make clean all’.  You might want to then customize the various profiles before running ‘make install’ as root.  You can also customize them in /opt/hashdot/profiles after install.&lt;/p&gt;

&lt;h2 id=&quot;install-distributions&quot;&gt;Install distributions&lt;/h2&gt;

&lt;p&gt;As root, unpack the Java JDK (or JRE) and JRuby under /opt/dist. Note I prefer to use a more descriptive name including the full java version number. Since we also reference some jruby distro provided bin scripts, symlink the latest jruby-1.1.4 to jruby. As shown, its possible for multiple versions of java/jruby to coexist for testing purposes.&lt;/p&gt;

&lt;h2 id=&quot;create-optjrubygems-repository&quot;&gt;Create /opt/jruby/gems repository&lt;/h2&gt;

&lt;p&gt;To create a starting gems repository, as root:&lt;/p&gt;

&lt;pre&gt;
% cp -a /opt/dist/jruby-1.1.4/lib/ruby/gems /opt/dist/jruby/gems
&lt;/pre&gt;

&lt;p&gt;To make sure this repository is used for all jruby invocations, GEM_HOME and GEM_PATH environment variables are set via Hashdot in the jruby.hdp profile (see below.)  This has an important advantage over setting in a user profile: The MRI ruby gem home/path will not be effected and can be kept separate.&lt;/p&gt;

&lt;h2 id=&quot;create-optbin-symlinks&quot;&gt;Create /opt/bin symlinks&lt;/h2&gt;

&lt;p&gt;The above list directory tree for /opt/bin should be self explanatory. We create ‘jirb’ and ‘jri’ symlinks to dist/jruby/bin in order to make these easy to use.  Note however that ‘jruby -S ri’ will also still work for these.  Similarly we create jrake as a symlink to gems/bin/rake, since in a development environment we will likely want MRI and jruby versions both available.  Finally, we choose to customize ‘jgem’ by copying and changing the hashbang to:&lt;/p&gt;

&lt;pre&gt;
#!/opt/bin/jruby-shortlived
&lt;/pre&gt;

&lt;p&gt;Which gives a bit of a performance advantage in common usage.  Note that with the given hashdot profiles, the server vm is default, which is almost always the best choice for any long lived application.&lt;/p&gt;

&lt;h2 id=&quot;system-path&quot;&gt;System PATH&lt;/h2&gt;

&lt;p&gt;Adjusting the standard system PATH is Linux (or UNIX) distribution dependent and might involve modifying /etc/profile or an equivalent. In any case, /opt/bin should be found early in the path (since we only include ‘j*’ commands here), and /opt/jruby/gems/bin should be added late in the path to get additional jruby gem bin scripts available, but without overriding any MRI ruby gem equivalents. For example:&lt;/p&gt;

&lt;pre&gt;
/opt/bin:/usr/local/bin:/bin:/usr/sbin:/usr/bin:/opt/jruby/gems/bin
&lt;/pre&gt;

&lt;p&gt;In the case where you need both the MRI and jruby gem bin script versions to coexist in the PATH, create a ‘j*’ symlink in /opt/bin for the jruby version.&lt;/p&gt;

&lt;h2 id=&quot;hashdot-profiles&quot;&gt;Hashdot profiles&lt;/h2&gt;

&lt;p&gt;The customized profiles I use on my 32bit development environment are given below.&lt;/p&gt;

&lt;p&gt;default.hdp:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# HashDot default launch profile&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# This profile is always read before any other profiles or hashdot&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# properties&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;## Setup the default JVM&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# JVM install directory&lt;/span&gt;
hashdot.vm.home &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; /opt/dist/jdk_sun_1.6.0_07_x32

&lt;span class=&quot;c&quot;&gt;# Use &quot;amd64&quot; instead of i386 for linux 64bit platforms&lt;/span&gt;
hashdot.vm.arch     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; i386

&lt;span class=&quot;c&quot;&gt;# Use server by default (see alternative in shortlived.hdp)&lt;/span&gt;
hashdot.vm.mode     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; server

hashdot.vm.libbase  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.vm.home&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/jre/lib/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.vm.arch&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Setup libpath to find libjvm.so and dependencies.&lt;/span&gt;
hashdot.vm.libpath  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.vm.libbase&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
hashdot.vm.libpath +&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.vm.libbase&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.vm.mode&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# TZ often needed for Java&lt;/span&gt;
hashdot.env.TZ &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; America/Los_Angeles
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;jruby.hdp:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# HashDot launch profile for JRuby (http://jruby.codehaus.org/)&lt;/span&gt;

jruby.home    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; /opt/dist/jruby-1.1.4
jruby.lib     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;jruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.home&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/lib
jruby.script  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; jruby
jruby.shell   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; /bin/sh

&lt;span class=&quot;c&quot;&gt;# Identical defaults to jruby launcher&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# These can still be overridden by the individual scripts&lt;/span&gt;
hashdot.vm.options +&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Xmx500m&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Xss1024k&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Only jruby.jar is required for typical usage (scripts can require&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# bsf.jar or JIP profiler if desired).&lt;/span&gt;
hashdot.vm.options +&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Xbootclasspath&lt;/span&gt;/a:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;jruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.lib&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/jruby.jar

hashdot.main &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; org.jruby.Main

&lt;span class=&quot;c&quot;&gt;# Arguments following these flags are _not_ a script to scan for&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# hashdot headers.&lt;/span&gt;
hashdot.parse_flags.value_args &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Give up looking for a script header with any of these&lt;/span&gt;
hashdot.parse_flags.terminal &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# GEM PATH/HOME&lt;/span&gt;
hashdot.env.GEM_HOME &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; /opt/jruby/gems
hashdot.env.GEM_PATH &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.env.GEM_HOME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;shortlived.hdp:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# HashDot profile for short lived processes.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Overrides default.hdp to use the client VM.&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Note: for current amd64 java distros, this entire profile should be&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# commented out to disable it, since these only include a server VM&lt;/span&gt;

hashdot.vm.mode     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; client

hashdot.vm.libpath  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.vm.libbase&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
hashdot.vm.libpath +&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.vm.libbase&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;hashdot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.vm.mode&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;jruby-shortlived.hdp:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# HashDot profile for shortlived jruby scripts.&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Extends shortlived (client VM) and (jruby) profiles with further&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# startup time tweaks.&lt;/span&gt;
hashdot.profile &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; shortlived jruby

&lt;span class=&quot;c&quot;&gt;# JMX setup slows startup and wont be used&lt;/span&gt;
jruby.management.enabled&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Introducing Hashdot</title>
   <link href="http://gravitext.com/2008/10/22/introducing-hashdot.html"/>
   <updated>2008-10-22T00:00:00-07:00</updated>
   <id>id:/2008/10/22/introducing-hashdot</id>
   <content type="html">&lt;p&gt;From the summary of &lt;a href=&quot;http://hashdot.sourceforge.net/&quot;&gt;Hashdot&lt;/a&gt; as published on SourceForge:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hashdot elevates Java-platform script interpreters to first class
status on Unix-like operating systems. It provides a script aware
replacement to the stock ‘java’ launcher, and thus avoids numerous
issues in using the ‘java’ launcher to bootstrap a script
interpreter. All relevant interpreter and JVM options (i.e: Java
heap size) may be specified directly in a script header and/or via
system profiles, without resorting to environment variables, command
line arguments, and the additional wrapper shell scripts needed to
maintain them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are some interesting aspects to this work. Firstly, Hashdot really needed to be written in C for tight UNIX integration (fork(), dlopen(), prctl(), setsid(), etc.)  Though I had developed in C and C++ for many years, it’s been a while, and I was surprised at how cumbersome I now found it.&lt;/p&gt;

&lt;p&gt;Its a bit of of an odd predicament to be forced to write C, essentially as &lt;em&gt;glue code&lt;/em&gt; between UNIX, java, and a Java-platform script interpreter.  When I took on my first large scale java project in the late 90’s (Java 1.1, Netscape Search federation and UI) I reserved the right to do any heavy lifting that was required in C++. Surprisingly, it didn’t come to that then, and only rarely has in the last 10 years. I &lt;em&gt;have&lt;/em&gt; spent some time optimizing inner loops in a style of java that looks all too close to C. So here I am writing actual C again, but as glue code rather than for performance reasons, and simply to gain some modest comforts while writing ruby and java for the JVM!&lt;/p&gt;

&lt;p&gt;Its doubly odd to be writing C glue code in order to make decidedly incremental and cosmetic improvements to a UNIX, Java, and JRuby stack.  What if however, the aggregate total of current stack integration warts, annoyances, and complexities might otherwise retard broad acceptance of that stack by any of the UNIX, Java, or Ruby camps? Maybe then it would be worth diving in.&lt;/p&gt;

&lt;h2 id=&quot;the-stock-java-launcher&quot;&gt;The Stock ‘java’ Launcher&lt;/h2&gt;

&lt;p&gt;Understanding the &lt;em&gt;why&lt;/em&gt; of Hashdot must begin with a problem statement, and the problems begin with the stock ‘java’ launcher command line:&lt;/p&gt;

&lt;pre&gt;
% java -h
Usage: java [-options] class [args...]
           (to execute a class)
   or  java [-options] -jar jarfile [args...]
           (to execute a jar file)

where options include:
    -client       to select the &quot;client&quot; VM
    -server       to select the &quot;server&quot; VM
    -cp &amp;lt;class search path of directories and zip/jar files&amp;gt;
    -classpath &amp;lt;class search path of directories and zip/jar files&amp;gt;
                  A : separated list of directories, JAR archives,
                  and ZIP archives to search for class files.
    -D&amp;lt;name&amp;gt;=&amp;lt;value&amp;gt;
                  set a system property
    ...
&lt;/pre&gt;

&lt;p&gt;Like many before and after, the Java programming language started life as a toy. It must have seemed reasonable at that point, for a user starting a java program via a class’s Main method to effectively link its dependencies by passing -cp on the command line.  Anyone using Java in any sort of performance intensive application would know that also setting an appropriate maximum heap size (-Xmx) and, for long lived services, using the -server VM is essentual.  All told the following example “command line” is typical for a full production java service:&lt;/p&gt;

&lt;pre&gt;
java -server -Xss512k -Xms32m -Xmx256m -XX:NewSize=16m -Xnoclassgc
-Dsun.net.inetaddr.ttl=30 -Dsun.net.inetaddr.negative.ttl=0
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=/home/david/run/\
  apache-tomcat-5.5.20/conf/logging.properties
-Djava.endorsed.dirs=/home/david/run/apache-tomcat-5.5.20/common/endorsed
-classpath :/home/david/run/apache-tomcat-5.5.20/bin/bootstrap.jar\
  :/home/david/run/apache-tomcat-5.5.20/bin/commons-logging-api.jar
-Dcatalina.base=/home/david/run/apache-tomcat-5.5.20
-Dcatalina.home=/home/david/run/apache-tomcat-5.5.20
-Djava.io.tmpdir=/home/david/run/apache-tomcat-5.5.20/temp
org.apache.catalina.startup.Bootstrap start
&lt;/pre&gt;

&lt;p&gt;Since its rather impractical to type this on the command line, the obvious solution is to write a shell script wrapper that exec’s it. Ironically, by not offering a better facility for process boot strapping, and in the name of portability, Sun pushed developers into writing highly non-portable shell scripts.  For a developer starting his career in Java, this was an awkward introduction to (ba)sh scripts.  For a complete server such as Tomcat used in the above example, I count 1276 lines of shell script (sh + bat) and a maintenance job in its own right.&lt;/p&gt;

&lt;h2 id=&quot;enter-jruby&quot;&gt;Enter JRuby&lt;/h2&gt;

&lt;p&gt;Scripting languages like ruby, perl, or python have all done a much better job than java in supporting cross-platform portability while also not hamstringing OS, file system, or console operablility.  This was but one aspect that drew me to jruby.  Would it not be possible to deploy entire, potentially complex, java-based services as easily installed gems and directly executable ruby scripts, rather than the typical amalgam of tarballs, XML config files, and wrapper bash scripts?&lt;/p&gt;

&lt;p&gt;I believe the answer is a resounding “yes!” and that it will be well worth the effort, but there is some work to do to really get there. Lets consider the following very simple theoretical service implemented in java with a jruby based launch script:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/opt/dist/jruby-1.1.4/bin/jruby&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'myservice.jar'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'com.foobar.MyService'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note the use of a UNIX-standard ‘#!’ &lt;em&gt;hashbang&lt;/em&gt; (or &lt;em&gt;shebang&lt;/em&gt;) to reference the interpreter for the script. The UNIX shell or system exec calls will detect the hashbang and invoke the named interpreter executable, passing the script file as an argument.  While this works fine with any native script interpreter (ruby, perl, python, bash, etc.) &lt;em&gt;it won’t work out of the box with jruby or any other java-platform script interpreters not resorting to native code.&lt;/em&gt; The reason is that the referenced ‘jruby’ launcher is itself a bash script which  needs to exec a java command line with the required details. On Linux at least, multiple scripts/intpreters can not be chained in this way. The work around presented for jruby is:&lt;/p&gt;

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

&lt;p&gt;…which then requires something like:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/opt/dist/jruby-1.1.4/bin:&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…to be set in a user or system global profile. Besides raising some traditional security concerns, the use of the env workaround has notable side effects.  First, it introduces a PATH dependency that must be managed external to the script.  Next consider wanting to set various jruby intpreter and java options made available as command line flags to jruby, for example enabling full compilation and the java max heap size:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env jruby -X+C -J-Xmx256m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But &lt;em&gt;this also doesn’t work&lt;/em&gt;, at least on current versions of Linux. When used as a hashbang, the &lt;code class=&quot;highlighter-rouge&quot;&gt;jruby -X+C -J-Xmx256m&lt;/code&gt; is interpreted as the single parameter, and (fortunately) isn’t found in the path. Similarly the jruby script launcher supports JRUBY_OPTS as an alternative means to set these options:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env JRUBY_OPTS='-X+C -J-Xmx256m' jruby&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But again, on current versions of Linux, &lt;em&gt;this approach fails even more spectacularly: it causes an infinite loop!  Brilliant!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You could tune all of the java and jruby option flags by directly modifying the jruby launcher script in the jruby distribution, but then you are stuck with one set of settings per host and jruby install. Poor at best.  The only practical option (short of native code) is to write a wrapper script to launch any ruby script we want to set options for:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Hard code myservice.rb location or have additional fun determining&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# relative location via $0&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; /opt/dist/jruby-1.1.4/bin/jruby &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt;+C &lt;span class=&quot;nt&quot;&gt;-J-Xmx256m&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-J-server&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     /home/david/run/myservice.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Isn’t this fun? We are now back where we started with the java launcher: needing to write wrapper scripts around any significant production application. Besides being a nuisance and hiding important details in a seperate wrapper script file, this approach is particularly at odds with the bin script support offered by RubyGems.  Without a native launcher like Hashdot, does this lack of exec integration not deminish jruby by comparison, not with java, but with  MRI (native) ruby?&lt;/p&gt;

&lt;p&gt;In my next post on Hashdot, I’ll demonstrate how Hashdot solves these and other historic blemishes and limitations of java and java-platform script interpreters like JRuby.&lt;/p&gt;

</content>
 </entry>
 

</feed>
