<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>
  var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
  document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));

  try {
    var pageTracker = _gat._getTracker("UA-9103050-1");
    pageTracker._trackPageview();
  } catch(err) {}
</description><title>Pennyworth Only</title><generator>Tumblr (3.0; @gcnovus)</generator><link>http://jamesarosen.com/</link><item><title>On Interface Gems</title><description>&lt;p&gt;Some members of &lt;a href="http://bostonrb.org"&gt;Boston.rb&lt;/a&gt; had a great discussion at last night’s hackfest about library dependencies. In particular, we were discussing the problem of libraries (gems) that depend on other libraries and how Rubygems and Bundler both promote &lt;em&gt;implementation&lt;/em&gt; dependency instead of &lt;em&gt;interface&lt;/em&gt; dependency. “Interfaces? We don’t need no stinkin’ interfaces!” I hear you cry. It’s true: Ruby doesn’t have interfaces in the same sense Java does, but it certainly has them in the object-oriented sense. We generally call them APIs.&lt;/p&gt;

&lt;p&gt;Take the following scenario: you’re building a web application that you will deploy in a Java container, so you’re running on JRuby. You want to integrate with Twitter, so you add the &lt;a href="http://rubygems.org/gems/twitter"&gt;twitter gem&lt;/a&gt;, which in turn depends on the &lt;a href="http://rubygems.org/gems/yajl-ruby"&gt;yajl-ruby&lt;/a&gt; gem for JSON parsing. Sadly, &lt;code&gt;yajl-ruby&lt;/code&gt; is &lt;em&gt;C&lt;/em&gt; bindings for JSON. What you &lt;em&gt;want&lt;/em&gt; to use is &lt;a href="http://rubygems.org/gems/json-jruby"&gt;json-jruby&lt;/a&gt;, but you would have to fork the &lt;code&gt;twitter&lt;/code&gt; gem to make it use that. It’s not just that &lt;code&gt;yajl-ruby&lt;/code&gt; and &lt;code&gt;json-jruby&lt;/code&gt; have different APIs — indeed, they don’t differ by much — it’s that the dependencies are declared in the &lt;code&gt;.gemspec&lt;/code&gt; files, so even monkey-patching can’t save the day.&lt;/p&gt;

