<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
  <title>Jack Christensen</title>
  <link href="http://www.jackchristensen.com/"/>
  <link type="application/atom+xml" rel="self" href="http://www.jackchristensen.com/atom.xml"/>
  <updated>2012-02-16T19:19:07-06:00</updated>
  <id>http://www.jackchristensen.com/</id>
  <author>
    <name>Jack Christensen</name>
    <email>jack@jackchristensen.com</email>
  </author>

  
  <entry>
    <id>http://www.jackchristensen.com/2012/02/16/surus-postgresql-extensions-for-active-record</id>
    <link type="text/html" rel="alternate" href="http://www.jackchristensen.com/2012/02/16/surus-postgresql-extensions-for-active-record.html"/>
    <title>Surus - PostgreSQL Acceleration for ActiveRecord</title>
    <updated>2012-02-16T00:00:00-06:00</updated>
    <summary type="text">Accelerate ActiveRecord with PostgreSQL specific support</summary>
    <author>
      <name>Jack Christensen</name>
      <uri>http://www.jackchristensen.com/</uri>
    </author>
    <content type="html">&lt;p&gt;Surus accelerates ActiveRecord with PostgreSQL specific types and functionality. It enables indexed searching of serialized arrays and hashes. It also can control PostgreSQL synchronous commit behavior. By relaxing PostgreSQL&amp;#8217;s durability guarantee, transaction commit rate can be increased by 50% or more.&lt;/p&gt;

&lt;h1 id='installation'&gt;Installation&lt;/h1&gt;
&lt;pre class='shell'&gt;
gem install surus
&lt;/pre&gt;
&lt;p&gt;Or add to your Gemfile.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;gem&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;surus&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id='hstore'&gt;Hstore&lt;/h1&gt;

&lt;p&gt;Hashes can be serialized to an hstore column. hstore is a PostgreSQL key/value type that can be indexed for fast searching.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
  &lt;span class='n'&gt;serialize&lt;/span&gt; &lt;span class='ss'&gt;:properties&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Surus&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Hstore&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Serializer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;create&lt;/span&gt; &lt;span class='ss'&gt;:properties&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:favorite_color&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;green&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:results_per_page&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;20&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;create&lt;/span&gt; &lt;span class='ss'&gt;:properties&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:favorite_colors&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;green&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;blue&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;red&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Even though the underlying hstore can only use strings for keys and values (and NULL for values) Surus can successfully maintain type for integers, floats, bigdecimals, dates, and any value that YAML can serialize. It does this by storing an extra key value pair (or two) to maintain type information.&lt;/p&gt;

&lt;p&gt;Because it falls back to YAML serialization for complex types, this means that nested data structures can be serialized to an hstore. In other words, any hash that can be serialized with the normal Rails YAML serialization can be serialized with Surus. But you can get the benefits of PostgreSQL indexing on the top level keys and values for free.&lt;/p&gt;

&lt;p&gt;Hstores can be searched with helper scopes.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;hstore_has_pairs&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:properties&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;favorite_color&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;green&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;hstore_has_key&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:properties&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;favorite_color&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;hstore_has_all_keys&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:properties&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;favorite_color&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;gender&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;hstore_has_any_keys&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:properties&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;favorite_color&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;favorite_artist&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Read more in the &lt;a href='http://www.postgresql.org/docs/9.1/static/hstore.html'&gt;PostgreSQL hstore documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id='array'&gt;Array&lt;/h1&gt;