&lt;p&gt;This is precisely why Eric Gamma (one of the &lt;a href="http://en.wikipedia.org/wiki/Design_Patterns_%28book%29"&gt;Gang of Four&lt;/a&gt;) says you should, “program to an interface, not an implementation.” (See &lt;a href="http://www.artima.com/lejava/articles/designprinciples.html"&gt;this interview&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;Interface Libraries&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;twitter&lt;/code&gt; depended on an interface instead of an implementation, you would be free to use any implementation you wanted so long as it abided by the interface. But &lt;em&gt;how&lt;/em&gt;, exactly, do you write an interface in Ruby?&lt;/p&gt;

&lt;h2&gt;Rubygems and &lt;code&gt;provides&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In a &lt;a href="http://sickill.net/blog/2010/07/03/thoughts-on-ruby-gem-dependencies.html"&gt;recent post&lt;/a&gt;, Marcin Kulic discusses this and other dependency problems in the Ruby/Gems landscape. His recommendation is that we add &lt;code&gt;#provides&lt;/code&gt; to &lt;code&gt;Gem::Specification&lt;/code&gt; and use it like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Gem::Specification.new do |gem|
  gem.name = "my-json-implementation"
  gem.version "2.7.4"
  gem.provides "json", :version =&gt; "&lt;= 1.2.0"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a fine idea in theory, but Nick Quaranto pointed out that modifying Rubygems is exceedingly difficult and it raises backwards-compatibility problems. Additionally, there are naming issues, since the first implementation of a set of functionality (JSON, YAML, LDAP, Twitter API) usually takes the most obvious name for the interface.&lt;/p&gt;

&lt;h2&gt;Bridge Gems&lt;/h2&gt;

&lt;p&gt;Michael Bleigh, in writing &lt;a href="http://rubygems.org/gems/omniauth"&gt;OmniAuth&lt;/a&gt;, a Rack-based authentication provider, found he needed JSON parsing, but he didn’t want to depend on a specific implementation precisely because he didn’t want to dictate what the clients of his gem should use. To scratch this itch, he created &lt;a href="http://rubygems.org/gems/multi_json"&gt;multi_json&lt;/a&gt;, which uses the &lt;a href="http://en.wikipedia.org/wiki/Bridge_pattern"&gt;Bridge Pattern&lt;/a&gt;. &lt;code&gt;multi_json&lt;/code&gt; declares only two methods: &lt;code&gt;MultiJson#encode(object)&lt;/code&gt; and &lt;code&gt;MultiJson#decode(json_string)&lt;/code&gt;. It has a number of “engines” that it can use. If you want to write a JRuby-based web application that uses OmniAuth, simply declare your dependency on &lt;code&gt;json-jruby&lt;/code&gt; and then set the engine appropriately.&lt;/p&gt;

&lt;h2&gt;Pre-definition and Post-definition&lt;/h2&gt;

&lt;p&gt;For functionality that already exists in several implementations — JSON encoding and decoding, LDAP access, &amp;c. — the mission of an interface (either the “provides” version or the bridge gem version) is clear: extract out the most salient pieces of all the popular implementations and give them a common API. For functionality that does not yet exist — for example, the client API for a new web service — the interface builder is faced with a difficult decision: define the interface first or extract it later?&lt;/p&gt;

&lt;p&gt;Pre-defining the interface has one key advantage: clients of the interface can easily depend on the interface rather than an implementation, so there won’t be a mad rush to change all the gems in the wild later. It also has a disadvantage: it reeks of premature decision making. Waiting to define the interface until several competing implementations have sprung up allows the interface writer to select only the most important aspects of the libraries.&lt;/p&gt;

&lt;p&gt;Of course, in the real world, it is likely to be a mix. Perhaps you start with an implementation, try it out on a few projects, build a first version of an interface, and iterate on both. Interfaces, after all, can be versioned just like implementations. Of course, we hope they don’t change too often.&lt;/p&gt;

&lt;h2&gt;Core Principles of an Interface&lt;/h2&gt;

&lt;p&gt;Above all, &lt;a href="http://semver.org/"&gt;semantic versioning&lt;/a&gt; is key to building a great interface. Included in semantic versioning is proper use of deprecation. Any method, class, or module that is to be removed must first be deprecated in a minor or patch version and can only be removed in a major version.&lt;/p&gt;

&lt;p&gt;Secondly, interface writers should take input from the community on the API. Perhaps the easiest way to do this is to inspect the popular implementations in a category on &lt;a href="http://ruby-toolbox.com/"&gt;Ruby Toolbox&lt;/a&gt; for commonalities.&lt;/p&gt;

&lt;p&gt;And, of course, only build interfaces where they’re truly necessary. Out of the thousands and thousands of gems in the wild, very few provide such core functionality that they are used by libraries that are in turn used by applications. Let’s keep in mind the problem we’re trying to solve and not go interface-crazy.&lt;/p&gt;

&lt;h2&gt;How to Build an Interface Gem&lt;/h2&gt;

&lt;p&gt;What does it mean to &lt;em&gt;make&lt;/em&gt; an interface gem? &lt;code&gt;multi_json&lt;/code&gt; is certainly a good start at a JSON interface. It provides core functionality (just two methods, &lt;code&gt;#encode&lt;/code&gt; and &lt;code&gt;#decode&lt;/code&gt;) and makes it easy for the client to plug in new engines. The Boston.rb group decided that a truly great interface gem would have at least one additional feature, though: an easy way for implementers to test the compliance of their implementations. This will vary in practice, but it might be an Rspec macro:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'multi_json/spec_macros'

describe MyJSONImplementation do
  subject do
    MyJSONImplementation
  end
  it_should_implement_json(:version =&gt; 1.0)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or it could be a &lt;code&gt;Module&lt;/code&gt; that gets mixed in to a test class:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'multi_json/test_support'

class MyJSONTest &lt; Test::Unit::TestCase
  include MultiJson::TestCase::VersionOnePointOh
  def subject
    MyJSONImplementation
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In either case, the important point is that implementers have a dead-simple way to add a whole suite of compliance tests. One thing we discussed at the hackfest was creating an &lt;code&gt;rspec_rfc&lt;/code&gt; gem that supports SHOULD, SHOULD NOT, MUST, and MUST NOT language so implementers could distinguish non-compliance (fails at least one MUST or MUST NOT), conditional compliance (passes all MUSTs and MUST NOTs, but fails at least one SHOULD or SHOULD NOT), and compliance (passes all). The problem here is that Rspec already uses &lt;code&gt;should&lt;/code&gt; to mean what RFCs call MUST.&lt;/p&gt;

&lt;p&gt;The other improvement that should be made to &lt;code&gt;multi_json&lt;/code&gt;, and is important for all interfaces, is very thorough API documentation. &lt;code&gt;multi_json&lt;/code&gt; only defines two methods, but the API contains more than method names. Parameter types, return types, possible exceptions, yielded block, yield params, and yield return values are all important. The current documentation for &lt;code&gt;#encode&lt;/code&gt; and &lt;code&gt;#decode&lt;/code&gt; looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Decode a JSON string into Ruby.
#
# &lt;b&gt;Options&lt;/b&gt;
#
# &lt;tt&gt;:symbolize_keys&lt;/tt&gt; :: If true,
#    will use symbols instead of strings
#    for the keys.
def decode(string, options = {})
...

# Encodes a Ruby object as JSON.
def encode(object)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It should probably look like the following (and, yes, I will be submitting a patch):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Decode a JSON String into a Ruby Object.
#
# @param [String] string the object in JSON representation
# @param [Hash, nil] options additional options
# @option options [true, false] :symbolize_keys if true,
#         will use Symbols instead of Strings for Hash keys
# @return [Object] a Ruby Object
# @since 0.0.1
def decode(string, options = {})
...

# Encodes a Ruby Object as a JSON String
# 
# @param [Hash, Array, Numeric, String, Symbol, #to_json] object a
#        Ruby Object to be converted to JSON. Each object in
#        the graph must be a Hash, Array, Numeric, String, or Symbol
#        or declare a #to_json instance method.
# @raise [NoMethodError] if any object in the graph cannot be
#        translated into JSON.
# @return [String] the Ruby Object as JSON
# @since 0.0.1
def encode(object)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Ideal Candidates&lt;/h2&gt;

&lt;p&gt;Good candidates for interfaces tend to meet the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provide functionality that is used by libraries more than applications&lt;/li&gt;
&lt;li&gt;some external definition of correctness of functionality&lt;/li&gt;
&lt;li&gt;the potential for or existence of a variety of implementations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The three categories that come most easily to mind are formats (JSON, YAML, Markdown), protocols (LDAP, OpenID, HMAC, AES), and API clients (Twitter, Flickr, Delicious).&lt;/p&gt;

&lt;p&gt;(&lt;strong&gt;Later&lt;/strong&gt;: not ten minutes after posting this, I came across &lt;a href="http://github.com/wycats/moneta"&gt;Yehuda Katz’s Moneta&lt;/a&gt;, an interface for key/value stores. Like &lt;code&gt;multi_json&lt;/code&gt;, &lt;code&gt;moneta&lt;/code&gt; is a great example of a bridge gem, though it could use some of the same improvements that &lt;code&gt;multi_json&lt;/code&gt; could, namely providing implementers a test suite and having thorough documentation on the API.)&lt;/p&gt;

&lt;h2&gt;On Stifling Innovation&lt;/h2&gt;

&lt;p&gt;One criticism of interfaces is that they stifle innovation. It’s harder for me to write a truly new way of using LDAP if I have to comply with an interface. This is certainly a valid concern. The mitigation strategy should be extreme care in writing and curating interfaces. Don’t write one unless there’s an obvious need, and when you do write one, require only as much as you truly need for basic functionality. People can always depend on a specific implementation if they need more than the basics.&lt;/p&gt;</description><link>http://jamesarosen.com/post/783132142</link><guid>http://jamesarosen.com/post/783132142</guid><pubDate>Wed, 07 Jul 2010 21:04:00 -0400</pubDate></item><item><title>Power-Law Distributions and Code Metrics</title><description>&lt;h3&gt;Existing Metrics Tools&lt;/h3&gt;

&lt;p&gt;The Ruby community has a wealth of tools to measure code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://saikuro.rubyforge.org/"&gt;Saikuro&lt;/a&gt; for &lt;a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity"&gt;cyclomatic complexity&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://rubyforge.org/projects/rcov/"&gt;rcov&lt;/a&gt; for code coverage&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://github.com/danmayer/churn"&gt;churn&lt;/a&gt; for finding code that changes a great deal over time&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://ruby.sadi.st/Flog.html"&gt;Flog&lt;/a&gt; for finding complex code&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://ruby.sadi.st/Flay.html"&gt;Flay&lt;/a&gt; for finding duplication in code&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://metric-fu.rubyforge.org/"&gt;MetricFu&lt;/a&gt;, which packages many of the other metric tools together&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The Power-Law Problem&lt;/h3&gt;

&lt;p&gt;The underlying behavior that these tools measure tend to lie in &lt;a href="http://en.wikipedia.org/wiki/Power_law"&gt;power-law&lt;/a&gt; distributions. Perhaps 80% of your classes have 100% (or near-100%) code coverage, but 20% have less than 50%. Or maybe the cyclomatic complexity of 80% of your methods is less than 5, but 20% of them have a complexity greater than 16. The problem is that describing the mean or median of a power-law distribution is nearly meaningless. The reason is that the small number of instances at the head of the distribution share very little in common with the many that compose the tail.&lt;/p&gt;

&lt;p&gt;The problem, thus, is not that we’re measuring the wrong things, but that we’re reporting the wrong measurements. If you look at a graph of mean-code-complexity over time and see that it is slowly but steadily getting worse (increasing), you can’t tell whether the bulk of your code is getting worse or whether there’s one particular module that is accruing all the &lt;a href="http://en.wikipedia.org/wiki/Technical_debt"&gt;technical debt&lt;/a&gt;. In the former case, you almost certainly have a communication or process problem; in the latter case, you might be doing everything perfectly and simply have to plan for a future refactoring.&lt;/p&gt;

&lt;h3&gt;The Reporting Solution&lt;/h3&gt;

&lt;p&gt;Instead, power-law distributions are better described by the mean and/or median of the “head” (generally 20%) and “tail” (generally 80%) groups. Thus, each metric should have three configuration options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;bulk_delimiter&lt;/code&gt;: the dividing line between the “head” and “tail” groups. Defaults to 20%.&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;head_min&lt;/code&gt;, &lt;code&gt;head_max&lt;/code&gt;, or &lt;code&gt;head_range&lt;/code&gt; for the ideal values for the “head” group. Default depends on the metric in question, but might be &lt;code&gt;&lt;= 50&lt;/code&gt; for Flog.&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;tail_min&lt;/code&gt;, &lt;code&gt;tail_max&lt;/code&gt;, or &lt;code&gt;tail_range&lt;/code&gt; for the ideal values for the “tail” group. Default depends on the metric in question, but might be &lt;code&gt;&lt;= 10&lt;/code&gt; for Flog.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Implications&lt;/h3&gt;

&lt;p&gt;What we have to change: the reporting of metrics we’re collecting. MetricFu’s graphs should have two lines for each metric. RCov’s coverage reports should have a divider between the “head” and “tail” groups and summary information for each.&lt;/p&gt;

&lt;p&gt;What we &lt;em&gt;don’t&lt;/em&gt; have to change: the collection methods. The hard part of these metrics libraries is the instrumentation of code and collection of data. Fortunately, we don’t have to change any of that. (NB: I haven’t actually read through &lt;em&gt;all&lt;/em&gt; of the code for the above projects, so this isn’t actually a guarantee. I can, however, be fairly certain that we can keep at least 80% of the code :) )&lt;/p&gt;</description><link>http://jamesarosen.com/post/677221793</link><guid>http://jamesarosen.com/post/677221793</guid><pubDate>Tue, 08 Jun 2010 13:53:00 -0400</pubDate></item><item><title>The Stuff of Stuff</title><description>&lt;p&gt;A beautifully-laid out piece of analysis from Steven Pinker’s &lt;a href="http://www.amazon.com/Stuff-Thought-Language-Window-Nature/dp/0143114247"&gt;The Stuff of Thought&lt;/a&gt; on how the brain models &lt;em&gt;stuff&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
At first glance, the conceptual distinction between an object and a
substance seems to be captured in the linguistic distinction between a
count noun and a mass noun.  Count nouns, like “apple” and “pebble,”
tend to be used for bounded hunks of matter. Mass nouns, like
“applesauce” and “gravel,” tend to be used for substances without
their own boundaries.  The two types of nouns are sharply
distinguished by the grammar of English.  We can enumerate and
pluralize count nouns, but not mass nouns.  When we refer to
quantities, we have to use different quantifying words: “a pebble” is
fine, but “a gravel” is not.  We talk about “many pebbles” but not
“many gravel,” and we talk about “much gravel” but not “much pebble”
or “much pebbles.”  And mass nouns can appear in public naked: “gravel
is expensive,” “I like gravel.”  Whereas count nouns generally cannot:
“pebble is expensive,” “I like pebble.”

An important clue to the mental model behind mass nouns is that, in
some ways, they act like plurals of count nouns.  They share some of
their quantifiers: “more applesauce,” “more pebbles.”  Or their
ability to appear naked in a sentence: “I like applesauce,” “I like
pebbles.”  And their ability to appear with spatial words like “all
over,” as in “applesauce was all over the floor,” and “pebbles were
all over the floor.”
&lt;/p&gt;
&lt;p&gt;
The overlap in grammar reflects a similarity in the way we conceive of
substances.  The things typically labelled with mass nouns and
multitudes, the things typically labelled with plurals, which,
together, may be called “aggregates.”  Substances and multitudes both
lack intrinsic boundaries and can spill out into any shape.  They can
coalesce: put some pebbles together with some pebbles and you still
get pebbles, put some applesauce together with some applesauce and you
still get applesauce.  And they can be divided.  None of this is true
of the typical referent of a count noun, like a horse.  No one is in
doubt as to the boundary where a horse leaves off, and when you cut a
horse in half, the result is not a horse.
&lt;/p&gt;
&lt;p&gt;
Where a plural differs from a mass noun is that it is conceived as a
set of individuals which can be identified and counted.  A singular
count noun like “pebble” stands for something that is bounded —
delineated by a fixed shape, and not made up of individuals.  A
plural, like “pebbles,” stands for something that is unbounded and
made up of individuals.  A mass noun, like “applesauce,” stands for
something that is neither bounded nor made up of individuals.  All
this suggests that our basic concepts about matter are not the
concepts “count” and “mass,” but the mini-concepts, “bounded,” and
“made up of individuals.”  If so, we should see a fourth possibility:
things that are both bounded and composed of individuals.  We do,
indeed.  These are collective nouns, such as “committee,” “bouquet,”
and “rock band.”
&lt;/p&gt;
&lt;p&gt;
It’s not just nouns that care about boundedness and individuals.
Verbs do, too.  As we saw earlier, verbs like “pour” require
aggregates, like “water” or “pebbles.”  Verbs like “smear” and
“streak” apply to substances.  And verbs like “scatter” and “collect”
apply to multitudes.  This is because the concept of an action depends
on the number and kind of things it affects — as in the difference
between “eat” and “drink,” “throw” and “scatter,” “murder” and
“massacre.”
&lt;/p&gt;
&lt;p&gt;
The intuitive materials science behind the count/mass distinction
assumes a Play-Dough world in which objects are molded out of the
substance.  Rocks are made of rock, glasses are made of glass, beers
are made of beer, cats are made of cat.  The model breaks down when an
object can’t be construed as having been formed from a scoop of
material.  A television isn’t made out of something called
“television,” so we can’t say that, “a steamroller left television all
over the road.”  The distinction also breaks down when we put a
substance under a powerful enough microscope.  We use the word “rice”
to refer to a cup of it, a grain of it, or even a fragment of a grain
of it, but as we zoom closer and closer, we reach a point at which we
aren’t seeing rice any more.  Perhaps if humans could see the
crystals, fibers, cells, and atoms making up matter, we would never
have developed a count/mass distinction in the first place.
&lt;/p&gt;
&lt;p&gt;
The count/mass distinction in our minds is not just unfettered by the
object/substance distinction in the world.  It is unfettered by the
physical world altogether.  It is best thought of as cognitive lens or
attitude by which the mind can construe almost anything as a bounded,
countable item, or as a boundryless, continuous medium.  We see this
in a distinctive kind of mass noun that does what count nouns usually
do.  These are mass hypernyms — superordinates, such as “furniture,”
“fruit,” “clothing,” “mail,” “toast,” and “cutlery.”  Though they
don’t refer to a substance — chairs and tables aren’t made out of
“furniture” — the words can’t refer directly to the individual
objects they stand for, either.  They require a special classifier
noun, as in “a stick of furniture,” “an article of clothing,” or the
general-purpose classifier “piece.”
&lt;/p&gt;
&lt;/blockquote&gt;</description><link>http://jamesarosen.com/post/615292181</link><guid>http://jamesarosen.com/post/615292181</guid><pubDate>Thu, 20 May 2010 00:54:57 -0400</pubDate></item><item><title>Holy Jeebus!: Saddened by the heat @lessconf is taking over its speaker lineup</title><description>&lt;a href="http://b.holyjeeb.us/post/614134461/saddened-by-the-heat-lessconf-is-taking-over-its"&gt;Holy Jeebus!: Saddened by the heat @lessconf is taking over its speaker lineup&lt;/a&gt;: &lt;blockquote&gt;
&lt;p&gt;I was just perusing some of the tweets on the @lessconf twitter stream and started backing up through some of the conversations. Jeebus, it’s crazy-town out there. Several people called the guys at Less out for the fact that the speaker lineup for LessConf is all-male and all-white. Additional…&lt;/p&gt;
&lt;/blockquote&gt;</description><link>http://jamesarosen.com/post/614842859</link><guid>http://jamesarosen.com/post/614842859</guid><pubDate>Wed, 19 May 2010 22:13:13 -0400</pubDate></item><item><title>"The discovery, in the early Web browsers, that reasonably-typeset text [that] embedded simple forms..."</title><description>“The discovery, in the early Web browsers, that reasonably-typeset text [that] embedded simple forms and hyperlinks, and came equipped with a “Back” button, hit the biggest 80/20 point ever in the history of User Interfaces, couldn’t have been predicted by anybody; but it’s as true today as ever.”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;Tim Bray, “&lt;a href="http://www.tbray.org/ongoing/When/201x/2010/05/05/HTML5-and-the-Web"&gt;HTML5 and the Web&lt;/a&gt;”&lt;/em&gt;</description><link>http://jamesarosen.com/post/579701852</link><guid>http://jamesarosen.com/post/579701852</guid><pubDate>Fri, 07 May 2010 18:22:00 -0400</pubDate></item><item><title>I'm a Poet?</title><description>&lt;blockquote&gt;When a poet’s mind is perfectly equipped for its work, it is constantly amalgamating disparate experience; the ordinary man’s experience is chaotic, irregular, fragmentary. The latter falls in love, or reads Spinoza, and these two experiences have nothing to do with each other, or with the noise of the typewriter, or the smell of cooking; in the mind of the poet these experiences are always forming new wholes.&lt;/blockquote&gt;

&lt;p&gt;— &lt;cite&gt;&lt;a href="http://en.wikipedia.org/wiki/T._S._Eliot"&gt;T. S. Eliot&lt;/a&gt;, &lt;i&gt;&lt;a href="http://www.wright.edu/cola/Dept/ENG/limouze/eliotmetaphys.htm"&gt;The Metaphysical Poets&lt;/a&gt;&lt;/i&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;p&gt;This sounds a lot like me. Am I a poet? Why can’t I write any poetry? Or does “poet” and “poetry” mean something broader and the &lt;a href="http://github.com/jamesarosen"&gt;work I do&lt;/a&gt; &lt;em&gt;is&lt;/em&gt; poetry? Or is Eliot wrong?&lt;/p&gt;

&lt;p&gt;I like to &lt;a href="http://abstract-me.livejournal.com/353705.html"&gt;steal things from writers&lt;/a&gt;.&lt;/p&gt;</description><link>http://jamesarosen.com/post/461408160</link><guid>http://jamesarosen.com/post/461408160</guid><pubDate>Sat, 20 Mar 2010 14:22:00 -0400</pubDate></item><item><title>How Bose Lost a Customer for Life</title><description>&lt;p&gt;A few years ago, my father gave me a pair of &lt;a href="http://www.bose.com/"&gt;Bose&lt;/a&gt;’s QC-2 headphones. They were &lt;em&gt;awesome&lt;/em&gt;. I wore them while working at coffee shops, on airplanes, and to get the most out of my Brahms and Decemberists.&lt;/p&gt;

&lt;p&gt;Last week, alas, I broke a piece of plastic on one side of the headphones. It’s the bit that slides along the headband and swivels. It’s a pretty key element, especially given that piece of plastic cost Bose less than 8¢ and they charged $300 for the headphones. I knew the headphones were no longer under warranty, but I figured I could buy a replacement. I would’ve been happy to pay, say $10 for this 8¢ piece of plastic.&lt;/p&gt;

&lt;p&gt;Bose, however, wouldn’t settle for a profit margin of 12,400%. Nope. The only option they’re willing to give me is to trade in my QC2s along with $100 for a new pair of QC15s. (When I told them I wasn’t interested, they replied, “Apologies, but unfortunately the part you are looking for is not something we have available to sell individually.” Does that mean that actually &lt;em&gt;do&lt;/em&gt; have them but are unwilling to sell them?) I’m sure the QC15s are quite spiffy, but my QC2s have a lot of life left in them — if only I could get this part.&lt;/p&gt;

&lt;p&gt;Not only do they lose me as a customer, though, they also make an enemy. I’m going to work very hard to build a computer model of the part so that I and others can &lt;a href="http://en.wikipedia.org/wiki/3D_printing"&gt;print their own&lt;/a&gt;. Hopefully that will put a dent, however small, in their profits. Every time I wear my headphones out, people ask me what I think of them. I used to give glowing reviews. Now I’ll have nothing but vitriol to offer.&lt;/p&gt;

&lt;p&gt;And a final suggestion to every consumer products company out there: if the functionality of your $300 product can be lost for want of an 8¢ piece of plastic, order &lt;em&gt;lots&lt;/em&gt; of extras. Send one or two along with the original product and make it easy to order more. Make it &lt;em&gt;dead simple&lt;/em&gt; for your customers to like you instead of hate you.&lt;/p&gt;

&lt;p&gt;&lt;ins&gt;(&lt;em&gt;Later&lt;/em&gt;: I do have to give Bose credit for one thing: they responded quickly, both via email and Twitter. Their responses were ridiculous, but at least they didn’t leave me wondering.)&lt;/ins&gt;&lt;/p&gt;</description><link>http://jamesarosen.com/post/395098521</link><guid>http://jamesarosen.com/post/395098521</guid><pubDate>Wed, 17 Feb 2010 14:27:00 -0500</pubDate></item><item><title>Trash Day and the Fall of Civilization</title><description>&lt;p&gt;Today is trash day. That’s the day wherein we gather up all of our banana peels, used dental floss, socks with holes in them, and broken plant pots and put them on the curb for the nice garbage men&lt;sup&gt;1&lt;/sup&gt; to collect it. That’s the good part.&lt;/p&gt;

&lt;p&gt;Today is also recycling day. That’s the day wherein we gather up all of our wine bottles, empty dental floss containers, and news-magazines-we-never-got-around-to-reading-but-now-we’ve-gotten-the-next-issue-so-why-keep-the-old-one-around-s and put them on the curb for the nice recycling men&lt;sup&gt;1&lt;/sup&gt; to collect. The intent is good here — the less plastic that makes its way into the &lt;a href="http://en.wikipedia.org/wiki/Great_Pacific_Garbage_Patch"&gt;Pacific Gyre&lt;/a&gt;, the better off our planet is.&lt;/p&gt;

&lt;p&gt;The problem is that in Massachusetts we have a bottle deposit. When I buy a can of Coke or a bottle of Duvel, I pay an extra 5¢ to the state. Then, if I return the empty bottle, I get my 5¢ back. Sometimes I forget about a bottle and it goes into the normal recycling pile. No problem, I’m not worried about the occasional 5¢ and I’m happy for the state to keep it to work down its massive budget deficits.&lt;/p&gt;

&lt;p&gt;Except the state doesn’t get it. Every trash day there are roving bands of people with shopping carts who go around and poke through the trash and recycling looking for stray soda and beer bottles. I don’t begrudge them poking through my detritus. I sure don’t want it any more. What I do hate is that they tear open bags of trash and leave them mixed in with the recycling. Now &lt;em&gt;everything&lt;/em&gt; has to go into the garbage truck and there’s nothing left for the recycling people to pick up. All my effort of separating my garbage so the human race can live on Earth for an extra 0.0002 seconds is for naught.&lt;/p&gt;

&lt;p&gt;All for 5¢.&lt;/p&gt;

&lt;p&gt;[1] the three who do my street are all men. I’m sure this isn’t a global phenomenon.&lt;/p&gt;</description><link>http://jamesarosen.com/post/381950185</link><guid>http://jamesarosen.com/post/381950185</guid><pubDate>Wed, 10 Feb 2010 10:51:06 -0500</pubDate></item><item><title>Staff Bio</title><description>&lt;p&gt;Some day I hope to have a bio as creative as the ones on the &lt;a href="http://bandcamp.com/faq#whowebe"&gt;Bandcamp FAQ&lt;/a&gt;. For example:&lt;/p&gt;

&lt;blockquote&gt;
Shawn Grunberger helped negotiate the Peace of Westphalia in 1648. It was nontrivial.
&lt;/blockquote&gt;</description><link>http://jamesarosen.com/post/371170970</link><guid>http://jamesarosen.com/post/371170970</guid><pubDate>Thu, 04 Feb 2010 17:34:25 -0500</pubDate></item><item><title>Chuck Lorre Productions, #275: Ask Chuck!</title><description>&lt;p&gt;&lt;blockquote&gt;Dear Chuck,&lt;br/&gt;
At a recent dinner party, I found myself in an awkward situation when the host, a devout atheist, sneezed between spoonfuls of his gazpacho. Without thinking, I said, “God bless you.” He gave me a withering look and said, as if to a child, “Golly gee, I sure hope he does.” The other guests exploded with laughter, while I imploded with humiliation. To avoid future embarrassment, what is the correct response when an atheist sneezes?
Troubled with ahchoo&lt;br/&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
Dear Troubled,&lt;br/&gt;
First, a little background information. Saying “God bless you” following a sneeze is thought by some to have originated in the sixth century in order to protect the sneezer from falling ill to the bubonic plague. Another possible origin is that people once believed that the devil entered the body during a sneeze and saying “God bless you” could help ward him off. Since the plague has killed something like two hundred million people and the words “God bless you” have, in all likelihood, been said countless times to Glenn Beck, we can safely assume the phrase has no real power against germs or demonic possession. What it does contain is simple human courtesy — a means by which we express concern for one another. As to how to respond to a sneezing atheist, well, that’s easy. Simply say, “Sounds like you’re coming down with something; I hope you don’t die and rot in a box.”
&lt;/blockquote&gt;&lt;/p&gt;</description><link>http://jamesarosen.com/post/367104885</link><guid>http://jamesarosen.com/post/367104885</guid><pubDate>Tue, 02 Feb 2010 11:21:02 -0500</pubDate></item><item><title>An Open Letter to Frank D. Wagner</title><description>&lt;p&gt;An open letter to &lt;a href="http://en.wikipedia.org/wiki/Frank_D._Wagner"&gt;Frank D. Wagner&lt;/a&gt;, Reporter of Decisions of the Supreme Court of the United States:&lt;/p&gt;

&lt;p&gt;Dear Mr. Wagner:&lt;/p&gt;

&lt;p&gt;In reading through the &lt;a href="http://www.scotusblog.com/wp-content/uploads/2010/01/citizens-opinion.pdf"&gt;slip opinion&lt;/a&gt; of &lt;i&gt;Citizens United v. Federal Election Commission&lt;/i&gt;, I noticed a grammatical error. Associate Justice Kennedy, writing for the majority, quotes &lt;i&gt;McConnell, supra&lt;/i&gt;, as follows:&lt;/p&gt;

&lt;blockquote&gt;Section 311 disclaimers provide information to the 
electorate, &lt;i&gt;McConnell, supra&lt;/i&gt;, at 196, and “insure that the voters are 
fully informed” about who is speaking… (&lt;i&gt;Citizens United&lt;/i&gt;, Syllabus, page 7)
&lt;/blockquote&gt;

&lt;p&gt;The meaning of &lt;i&gt;insure&lt;/i&gt; is generally taken to be “protect against financial loss” rather than “guarantee.” The original author, Associate Justice Stevens, likely meant that the disclaimers “ensure that the voters are fully informed.” The error is repeated verbatim in the opinion on page 52. Both quotes in &lt;i&gt;Citizens United&lt;/i&gt; should include “[sic]” indicating the original misuse.&lt;/p&gt;

&lt;p&gt;Sincerely,&lt;/p&gt;

&lt;p&gt;James Alexander Rosen&lt;/p&gt;</description><link>http://jamesarosen.com/post/358291149</link><guid>http://jamesarosen.com/post/358291149</guid><pubDate>Thu, 28 Jan 2010 14:47:00 -0500</pubDate></item><item><title>"Under the majority’s view, I suppose it may be a First Amendment problem that corporations are not..."</title><description>“Under the majority’s view, I suppose it may be a First Amendment problem that corporations are not permitted to vote, given that voting is, among other things, a form of speech.”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;John Paul Stevens, in &lt;i&gt;&lt;a href="http://www.scotusblog.com/wp-content/uploads/2010/01/citizens-opinion.pdf"&gt;Citizens United v. Federal Election Commission&lt;/a&gt;&lt;/i&gt;&lt;/em&gt;</description><link>http://jamesarosen.com/post/358155368</link><guid>http://jamesarosen.com/post/358155368</guid><pubDate>Thu, 28 Jan 2010 12:47:09 -0500</pubDate></item><item><title>"But your children aren’t really yours. They have lives of their own. So when your designs do..."</title><description>“But your children aren’t really yours. They have lives of their own. So when your designs do change the world, you have to accept it. You have to say, ‘OK, this was such a good idea, other people took it and ran with it. I win.’”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;Wil Shipley, as quoted in &lt;a href="http://www.washingtonpost.com/wp-dyn/content/article/2010/01/27/AR2010012704221.html"&gt;The Washington Post&lt;/a&gt;&lt;/em&gt;</description><link>http://jamesarosen.com/post/357969576</link><guid>http://jamesarosen.com/post/357969576</guid><pubDate>Thu, 28 Jan 2010 09:56:08 -0500</pubDate></item><item><title>"If the problem has a solution, worrying is pointless, in the end the problem will be solved. If the..."</title><description>“If the problem has a solution, worrying is pointless, in the end the problem will be solved. If the problem has no solution, there is no reason to worry, because it can’t be solved.”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;Zen proverb&lt;/em&gt;</description><link>http://jamesarosen.com/post/356211732</link><guid>http://jamesarosen.com/post/356211732</guid><pubDate>Wed, 27 Jan 2010 10:01:47 -0500</pubDate></item><item><title>dancroak:

Electoral College reform. New states have roughly...</title><description>&lt;img src="http://30.media.tumblr.com/tumblr_kwte4bjTlQ1qz5x9po1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;&lt;a href="http://dancroak.com/post/352868687/electoral-college-reform-new-states-have-roughly" class="tumblr_blog"&gt;dancroak&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;a href="http://www.fakeisthenewreal.org/reform/"&gt;Electoral College reform&lt;/a&gt;. New states have roughly even populations around 5,617,000 according to the 2000 US Census.&lt;/p&gt;&lt;/blockquote&gt;</description><link>http://jamesarosen.com/post/352872572</link><guid>http://jamesarosen.com/post/352872572</guid><pubDate>Mon, 25 Jan 2010 13:06:06 -0500</pubDate></item><item><title>OSX + Multiple Network Locations + Proxies</title><description>&lt;p&gt;At work I’m behind a proxy. At home I’m not. OSX provides a nice way to handle that problem: multiple network &lt;em&gt;locations&lt;/em&gt;. You can create a location (“work”) with some proxy settings and one (“home”) without, then simply switch between them as you commute back and forth.&lt;/p&gt;

&lt;p&gt;Those proxy settings work great for Safari, iChat, and iTunes. They work reasonably well for Firefox (using &lt;a href="http://systemproxy.mozdev.org/"&gt;SystemProxy&lt;/a&gt;). What they absolutely &lt;strong&gt;don’t&lt;/strong&gt; work for is scripts run in Terminal. Rubygems? Nope. Git? Nope. Curl? Nope.&lt;/p&gt;

&lt;p&gt;The normal solution is to put the following in your &lt;code&gt;~/.bash_profile&lt;/code&gt; (assuming you’re a &lt;code&gt;bash&lt;/code&gt; person like me):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;export HTTP_PROXY='http://proxy.mycompany.com:80'
export http_proxy=$HTTP_PROXY
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That works great at work, but not so well at home. Instead, I recommend this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;export HTTP_PROXY=system_profiler SPNetworkDataType|grep "HTTP Proxy Server"|awk {'sub(/^.*:[ \t]*/, "", $0); print $0;'}
export http_proxy=$HTTP_PROXY
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You’ll have to either open a new Terminal tab or run &lt;code&gt;source ~/.bash_profile&lt;/code&gt; after you switch locations, but that seems easier than resetting each variable. Does anyone know a way to delay the binding so that when scripts ask for &lt;code&gt;$http_proxy&lt;/code&gt; that function gets re-evaluated? If so, please tweet me &lt;a href="http://twitter.com/jamesarosen"&gt;@jamesarosen&lt;/a&gt;.&lt;/p&gt;</description><link>http://jamesarosen.com/post/346570628</link><guid>http://jamesarosen.com/post/346570628</guid><pubDate>Thu, 21 Jan 2010 18:20:07 -0500</pubDate></item><item><title>A Fast Random Lookup in SQL with Conditions.</title><description>&lt;p&gt;If you want a random element from a table from ActiveRecord, you might be tempted to do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def MyModel.random
  find :first, :order =&gt; 'RAND()'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That has two unfortunate problems: it’s &lt;em&gt;unbearably&lt;/em&gt; slow for large tables, and it doesn’t work on all databases. To fix those problems, you might try the technique from &lt;a href="http://github.com/hgimenez/fast_random"&gt;hgimenez’s fast_random&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def MyModel.random
  if (c = self.count) == 0
    nil
  else
    find :first, :offset =&gt; rand(c)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That works great… until you want to add some conditions. Let’s say you want a random element created in the last two weeks. You might try this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def MyModel.random_in_last(since = nil)
  if (c = self.count) == 0
    nil
  else
    since ||= 2.weeks.ago
    find :first, :conditions =&gt; ["created_at &gt; ?", since],
                 :offset =&gt; rand(c)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Unfortunately, this goes &lt;strong&gt;bang&lt;/strong&gt;! The problem is that the &lt;code&gt;offset&lt;/code&gt; gets applied &lt;em&gt;first&lt;/em&gt;, and can point to a row that the &lt;code&gt;conditions&lt;/code&gt; exclude. You need to execute the &lt;code&gt;conditions&lt;/code&gt; in a sub-query so that by the time the &lt;code&gt;offset&lt;/code&gt; is applied, all rows are valid:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def MyModel.random_in_last(since = nil)
  if (c = self.count) == 0
    nil
  else
    since ||= 2.weeks.ago
    find :first, :select =&gt; 'recent_models.*',
                 :from =&gt; "(select * from `#{self.table_name}` where " +
                            sanitize_sql_array(['created_at &gt; ?', since]) +
                          ") as recent_models",
                 :offset =&gt; rand(c))
  end
end
&lt;/code&gt;&lt;/pre&gt;</description><link>http://jamesarosen.com/post/341011059</link><guid>http://jamesarosen.com/post/341011059</guid><pubDate>Mon, 18 Jan 2010 10:37:47 -0500</pubDate></item><item><title>Getting Rails 3 (pre) up on OSX</title><description>&lt;p&gt;I sat down to work on the most recent &lt;a href="http://bugmash.com/" title="Railsbridge Bugmash January 2010"&gt;Bugmash&lt;/a&gt;. First I followed &lt;a href="http://yehudakatz.com/2009/12/31/spinning-up-a-new-rails-app/" title="Spinning up a new Rails app"&gt;Yehuda Katz’s instructions&lt;/a&gt; on getting started with Rails 3. Those instructions worked great for running an app on Rails 3, but they’re not quite sufficient for working on Rails itself. Here are some pointers for that, as well as a few unresolved issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the &lt;a href="http://dev.mysql.com/downloads/mysql/5.1.html"&gt;latest MySQL&lt;/a&gt; using the DMG version. By default, it installs into &lt;code&gt;/usr/local/mysql&lt;/code&gt;, which is fine.&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="http://www.enterprisedb.com/products/pgdownload.do#osx"&gt;latest PostgreSQL&lt;/a&gt; also using the DMG version. By default, it installs into &lt;code&gt;/Library/PostgreSQL&lt;/code&gt;, which is fine.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;/Library/PostgreSQL/bin&lt;/code&gt; to your &lt;code&gt;$PATH&lt;/code&gt;. You probably want to do this in your &lt;code&gt;~/.bash_profile&lt;/code&gt; or equivalent.&lt;/li&gt;
&lt;li&gt;Go to the directory in which you &lt;code&gt;clone&lt;/code&gt;d Rails and add a &lt;code&gt;build_options.yml&lt;/code&gt; file. In it, place the following:
&lt;script src="http://gist.github.com/279443.js?file=build_options.yml"&gt;&lt;/script&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;gem bundle -b build_options.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;rake&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Most&lt;/em&gt; of the tests run so far, but I’m still having trouble getting Rails 3 to actually connect to the MySQL and PostgreSQL databases. I’ll have to work on that.&lt;/p&gt;</description><link>http://jamesarosen.com/post/339319063</link><guid>http://jamesarosen.com/post/339319063</guid><pubDate>Sun, 17 Jan 2010 11:49:00 -0500</pubDate></item><item><title>Lessons in Toilet Paper Engineering</title><description>&lt;p&gt;First, a wonderful &lt;a href="http://currentconfig.com/images/overisright_hanger.pdf" title="Overhanging: Right for You, Right for Everyone"&gt;brochure&lt;/a&gt; created by Chris Rugen, author of an equally informative &lt;a href="http://currentconfig.com/2005/02/22/essential-life-lesson-1-over-is-right-under-is-wrong/" title="Essential Life Lesson #1: Over is Right, Under is Wrong"&gt;blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Second, &lt;a href="http://www.squidoo.com/ToiletPaperRollOverOrUnder" title="Should The Toilet Paper Roll Go on Over or Under?"&gt;two&lt;/a&gt; &lt;a href="http://www.squidoo.com/overorunder" title="OVER or UNDER: Which is correct for a roll of toilet paper?"&gt;different&lt;/a&gt; Squidoo pages.&lt;/p&gt;