&lt;p&gt;Ruby arrays can be serialized to PostgreSQL arrays. Surus includes support for text, integer, float, and decimal arrays.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Base&lt;/span&gt;
  &lt;span class='n'&gt;serialize&lt;/span&gt; &lt;span class='ss'&gt;:permissions&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Surus&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='nb'&gt;Array&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;TextSerializer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;serialize&lt;/span&gt; &lt;span class='ss'&gt;:favorite_integers&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Surus&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='nb'&gt;Array&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;IntegerSerializer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;serialize&lt;/span&gt; &lt;span class='ss'&gt;:favorite_floats&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Surus&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='nb'&gt;Array&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;FloatSerializer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;serialize&lt;/span&gt; &lt;span class='ss'&gt;:favorite_decimals&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Surus&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='nb'&gt;Array&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;DecimalSerializer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;create&lt;/span&gt; &lt;span class='ss'&gt;:permissions&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='sx'&gt;%w{ read_notes write_notes, manage_topics }&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='ss'&gt;:favorite_integers&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='ss'&gt;:favorite_floats&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
  &lt;span class='ss'&gt;:favorite_decimals&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='no'&gt;BigDecimal&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;3.14&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt; &lt;span class='no'&gt;BigDecimal&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;4.23&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Arrays can be searched with helper scopes.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;array_has&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:permissions&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;admin&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;array_has&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:permissions&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;manage_accounts&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;manage_users&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;array_has_any&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:favorite_integers&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;7&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;11&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;42&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id='synchronous_commit'&gt;Synchronous Commit&lt;/h1&gt;

&lt;p&gt;PostgreSQL can trade durability for speed. By disabling synchronous commit, transactions will return before the data is actually stored on the disk. This can be substantially faster, but it entails a short window where a crash could cause data loss (but not data corruption). This can be enabled for an entire session or per transaction.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;synchronous_commit&lt;/span&gt; &lt;span class='c1'&gt;# -&amp;gt; true&lt;/span&gt;

&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;transaction&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;synchronous_commit&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;
  &lt;span class='vi'&gt;@user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;save&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt; &lt;span class='c1'&gt;# This transaction can return before the data is written to the drive&lt;/span&gt;

&lt;span class='c1'&gt;# synchronous_commit returns to its former value outside of the transaction&lt;/span&gt;
&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;synchronous_commit&lt;/span&gt; &lt;span class='c1'&gt;# -&amp;gt; true&lt;/span&gt;

&lt;span class='c1'&gt;# synchronous_commit can be turned off permanently&lt;/span&gt;
&lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;synchronous_commit&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Read more in the &lt;a href='http://www.postgresql.org/docs/9.1/interactive/wal-async-commit.html'&gt;PostgreSQL asynchronous commit documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Head over to &lt;a href='https://github.com/JackC/surus'&gt;Github&lt;/a&gt; for more details.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <id>http://www.jackchristensen.com/2011/02/07/tern-the-sql-fans-migrator</id>
    <link type="text/html" rel="alternate" href="http://www.jackchristensen.com/2011/02/07/tern-the-sql-fans-migrator.html"/>
    <title>tern - The SQL Fan's Migrator</title>
    <updated>2011-02-07T00:00:00-06:00</updated>
    <summary type="text">new gem to simplify complex database schema migrations</summary>
    <author>
      <name>Jack Christensen</name>
      <uri>http://www.jackchristensen.com/</uri>
    </author>
    <content type="html">&lt;p&gt;I just released tern &amp;#8211; a new migration system.&lt;/p&gt;

&lt;p&gt;Tern is designed to simplify migrating database schemas with views, functions, triggers, constraints and other objects where altering one may require dropping and recreating others. For example, if view A selects from view B which selects from table C, then altering C could require five steps: drop A, drop B, alter C, recreate B, and recreate A. Tern can be told to alter C and it will automatically perform the other four steps.&lt;/p&gt;

&lt;p&gt;Head over to &lt;a href='https://github.com/JackC/tern'&gt;Github&lt;/a&gt; for more details.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <id>http://www.jackchristensen.com/2010/11/29/tod-time-of-day-for-ruby</id>
    <link type="text/html" rel="alternate" href="http://www.jackchristensen.com/2010/11/29/tod-time-of-day-for-ruby.html"/>
    <title>tod - Time of Day for Ruby</title>
    <updated>2010-11-29T00:00:00-06:00</updated>
    <summary type="text">new gem to simplify time of day handling</summary>
    <author>
      <name>Jack Christensen</name>
      <uri>http://www.jackchristensen.com/</uri>
    </author>
    <content type="html">&lt;p&gt;I just released tod &amp;#8211; a new ruby gem for time of day handling. It handles parsing, strftime, comparison, and arithmetic.&lt;/p&gt;
&lt;pre class='shell'&gt;
gem install tod
&lt;/pre&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;tod&amp;#39;&lt;/span&gt;

&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;parse&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;8&amp;quot;&lt;/span&gt;                            &lt;span class='c1'&gt;# =&amp;gt; 08:00:00&lt;/span&gt;
&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;parse&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;8pm&amp;quot;&lt;/span&gt;                          &lt;span class='c1'&gt;# =&amp;gt; 20:00:00&lt;/span&gt;
&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;parse&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;1230&amp;quot;&lt;/span&gt;                         &lt;span class='c1'&gt;# =&amp;gt; 12:30:00&lt;/span&gt;
&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;parse&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;3:25:58&amp;quot;&lt;/span&gt;                      &lt;span class='c1'&gt;# =&amp;gt; 03:25:58&lt;/span&gt;
&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;parse&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;515p&amp;quot;&lt;/span&gt;                         &lt;span class='c1'&gt;# =&amp;gt; 17:15:00&lt;/span&gt;

&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='mi'&gt;3600&lt;/span&gt;                        &lt;span class='c1'&gt;# =&amp;gt; 09:00:00&lt;/span&gt;
&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='mi'&gt;3600&lt;/span&gt;                        &lt;span class='c1'&gt;# =&amp;gt; 07:00:00&lt;/span&gt;
&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;23&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='mi'&gt;59&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='mi'&gt;45&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='mi'&gt;30&lt;/span&gt;                   &lt;span class='c1'&gt;# =&amp;gt; 00:00:15&lt;/span&gt;

&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;            &lt;span class='c1'&gt;# =&amp;gt; true&lt;/span&gt;

&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='mi'&gt;30&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;strftime&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;%H:%M&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;          &lt;span class='c1'&gt;# =&amp;gt; &amp;quot;08:30&amp;quot;&lt;/span&gt;
&lt;span class='no'&gt;TimeOfDay&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;17&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='mi'&gt;15&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;strftime&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;%I:%M %p&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;      &lt;span class='c1'&gt;# =&amp;gt; &amp;quot;05:15 PM&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Head over to &lt;a href='https://github.com/JackC/tod'&gt;Github&lt;/a&gt; for more details.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <id>http://www.jackchristensen.com/2009/11/14/assert-db-rejects-new-rails-test-plugin</id>
    <link type="text/html" rel="alternate" href="http://www.jackchristensen.com/2009/11/14/assert-db-rejects-new-rails-test-plugin.html"/>
    <title>assert_db_rejects - Test database constraints in Ruby on Rails</title>
    <updated>2009-11-14T00:00:00-06:00</updated>
    <summary type="text">A simple gem plugin for testing database constraints.</summary>
    <author>
      <name>Jack Christensen</name>
      <uri>http://www.jackchristensen.com/</uri>
    </author>
    <content type="html">&lt;p&gt;If you believe in using database constraints in addition to ActiveRecord validations, you may find &lt;a href=&quot;http://github.com/JackC/assert_db_rejects&quot;&gt;assert_db_rejects&lt;/a&gt; useful to help you test them.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;test_helper&amp;#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersonTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TestCase&lt;/span&gt;
  
  &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;database blocks duplicate names&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create!&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;&amp;quot;Test&amp;quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Assume the people table has a not null constraint on name. The assertion&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# will pass if a ActiveRecord::StatementInvalid is raised when saving p.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Validations are bypassed by assert_db_rejects as the purpose is to test&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# the database and not the model&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_db_rejects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Test&amp;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;</content>
  </entry>
  
  <entry>
    <id>http://www.jackchristensen.com/2009/08/08/solving-a-math-puzzle-with-ruby</id>
    <link type="text/html" rel="alternate" href="http://www.jackchristensen.com/2009/08/08/solving-a-math-puzzle-with-ruby.html"/>
    <title>Solving a Math Puzzle with Ruby</title>
    <updated>2009-08-08T00:00:00-05:00</updated>
    <summary type="text">A math puzzle from The Art of Game Design by Jesse Schell is solved with Ruby.</summary>
    <author>
      <name>Jack Christensen</name>
      <uri>http://www.jackchristensen.com/</uri>
    </author>
    <content type="html">&lt;p&gt;On page 212 of the excellent book &lt;a href=&quot;http://artofgamedesign.com/&quot;&gt;The Art of Game Design by Jesse Schell&lt;/a&gt;, the author presents the following puzzle as a negative example of the puzzle principle of &amp;#8220;make it easy to get started&amp;#8221;. The problem is to find the value of all the letters.&lt;/p&gt;