&lt;p&gt;Third, a new &lt;a href="http://www.cottonellerollpoll.com/" title="Roll Poll"&gt;poll&lt;/a&gt; (read: &lt;em&gt;marketing campaign&lt;/em&gt;) by Cottonelle.&lt;/p&gt;

&lt;p&gt;The evidence is clear, folks. Friends don’t let friends hang toilet paper under.&lt;/p&gt;</description><link>http://jamesarosen.com/post/336693952</link><guid>http://jamesarosen.com/post/336693952</guid><pubDate>Fri, 15 Jan 2010 21:47:52 -0500</pubDate></item><item><title>Wine Review: 2006 Calatrava Sauvignon Blanc</title><description>&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;td&gt;★✩✩✩✩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Winery&lt;/th&gt;
&lt;td&gt;Calatrava&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Vintage&lt;/th&gt;
&lt;td&gt;2006&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;td&gt;Colchagua Valley, Chile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Color&lt;/th&gt;
&lt;td&gt;White&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;td&gt;Sauvignon Blanc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Grape&lt;/th&gt;
&lt;td&gt;Sauvignon blanc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Price Paid&lt;/th&gt;
&lt;td&gt;$4.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Good for&lt;/th&gt;
&lt;td&gt;foisting off on others at college house parties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Bad for&lt;/th&gt;
&lt;td&gt;a bread and cheese plate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Smells a bit of acetone and citrus rind. The most redeeming thing I can say about this wine is that I can drink a full glass of it without feeling ill. Oh, and it does actually have a rather nice heavy texture. Unfortunately, enjoying the texture means you have to taste the wine longer…&lt;/p&gt;</description><link>http://jamesarosen.com/post/327762992</link><guid>http://jamesarosen.com/post/327762992</guid><pubDate>Sun, 10 Jan 2010 19:37:50 -0500</pubDate></item></channel></rss>