&lt;pre style=&quot;font-size: 2em; font-weight: bold; line-height: 1em;&quot;&gt;
   CEI
&lt;u&gt; ×  DA&lt;/u&gt;
   GCH
&lt;u&gt;+ DFB &lt;/u&gt;
  ADFH
&lt;/pre&gt;
&lt;p&gt;Next the author has to say that &amp;#8220;most players are at a complete loss as to how to begin solving a puzzle like this&amp;#8221;. After reading this I had to prove that I could beat it. I started on what turned into a quest of seven hours spread over two days.&lt;/p&gt;
&lt;p&gt;Some initial examination of the equation yields a few clues. To start with, a zero would not typically be written on the leftmost digit of a multiplier. So &lt;strong&gt;C&lt;/strong&gt; and &lt;strong&gt;D&lt;/strong&gt; cannot be zero. Furthermore, &lt;strong&gt;D×C=D&lt;/strong&gt;. This means that either &lt;strong&gt;C&lt;/strong&gt; or &lt;strong&gt;D&lt;/strong&gt; must be &lt;strong&gt;1&lt;/strong&gt;. Since &lt;strong&gt;D×I&lt;/strong&gt; does not equal &lt;strong&gt;D&lt;/strong&gt; we can prove that &lt;strong&gt;C&lt;/strong&gt; is &lt;strong&gt;1&lt;/strong&gt;. I continued on the path of slowly narrowing done the possible values of each letter until, in theory, I should have arrived at the solution. It did not work out this way in practice. Three times I reached a point where I had a logical contradiction such as having both proved that &lt;strong&gt;D&lt;/strong&gt; was &lt;strong&gt;6&lt;/strong&gt; and that &lt;strong&gt;D&lt;/strong&gt; must be less than &lt;strong&gt;5&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;After this, I really started to wonder if the problem had a solution. After all, the author had previously discussed player frustration and unfair challenges, and the puzzle was a negative example. Maybe there was no solution. On the other hand, my attack on the problem had been based on a great number of small assertions that built on all previous assertions. One inaccurate assertion would break the whole chain. At this point, I had put four hours into this puzzle and I was not about to stop until I had solved it, or I had proved there is no solution.&lt;/p&gt;
&lt;p&gt;It was time to bring the big guns into play and just brute-force the problem. It should be a simple matter to write a script in Ruby that tests every combination of values for a solution. Either I would have the answer, or I would prove there was no answer. As it turned out it, took about three hours to get the script right. Some of that was due to the fact that I decided to try some new features of Ruby 1.9 such as chaining iterators together, but most of it was due to difficulties perfecting a function that would yield every combination of values. But finally I built a solver for this type of puzzle.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;chars_to_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;digits&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each_with_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_and_place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_and_place&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;valid_multiplication?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_mult1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_mult2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mult1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars_to_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_mult1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mult2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars_to_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_mult2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars_to_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mult1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mult2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;valid_addition?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_add1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_add2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;add1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars_to_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_add1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;add2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars_to_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_add2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars_to_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;add1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;each_combination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&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;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;value_set&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;
  
  &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&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;n&quot;&gt;value_set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&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;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;value_set_without_current_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;value_set_without_current_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;each_combination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_set_without_current_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;value_set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&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;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;mult1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&amp;quot;cei&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mult2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;    &lt;span class=&quot;s2&quot;&gt;&amp;quot;da&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;    &lt;span class=&quot;s2&quot;&gt;&amp;quot;gch&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&amp;quot;dfb &amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;adfh&amp;quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mult1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mult2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer&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;chars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uniq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# space is hard-coded to 0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;value_set&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&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;to_a&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;each_combination&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&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;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_multiplication?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mult1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mult2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_addition?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char_values&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;p&gt;You can &lt;a href=&quot;/files/solving-a-math-puzzle-with-ruby/math_game.zip&quot;&gt;download&lt;/a&gt; the final code for this solver. The library code is split from the runner for this specific problem and it also includes a test suite.&lt;/p&gt;
&lt;p&gt;As it turns out the puzzle did have a solution. Select the space below to reveal the solution.&lt;/p&gt;
&lt;pre style=&quot;font-size: 2em; font-weight: bold; line-height: 1em; background-color: gray; color: gray; &quot;&gt;
A = 4
B = 7
C = 1
D = 3
E = 2
F = 8
G = 5
H = 6
I = 9
&lt;/pre&gt;
&lt;p&gt;I would be interested in hearing about any other approaches to this type of puzzle.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <id>http://www.jackchristensen.com/2009/07/11/linksys-wrt54g2-connection-problems-solved-by-dd-wrt</id>
    <link type="text/html" rel="alternate" href="http://www.jackchristensen.com/2009/07/11/linksys-wrt54g2-connection-problems-solved-by-dd-wrt.html"/>
    <title>Linksys WRT54G2 Connection Problems Solved by DD-WRT</title>
    <updated>2009-07-11T00:00:00-05:00</updated>
    <summary type="text">Random internet connection loss was solved by installing DD-WRT.</summary>
    <author>
      <name>Jack Christensen</name>
      <uri>http://www.jackchristensen.com/</uri>
    </author>
    <content type="html">&lt;p&gt;I was having issues with random loss of internet connection with my WRT54G2. Searching the internet found other people with similar problems but no definate solutions. I was somewhat resigned to buying a new router, but I decided as a last ditch effort to install &lt;a href=&quot;http://www.dd-wrt.com/&quot;&gt;DD-&lt;span class=&quot;caps&quot;&gt;WRT&lt;/span&gt;&lt;/a&gt;. The &lt;a href=&quot;http://www.dd-wrt.com/wiki/index.php/Linksys_WRT54G2&quot;&gt;instuctions&lt;/a&gt; on their wiki worked fine. The only hitch was turning off the Windows firewall was necessary for the tftp client to work. Once DD-&lt;span class=&quot;caps&quot;&gt;WRT&lt;/span&gt; was installed my problems vanished, and I have some cool extra features.&lt;/p&gt;
&lt;p&gt;Note: According to the instructions I followed this process is irreversible and can brick your router. So do this at your own risk.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <id>http://www.jackchristensen.com/2009/06/22/selenium-broken-by-firefox-upgrade-on-ubuntu</id>
    <link type="text/html" rel="alternate" href="http://www.jackchristensen.com/2009/06/22/selenium-broken-by-firefox-upgrade-on-ubuntu.html"/>
    <title>Selenium Broken by Firefox Upgrade on Ubuntu</title>
    <updated>2009-06-22T00:00:00-05:00</updated>
    <summary type="text">The path to the Firefox binary changes on upgrade. This can break Selenium.</summary>
    <author>
      <name>Jack Christensen</name>
      <uri>http://www.jackchristensen.com/</uri>
    </author>
    <content type="html">&lt;p&gt;I use &lt;a href=&quot;http://github.com/brynary/webrat/tree/master&quot;&gt;Webrat&lt;/a&gt; and &lt;a href=&quot;http://seleniumhq.org/&quot;&gt;Selenium&lt;/a&gt; to automate browser testing. There can be an &lt;a href=&quot;http://groups.google.com/group/webrat/browse_thread/thread/ea60a0758b0c174a&quot;&gt;problem&lt;/a&gt; with Firefox 3 on Ubuntu that requires configuring &lt;a href=&quot;http://seleniumhq.org/&quot;&gt;Selenium&lt;/a&gt; to point the Firefox binary directly.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt; 
&lt;span class=&quot;no&quot;&gt;Webrat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&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;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:selenium&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selenium_browser_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;*firefox3 /usr/lib/firefox-3.0.10/firefox&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The problem is when Firefox is upgraded the path to the binary will change. That causes an error at the beginning of the test run.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;
16:10:07.104 WARN - POST /selenium-server/driver/ HTTP/1.1
java.lang.RuntimeException: java.lang.NullPointerException
   at org.openqa.selenium.server.browserlaunchers.BrowserLauncherFactory.createBrowserLauncher(BrowserLauncherFactory.java:145)
   at org.openqa.selenium.server.browserlaunchers.BrowserLauncherFactory.getBrowserLauncher(BrowserLauncherFactory.java:85)
   at org.openqa.selenium.server.BrowserSessionFactory.createNewRemoteSession(BrowserSessionFactory.java:318)
   at org.openqa.selenium.server.BrowserSessionFactory.getNewBrowserSession(BrowserSessionFactory.java:118)
   at org.openqa.selenium.server.BrowserSessionFactory.getNewBrowserSession(BrowserSessionFactory.java:81)
   at org.openqa.selenium.server.SeleniumDriverResourceHandler.getNewBrowserSession(SeleniumDriverResourceHandler.java:658)
   at org.openqa.selenium.server.SeleniumDriverResourceHandler.doCommand(SeleniumDriverResourceHandler.java:392)
   at org.openqa.selenium.server.SeleniumDriverResourceHandler.handleCommandRequest(SeleniumDriverResourceHandler.java:368)
   at org.openqa.selenium.server.SeleniumDriverResourceHandler.handle(SeleniumDriverResourceHandler.java:129)
   at org.mortbay.http.HttpContext.handle(HttpContext.java:1530)
   at org.mortbay.http.HttpContext.handle(HttpContext.java:1482)
   at org.mortbay.http.HttpServer.service(HttpServer.java:909)
   at org.mortbay.http.HttpConnection.service(HttpConnection.java:816)
   at org.mortbay.http.HttpConnection.handleNext(HttpConnection.java:982)
   at org.mortbay.http.HttpConnection.handle(HttpConnection.java:833)
   at org.mortbay.http.SocketListener.handleConnection(SocketListener.java:244)
   at org.mortbay.util.ThreadedServer.handle(ThreadedServer.java:357)
   at org.mortbay.util.ThreadPool$PoolThread.run(ThreadPool.java:534)
Caused by: java.lang.NullPointerException
   at org.openqa.selenium.server.browserlaunchers.FirefoxChromeLauncher.&amp;lt;init&amp;gt;(FirefoxChromeLauncher.java:62)
   at org.openqa.selenium.server.browserlaunchers.Firefox3Launcher.&amp;lt;init&amp;gt;(Firefox3Launcher.java:30)
   at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
   at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
   at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
   at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
   at org.openqa.selenium.server.browserlaunchers.BrowserLauncherFactory.createBrowserLauncher(BrowserLauncherFactory.java:137)
   ... 17 more 
&lt;/pre&gt;
&lt;p&gt;Then each test will fail with a Selenium::CommandError: Selenium::CommandError. The fix is simple, just update the path to the Firefox binary. Hopefully this will save someone some time.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <id>http://www.jackchristensen.com/2009/06/20/ruby-19-on-windows-7-virtualbox-and-ubuntu</id>
    <link type="text/html" rel="alternate" href="http://www.jackchristensen.com/2009/06/20/ruby-19-on-windows-7-virtualbox-and-ubuntu.html"/>
    <title>Ruby 1.9 Development with Windows, VirtualBox, and Ubuntu</title>
    <updated>2009-06-20T00:00:00-05:00</updated>
    <summary type="text">Ruby is more at home on *nix than on Windows. This tutorial covers setting up a Windows machine as a VM host, installing an Ubuntu VM, building Ruby 1.9, and setting up file sharing between Windows and the VM.</summary>
    <author>
      <name>Jack Christensen</name>
      <uri>http://www.jackchristensen.com/</uri>
    </author>
    <content type="html">&lt;p&gt;Ruby is more at home on *nix than on Windows. This tutorial covers setting up a Windows machine as a VM host, installing an Ubuntu VM, building Ruby 1.9, and setting up file sharing between Windows and the VM.&lt;/p&gt;
&lt;p&gt;First download and install &lt;a href=&quot;http://www.virtualbox.org/wiki/Downloads&quot;&gt;VirtualBox&lt;/a&gt;. You should create a VM and mount the Ubuntu 9.04 server &lt;span class=&quot;caps&quot;&gt;ISO&lt;/span&gt; in the CD drive. I used the 64-bit version. I experienced numerous random problems with the desktop edition, but the server edition worked well.&lt;/p&gt;
&lt;p&gt;Before booting the VM for the first time we should set up the network the VM will use. VirtualBox has several different network options depending on what level of access is needed to and from the VM. In this case, I want complete inbound and outbound access to the VM. The network setup we will use is very simple, but it may not be secure outside of a trusted &lt;span class=&quot;caps&quot;&gt;LAN&lt;/span&gt;. In the settings for the VM set the network adapter to bridged.&lt;/p&gt;
&lt;p&gt;Boot the VM and install the OS. The network configuration will probably grab a &lt;span class=&quot;caps&quot;&gt;DHCP&lt;/span&gt; address automatically, but we really want to assign a static IP so we can easily connect via &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt;. Either cancel the network auto-detection or if you are not quick enough choose to go back when it asks you for a host name. Manually configure the network to use a static IP address (you might also want to add an entry to your hosts file on the Windows machine so you don&amp;#8217;t have to remember the IP address). When it offers a list of servers to install choose &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;After the install completes we should be able to use &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; into our VM.&lt;/p&gt;
&lt;p&gt;Before we do anything else we should setup version control for our VM&amp;#8217;s /etc directory. This simple step can save much frustration down the road if we ever mess this VM up.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;
sudo aptitude install etckeeper
sudo etckeeper init
sudo etckeeper commit &quot;initial import&quot; 
&lt;/pre&gt;
&lt;p&gt;We could just install Ruby with aptitude and be done, but historically there have been issues especially with RubyGems so I prefer to compile from source. So we need to install the build tools and libraries Ruby will need.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;
sudo aptitude install build-essential libssl-dev libreadline5-dev zlib1g-dev
&lt;/pre&gt;
&lt;p&gt;Next download, compile, and install the latest version of Ruby 1.9 (1.91.-p129 at the time this was written).&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;
wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p129.tar.gz
tar xzf ruby-1.9.1-p129.tar.gz
cd ruby-1.9.1-p129/
./configure
make
make test
sudo make install
&lt;/pre&gt;
&lt;p&gt;At this point we should have Ruby operational. However, we really want to be able to access our files transparently from Windows. So let&amp;#8217;s setup &lt;span class=&quot;caps&quot;&gt;SAMBA&lt;/span&gt;.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;
sudo aptitude install samba
&lt;/pre&gt;
&lt;p&gt;Now we need to configure our share. Edit /etc/samba/smb.conf.&lt;/p&gt;
&lt;p&gt;Around the middle of the file we need to change the &lt;em&gt;security&lt;/em&gt; option. Uncomment it and change it to share.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;
security = share
&lt;/pre&gt;
&lt;p&gt;Then at the bottom add your share. &lt;em&gt;force user&lt;/em&gt; and &lt;em&gt;force group&lt;/em&gt; are needed to set the proper owner for any files created from Windows. Since I am using static IP assignments it is easy to only allow the host access to the file shares with &lt;em&gt;hosts allow&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;
[jack]
   path = /home/jack
   public = yes
   writable = yes
   browsable = yes
   read only = no
   force user = jack
   force group = jack
   hosts allow = 127.0.0.1 192.168.2.5
&lt;/pre&gt;
&lt;p&gt;At this point we have a working Ubuntu Ruby 1.9 development environment that we can access from Windows.&lt;/p&gt;</content>
  </entry>
  
 
</feed>


