<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="http://feeds.feedburner.com/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-US">
  <title>Atomic Spin - Home</title>
  <id>tag:spin.atomicobject.com,2008:mephisto/</id>
  <generator version="0.7.3" uri="http://mephistoblog.com">Mephisto Noh-Varr</generator>
  
  <link href="http://spin.atomicobject.com/" rel="alternate" type="text/html" />
  <updated>2008-07-02T17:50:23Z</updated>
  <geo:lat>42.955944</geo:lat><geo:long>-85.644983</geo:long><link rel="self" href="http://feeds.feedburner.com/atomic_spin" type="application/atom+xml" /><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatomic_spin" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatomic_spin" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatomic_spin" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.rojo.com/add-subscription?resource=http%3A%2F%2Ffeeds.feedburner.com%2Fatomic_spin" src="http://blog.rojo.com/RojoWideRed.gif">Subscribe with Rojo</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/atomic_spin" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatomic_spin" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fatomic_spin" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatomic_spin" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Fatomic_spin" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>fletcher</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-07-02:856</id>
    <published>2008-07-02T17:46:00Z</published>
    <updated>2008-07-02T17:50:23Z</updated>
    <category term="Technologies" />
    <category term="Tips" />
    <category term="Tools" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/325080462/rolling-a-jruby-desktop-application" rel="alternate" type="text/html" />
    <title>Rolling a JRuby desktop application</title>
<summary type="html">Atomic has been using &lt;a href="http://jruby.codehaus.org"&gt;JRuby&lt;/a&gt;, several Java libraries, and &lt;a href="http://code.google.com/p/jarjar"&gt;Jar Jar Links&lt;/a&gt; to distribute five different Ruby desktop applications (read: not Rails applications) as single, executable jar files. In this article, I will show how we:
	&lt;ul&gt;
	&lt;li&gt;Work on our application as developers&lt;/li&gt;
		&lt;li&gt;Distribute our application as a single jar file to our users&lt;/li&gt;
		&lt;li&gt;Compile the Ruby source files into Java class files&lt;/li&gt;
		&lt;li&gt;Integrate third-party Ruby and Java libraries&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;This article includes an example project that demonstrates all of the above.&lt;/p&gt;</summary><content type="html">
            Atomic has been using &lt;a href="http://jruby.codehaus.org"&gt;JRuby&lt;/a&gt;, several Java libraries, and &lt;a href="http://code.google.com/p/jarjar"&gt;Jar Jar Links&lt;/a&gt; to distribute five different Ruby desktop applications (read: not Rails applications) as single, executable jar files. In this article, I will show how we:
	&lt;ul&gt;
	&lt;li&gt;Work on our application as developers&lt;/li&gt;
		&lt;li&gt;Distribute our application as a single jar file to our users&lt;/li&gt;
		&lt;li&gt;Compile the Ruby source files into Java class files&lt;/li&gt;
		&lt;li&gt;Integrate third-party Ruby and Java libraries&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;This article includes an example project that demonstrates all of the above.&lt;/p&gt;
The example is our old friend Hello World. A tarball and zip file are available here:
	&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://spin.atomicobject.com/assets/2008/7/2/jar_rolling_example.tar.gz"&gt;jar_rolling_example.tar.gz&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://spin.atomicobject.com/assets/2008/7/2/jar_rolling_example.zip"&gt;jar_rolling_example.zip&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;This article will read best if you can download the example and follow along, but I will include some code snippets so that you can gather the main ideas even without the example.&lt;/p&gt;


	&lt;p&gt;I’ll start by discussing the project layout. It is meant to be similar to a normal Ruby project—there’s a Rakefile at the top level, a lib directory for Ruby files, and a vendor directory for third-party libraries (though I think having a vendor directory may be a Railsism, not necessarily a Ruby thing). In this case, I’ve got a couple of Ruby files in the lib directory, a Java library and a Ruby library in vendor, and all of JRuby 1.1.2 in vendor as well.&lt;/p&gt;


	&lt;p&gt;The Java library I’ve chosen to use is &lt;a href="https://swingx.dev.java.net"&gt;SwingX&lt;/a&gt; from SwingLabs. SwingX includes some nice Swing widgets that don’t ship with Java. In the sample application, I’m going to use the JXDatePicker widget. The swingx-0.9.2.jar is hanging out in the top level of the vendor directory.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://atomicobjectrb.rubyforge.org/constructor"&gt;Constructor&lt;/a&gt; is a Ruby library we use at Atomic for simplifying the declaration of Ruby class constructors. The constructor gem is unpacked into the vendor directory, so it lives in vendor/constructor-1.0.0 &lt;sup&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;


	&lt;p&gt;I’ve laid the project out like this for several reasons. The first is that it is a typical Ruby project layout. The second is that all of the libraries this application needs are distributed with the application itself; thus, the only dependence on the host system is a working Java 1.5+ installation. Err the blog has an &lt;a href="http://errtheblog.com/posts/50-vendor-everything"&gt;excellent write-up&lt;/a&gt; about why distributing an application like this is a good idea. His discussion is in the context of Rails, but it is still relevant here.&lt;/p&gt;


	&lt;p&gt;The third reason is that this layout allows me to work as a developer or roll everything up for a user. As a developer, I don’t want to build a new jar every time I run the tests, since that is too slow. Users want to run a jar instead of trying to navigate some files splattered all over the filesystem. Using some tricks with JRuby’s $LOAD_PATH and $CLASSPATH globals, I will make the application work transparently as either a developer working against the filesystem or a user working with the jar.&lt;/p&gt;


	&lt;p&gt;Now let’s take a peek at the Rakefile. There are two namespaces: dist and java. The dist namespace defines tasks for building, cleaning, and running the single jar file. The tasks in the java namespace are used by the dist tasks to do the actual building.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;namespace &lt;span class="sy"&gt;:java&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  output_directory = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;classes&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  desc &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Compile java executable stub class&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  task &lt;span class="sy"&gt;:stub&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;java:clean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;    mkdir_p output_directory&lt;tt&gt;
&lt;/tt&gt;    sh &lt;span class="s"&gt;&lt;span class="dl"&gt;%+&lt;/span&gt;&lt;span class="k"&gt;javac -target 1.5 -d &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;output_directory&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; -classpath vendor/jruby-complete-&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;&lt;span class="co"&gt;JRUBY_VERSION&lt;/span&gt;&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;.jar lib/Main.java&lt;/span&gt;&lt;span class="dl"&gt;+&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  desc &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Compile the Ruby files into class files&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  task &lt;span class="sy"&gt;:rb&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;java:clean&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    prefix = &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;com/atomicobject/hello_world&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    sh &lt;span class="s"&gt;&lt;span class="dl"&gt;%+&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;jruby&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;jrubyc&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; -p &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;prefix&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; -t &lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;output_directory&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt; lib&lt;/span&gt;&lt;span class="dl"&gt;+&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  desc &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Compile all source files into class files&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  task &lt;span class="sy"&gt;:compile&lt;/span&gt; =&amp;gt; [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;java:stub&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;java:rb&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;]&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="co"&gt;CLEAN&lt;/span&gt;.include output_directory &lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The truly interesting bits of the Rakefile are the java:stub and java:rb tasks, which compile the Java and Ruby files, respectively. The Java stub file is a small Java class that fires up the JRuby interpreter; we’ll look at that next. The java:rb task uses the JRuby compiler to translate all of the Ruby .rb files in the lib directory into Java .class files. The java:rb task also passes the ‘com/atomicobject/hello_world’ argument to the JRuby compiler, which in turn will use that for assigning a package to the each compiled class. After the compiler has run against a .rb file, the corresponding .class file will have the package ‘com.atomicobject.hello_world.lib’—a combination of the prefix and directory we pointed the compiler at.&lt;/p&gt;


	&lt;p&gt;After building the Java classes, the dist:build task concludes by running ant. An ant task will build the final jar for us using JarJar.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;package com.atomicobject.hello_world.lib;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;import org.jruby.Ruby;&lt;tt&gt;
&lt;/tt&gt;import org.jruby.RubyRuntimeAdapter;&lt;tt&gt;
&lt;/tt&gt;import org.jruby.javasupport.JavaEmbedUtils;&lt;tt&gt;
&lt;/tt&gt;import java.util.ArrayList;&lt;tt&gt;
&lt;/tt&gt;    &lt;tt&gt;
&lt;/tt&gt;// This technique for starting JRuby is taken from&lt;tt&gt;
&lt;/tt&gt;// http://wiki.jruby.org/wiki/Direct_JRuby_Embedding&lt;tt&gt;
&lt;/tt&gt;public class Main {&lt;tt&gt;
&lt;/tt&gt;  public static void main(String[] args) {&lt;tt&gt;
&lt;/tt&gt;    Ruby runtime = JavaEmbedUtils.initialize(new ArrayList());&lt;tt&gt;
&lt;/tt&gt;    RubyRuntimeAdapter evaler = JavaEmbedUtils.newRuntimeAdapter();&lt;tt&gt;
&lt;/tt&gt;    evaler.eval(runtime, &amp;quot;require 'com/atomicobject/hello_world/lib/application_bootstrap'&amp;quot;);&lt;tt&gt;
&lt;/tt&gt;    JavaEmbedUtils.terminate(runtime);&lt;tt&gt;
&lt;/tt&gt;  }&lt;tt&gt;
&lt;/tt&gt;}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The next thing worth looking at is the Main Java class in ‘lib/Main.java’, which is the class that is executed when Java is pointed at the jar. The main method is using one of the JRuby APIs for firing up the interpreter. We’ve got it requiring the ‘lib/application_bootstrap.rb’ file, which is a classless script that takes over the responsibility of starting the application.&lt;/p&gt;


	&lt;p&gt;The Main.java file distributed with the example includes an alternative method (which is not shown here) of starting JRuby. This method allows you to start the JRuby interpreter by giving it JRuby parameters just like you would on the command line. The downside to this technique is that it is not considered part of the public JRuby embedding &lt;span class="caps"&gt;API&lt;/span&gt;, which means it may be brittle with respect to JRuby versioning.&lt;/p&gt;


Now we can move along to the application_bootstrap.rb file.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;include &lt;span class="co"&gt;Java&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="gv"&gt;$LOAD_PATH&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;                              &lt;span class="c"&gt;# running from filesystem&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="gv"&gt;$LOAD_PATH&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;com/atomicobject/hello_world/lib&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="c"&gt;# running from jar&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="gv"&gt;$LOAD_PATH&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor/constructor-1.0.0/lib&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="gv"&gt;$CLASSPATH&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor/swingx-0.9.2.jar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;          &lt;span class="c"&gt;# running from filesystem&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;view_builder&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;hello_world&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;tt&gt;
&lt;/tt&gt;hello_world = &lt;span class="co"&gt;HelloWorld&lt;/span&gt;.new(&lt;span class="sy"&gt;:view_builder&lt;/span&gt; =&amp;gt; &lt;span class="co"&gt;ViewBuilder&lt;/span&gt;.new)&lt;tt&gt;
&lt;/tt&gt;hello_world.run&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

This script’s goal is to pull in our third-party dependences and then start the application. Then the rest of the application does not care where the various libraries come from.

	&lt;p&gt;The first line of application_bootstrap includes the Java module, which brings in JRuby’s Java integration support. The second and third lines make the source files in the lib directory available to the require lines farther down. The fourth line and fifth lines make the Ruby constructor and Java SwingX libraries available, respectively. Note that the jar is added to $CLASSPATH instead of $LOAD_PATH; $CLASSPATH is a global made available via Java integration and is useful for adding things to Java’s classpath during runtime &lt;sup&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt;. My applications generally loop over directory globs when setting up the &lt;span class="caps"&gt;LOAD&lt;/span&gt;_PATH and &lt;span class="caps"&gt;CLASSPATH&lt;/span&gt;, but in this example I think it is more instructive to be explicit. Once the environment is setup, we require and build a couple of Ruby classses and then start the application using HelloWorld’s run method.&lt;/p&gt;


	&lt;p&gt;Rather than pasting in the entirety of ViewBuilder and HelloWorld, I will simply discuss the interesting bits.&lt;/p&gt;


	&lt;p&gt;view_builder.rb contains the line&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;import org.jdesktop.swingx.&lt;span class="co"&gt;JXDatePicker&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

which allows it to do
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;date_picker = &lt;span class="co"&gt;JXDatePicker&lt;/span&gt;.new&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This is how the application can access the Java library.&lt;/p&gt;


	&lt;p&gt;Similarly, hello_world.rb contains&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;constructor &lt;span class="sy"&gt;:view_builder&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;which uses the Ruby library. Thanks to the work done by application_bootstrap.rb, accessing both the Java and Ruby libraries is transparent to the application, whether it is running from the filesystem or within a jar.&lt;/p&gt;


	&lt;p&gt;Finally, here are the guts of the build.xml Ant file:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="ta"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="an"&gt;basedir&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="an"&gt;default&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="an"&gt;name&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="ta"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Combine Ruby and Java source with the jruby-complete jar&lt;span class="ta"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="ta"&gt;&amp;lt;target&lt;/span&gt; &lt;span class="an"&gt;name&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="an"&gt;description&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Create the deliverable jar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="ta"&gt;&amp;lt;taskdef&lt;/span&gt; &lt;span class="an"&gt;name&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;jarjar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;             &lt;span class="an"&gt;classname&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;com.tonicsystems.jarjar.JarJarTask&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;             &lt;span class="an"&gt;classpath&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor/jarjar-1.0rc7.jar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;/&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="ta"&gt;&amp;lt;mkdir&lt;/span&gt; &lt;span class="an"&gt;dir&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;pkg&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;/&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="ta"&gt;&amp;lt;jarjar&lt;/span&gt; &lt;span class="an"&gt;destfile&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;pkg/HelloWorld.jar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;manifest&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        &lt;span class="ta"&gt;&amp;lt;attribute&lt;/span&gt; &lt;span class="an"&gt;name&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;Main-Class&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="an"&gt;value&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;com.atomicobject.hello_world.lib.Main&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;/&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;fileset&lt;/span&gt; &lt;span class="an"&gt;dir&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;classes&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;/&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;zipfileset&lt;/span&gt; &lt;span class="an"&gt;src&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor/jruby-complete-1.1.1.jar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;/&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;zipfileset&lt;/span&gt; &lt;span class="an"&gt;src&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor/swingx-0.9.2.jar&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;/&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="ta"&gt;&amp;lt;zipfileset&lt;/span&gt; &lt;span class="an"&gt;dir&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor/constructor-1.0.0&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="an"&gt;prefix&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;vendor/constructor-1.0.0&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class="ta"&gt;/&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="ta"&gt;&amp;lt;/jarjar&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="ta"&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="ta"&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The Ant file is pretty small; it exists only to make use of JarJar for bundling the application. The &amp;lt;jarjar&gt; task extends Ant’s regular &amp;lt;jar&gt; task by allowing existing jars to be rolled up into the new jar target. In this case, JarJar is directed to pull jruby-complete, swingx, and constructor into our final HelloWorld.jar. Note that the package I chose earlier, com.atomicobject.hello_world, shows up in the Main-Class jar attribute.&lt;/p&gt;


	&lt;p&gt;If your application is dependent on any RubyGems then I suggest throwing them them into the vendor directory as well. Rolling them in with jruby-complete is not easy, and the same arguments from the previously linked Err the Blog post are relevant. His technique for vendoring gems is good and I recommend using it (Constructor was, in fact, a gem that I unpacked).&lt;/p&gt;


	&lt;p&gt;I have run into a problem when using JarJar to roll signed jars into our single jar file. In my case, I got exceptions coming out of the signed &lt;a href="http://www.bouncycastle.org/"&gt;Bouncy Castle&lt;/a&gt; library I use for OpenSSL encryption. My problem likely stems from the fact that JarJar does some bytecode manipulation, which in turn means the bouncycastle files no longer pass signature verification. I do not entirely understand what JarJar does, so I’m not totally sure about this. Either way, my solution was to distribute the bouncycastle library alongside the application’s jar file—the standard way of distributing a Java application. It isn’t as clean as I wanted, but it works.&lt;/p&gt;


	&lt;p&gt;One thing I’m not particularly happy about with this process is with how many places the com.atomicobject.hello_world package name shows up—it appears in one form or another in almost every file we’ve discussed. Each case is slightly different and exists in a different part of the process, so solving this problem is not easy. I have considered having the main Rakefile generate Main.java, build.xml, and application_bootstrap.rb. This could not only solve the problem of the com.atomicobject.hello_world duplication, but also make it so that less files need to be touched when we add new Ruby and Java libraries to the project. But this solution is not particularly easy to implement and I do not have a great sampling of different project with which to base my abstractions.&lt;/p&gt;


	&lt;p&gt;David Koontz has a RubyGem named &lt;a href="http://rubyforge.org/projects/rawr/"&gt;Rawr&lt;/a&gt; that helps you turn your project into a single jar. The last time I tried it (a while ago) I could not get it to work the way I wanted; then when I looked again I couldn’t find the website anymore. The above linked page is now active, but I have not tried it lately. So I cannot speak to whether it works or not. And I cannot speak to whether is better or worse than what I’ve described in this article.&lt;/p&gt;


	&lt;p&gt;I’ve written this post from the first-person perspective, but Atomic’s jar rolling process is hardly my own effort: Micah Alles, David Crosby, Shawn Crowley, Karlin Fox, and Andrew Witte all helped figure this out a year ago and evolve it since then.&lt;/p&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;My goal here is not to promote the Constructor or SwingX libraries. I chose them simply because they are representative of both Ruby and Java libraries and were easy for me to integrate into the example.
&lt;a href="#fnref1" title="Jump back to footnote 1 in the text."&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I believe there is an outstanding JRuby &lt;span class="caps"&gt;JIRA&lt;/span&gt; issue for bringing $LOAD_PATH and $CLASSPATH closer to synonymous.
&lt;a href="#fnref2" title="Jump back to footnote 2 in the text."&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=IhdcIJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=IhdcIJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=3Hqy9j"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=3Hqy9j" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=Tr0Ecj"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=Tr0Ecj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=kfpddj"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=kfpddj" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/325080462" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/07/02/rolling-a-jruby-desktop-application</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>fogle</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-06-16:573</id>
    <published>2008-06-16T02:08:00Z</published>
    <updated>2008-06-16T02:09:00Z</updated>
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/312704893/finder-objects-in-rails" rel="alternate" type="text/html" />
    <title>Finder Objects in Rails</title>
<summary type="html">&lt;p&gt;ActiveRecord is perhaps the most compelling component of Ruby on Rails. ActiveRecord makes it easy to create a record, or to find one, or even to migrate the database schema. Unfortunately those “find” methods can be a little too seductive. Let’s see why.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;ActiveRecord is perhaps the most compelling component of Ruby on Rails. ActiveRecord makes it easy to create a record, or to find one, or even to migrate the database schema. Unfortunately those “find” methods can be a little too seductive. Let’s see why.&lt;/p&gt;
&lt;p&gt;Our example project is a basic social network. It will start with profiles and friends, and add from there. We’ve implemented “Profile” and “Friend” features and now our customer asks for “Search by State”. In our “Profiles” controller, we implement:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_all_by_state(params[&lt;span class="sy"&gt;:state&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;At first, this is wonderful. It does not burden the caller with unnecessary knowledge, and keeps the code simple and clean. It’s also simple to test through either state-based or interaction testing. At the customer’s request, we add an admin listing, a friend list, and the profile page itself. It’s working beautifully.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_all_by_state(params[&lt;span class="sy"&gt;:state&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profile&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:id&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesAdminController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;index&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;FriendsController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;index&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@user&lt;/span&gt; = &lt;span class="co"&gt;User&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:user_id&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@friends&lt;/span&gt; = &lt;span class="iv"&gt;@user&lt;/span&gt;.friends&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Then our customer comes back and says we need the ability to have private profiles. A profile should only show up if it’s public. We make the changes as follows:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_all_by_state_and_public(params[&lt;span class="sy"&gt;:state&lt;/span&gt;], &lt;span class="pc"&gt;true&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profile&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:id&lt;/span&gt;], &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; {&lt;span class="sy"&gt;:public&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;true&lt;/span&gt;})&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesAdminController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;index&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;FriendsController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;index&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@user&lt;/span&gt; = &lt;span class="co"&gt;User&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:user_id&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@friends&lt;/span&gt; = &lt;span class="iv"&gt;@user&lt;/span&gt;.friends.find_all_by_public(&lt;span class="pc"&gt;true&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;We know it’s not ‘DRY’, but we move on in the interest of ‘Getting Things Done’. We run our integration tests, but one is failing. A user can no longer view their own profile. After investigating, we realize that in reality the rule should be “A profile should be visible if it’s public or belongs to the current user”.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;25&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;state = ? and (public = ? or user_id = ?)&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;      params[&lt;span class="sy"&gt;:state&lt;/span&gt;], &lt;span class="pc"&gt;true&lt;/span&gt;, current_user.id&lt;tt&gt;
&lt;/tt&gt;    ]&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;public = ? or user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;, current_user.id]&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profile&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:id&lt;/span&gt;], &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesAdminController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;FriendsController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;index&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@user&lt;/span&gt; = &lt;span class="co"&gt;User&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:user_id&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;public = ? or user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;, current_user.id]&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@friends&lt;/span&gt; = &lt;span class="iv"&gt;@user&lt;/span&gt;.friends.find(&lt;span class="sy"&gt;:all&lt;/span&gt;, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This has gotten out of hand. It’s just not ok to leave &lt;span class="caps"&gt;SQL&lt;/span&gt; lying around in our controller, even if it’s just condition arrays. The caller should specify intent, but not worry about details like privacy.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;25&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;29&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;31&lt;tt&gt;
&lt;/tt&gt;32&lt;tt&gt;
&lt;/tt&gt;33&lt;tt&gt;
&lt;/tt&gt;34&lt;tt&gt;
&lt;/tt&gt;35&lt;tt&gt;
&lt;/tt&gt;36&lt;tt&gt;
&lt;/tt&gt;37&lt;tt&gt;
&lt;/tt&gt;38&lt;tt&gt;
&lt;/tt&gt;39&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;40&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;41&lt;tt&gt;
&lt;/tt&gt;42&lt;tt&gt;
&lt;/tt&gt;43&lt;tt&gt;
&lt;/tt&gt;44&lt;tt&gt;
&lt;/tt&gt;45&lt;tt&gt;
&lt;/tt&gt;46&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Profile&lt;/span&gt; &amp;lt; &lt;span class="co"&gt;ActiveRecord&lt;/span&gt;::&lt;span class="co"&gt;Base&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.find_all_by_state(state, current_user)&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;state = ? and (public = ? or user_id = ?)&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;      state, &lt;span class="pc"&gt;true&lt;/span&gt;, current_user.id&lt;tt&gt;
&lt;/tt&gt;    ]&lt;tt&gt;
&lt;/tt&gt;    find(&lt;span class="sy"&gt;:all&lt;/span&gt;, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.find_all_for_admin&lt;tt&gt;
&lt;/tt&gt;    find(&lt;span class="sy"&gt;:all&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.find_by_id(id, current_user)&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;public = ? or user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;, current_user.id]&lt;tt&gt;
&lt;/tt&gt;    find(id, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.find_friend_profiles_of(user, current_user)&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;public = ? or user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;, current_user.id]&lt;tt&gt;
&lt;/tt&gt;    user.friend_profiles.find(&lt;span class="sy"&gt;:all&lt;/span&gt;, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_all_by_state(params[&lt;span class="sy"&gt;:state&lt;/span&gt;], current_user)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profile&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_by_id(params[&lt;span class="sy"&gt;:id&lt;/span&gt;], current_user)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesAdminController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_all_for_admin&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;FriendsController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;index&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@user&lt;/span&gt; = &lt;span class="co"&gt;User&lt;/span&gt;.find(params[&lt;span class="sy"&gt;:user_id&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@friends&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_friend_profiles_of(&lt;span class="iv"&gt;@user&lt;/span&gt;, current_user)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Now all of the actions that need profiles can find them safely by calling one of our methods, and the privacy logic is handled for us. Something doesn’t feel quite right, but we’ll try it for a bit to make sure we’re not just suffering from indigestion.&lt;/p&gt;


	&lt;p&gt;A month later, the customer asks for a different kind of search. Now we need to search by last name. By this time, there’s a second pair on the project, and they implement this feature.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search_by_state&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_all_by_state(params[&lt;span class="sy"&gt;:state&lt;/span&gt;], current_user)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search_by_last_name&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_all_by_last_name(params[&lt;span class="sy"&gt;:last_name&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profile&lt;/span&gt; = &lt;span class="co"&gt;Profile&lt;/span&gt;.find_by_id(params[&lt;span class="sy"&gt;:id&lt;/span&gt;], current_user)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Their integration test passes and they check it in. After we deploy, the customer complains. It seems the new form of search doesn’t take privacy into account. Why did this happen? Intelligent programmers following project conventions and acting conscientiously will still make these mistakes. Is it a communication breakdown? I contend that it’s a code flaw. When finding rules are implemented, they need to be designed to allow later programmers to forget the details until they need them. The programmers should not be filling their minds with privacy while implementing the controller action. But how?&lt;/p&gt;


	&lt;p&gt;In my experience, finder logic very often gets complicated very quickly. The responsibility for finding a specific kind of entity is a big deal, and should not be given to a class that already fills another responsibility (persisting profile information). In addition, finder methods should not be implicit, as it leads to the issues outlined above. Instead we use finder objects.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;25&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;29&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;31&lt;tt&gt;
&lt;/tt&gt;32&lt;tt&gt;
&lt;/tt&gt;33&lt;tt&gt;
&lt;/tt&gt;34&lt;tt&gt;
&lt;/tt&gt;35&lt;tt&gt;
&lt;/tt&gt;36&lt;tt&gt;
&lt;/tt&gt;37&lt;tt&gt;
&lt;/tt&gt;38&lt;tt&gt;
&lt;/tt&gt;39&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;40&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;41&lt;tt&gt;
&lt;/tt&gt;42&lt;tt&gt;
&lt;/tt&gt;43&lt;tt&gt;
&lt;/tt&gt;44&lt;tt&gt;
&lt;/tt&gt;45&lt;tt&gt;
&lt;/tt&gt;46&lt;tt&gt;
&lt;/tt&gt;47&lt;tt&gt;
&lt;/tt&gt;48&lt;tt&gt;
&lt;/tt&gt;49&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;50&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;51&lt;tt&gt;
&lt;/tt&gt;52&lt;tt&gt;
&lt;/tt&gt;53&lt;tt&gt;
&lt;/tt&gt;54&lt;tt&gt;
&lt;/tt&gt;55&lt;tt&gt;
&lt;/tt&gt;56&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfileFinder&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  attr_accessor &lt;span class="sy"&gt;:current_user&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;find_all_by_state&lt;/span&gt;(state)&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;state = ? and (public = ? or user_id = ?)&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;tt&gt;
&lt;/tt&gt;      state, &lt;span class="pc"&gt;true&lt;/span&gt;, &lt;span class="iv"&gt;@current_user&lt;/span&gt;.id&lt;tt&gt;
&lt;/tt&gt;    ]&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Profile&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;find_all_by_last_name&lt;/span&gt;(last_name)&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;last_name = ? and (public = ? or user_id = ?)&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;tt&gt;
&lt;/tt&gt;      last_name, &lt;span class="pc"&gt;true&lt;/span&gt;, &lt;span class="iv"&gt;@current_user&lt;/span&gt;.id&lt;tt&gt;
&lt;/tt&gt;    ]&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Profile&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;find_all_for_admin&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Profile&lt;/span&gt;.find(&lt;span class="sy"&gt;:all&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;find_by_id&lt;/span&gt;(id)&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;public = ? or user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;, &lt;span class="iv"&gt;@current_user&lt;/span&gt;.id]&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Profile&lt;/span&gt;.find(id, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;find_friend_profiles_of&lt;/span&gt;(user)&lt;tt&gt;
&lt;/tt&gt;    conditions = [&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;public = ? or user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pc"&gt;true&lt;/span&gt;, &lt;span class="iv"&gt;@current_user&lt;/span&gt;.id]&lt;tt&gt;
&lt;/tt&gt;    user.friend_profiles.find(&lt;span class="sy"&gt;:all&lt;/span&gt;, &lt;span class="sy"&gt;:conditions&lt;/span&gt; =&amp;gt; conditions)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;ProfilesController&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  before_filter &lt;span class="sy"&gt;:build_profile_finder&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search_by_state&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="iv"&gt;@profile_finder&lt;/span&gt;.find_all_by_state(params[&lt;span class="sy"&gt;:state&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;search_by_last_name&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profiles&lt;/span&gt; = &lt;span class="iv"&gt;@profile_finder&lt;/span&gt;.find_all_by_last_name(params[&lt;span class="sy"&gt;:last_name&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profile&lt;/span&gt; = &lt;span class="iv"&gt;@profile_finder&lt;/span&gt;.find_by_id(params[&lt;span class="sy"&gt;:id&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  private &lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;build_profile_finder&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profile_finder&lt;/span&gt; = &lt;span class="co"&gt;ProfileFinder&lt;/span&gt;.new&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@profile_finder&lt;/span&gt;.current_user = current_user&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Now when our new pair is asked to implement the ability to find profiles by last name, they know project standards dictate the use of the relevant finder. Their integration test fails since the method does not exist. When they create it, they see the other privacy logic in close proximity, and now have enough information to initiate a conversation with the customer about privacy. We’ve found that conscientious use of finder objects enables us to have very consistent finding logic, which reduces bugs and user confusion. It also simplifies tests, as the tests benefit from a clear scope definition, and are not cluttered by other model logic. It also makes finder rules discoverable as they are now in a centralized location.&lt;/p&gt;


	&lt;p&gt;We decided that finder objects are allowed to know model fields and conditions, sql, etc. They are also allowed to call with_scope on that model. This all fits with their responsibility of knowing how to find the appropriate model.&lt;/p&gt;


	&lt;h2&gt;Usage Tips&lt;/h2&gt;


	&lt;p&gt;For bonus points, combine this with dependency injection. We do, and we’re loving it. It allows context like the current user to be injected, and then the caller doesn’t even need to know that the method requires the current user, as that’s simply a privacy constraint.&lt;/p&gt;


	&lt;p&gt;We’ve also discovered that for purposes of authorization rules (are you allowed to see this record?), it helps for the caller to provide intent. If a record otherwise exists but the user does not have permission to follow through with the stated intent, nil is returned.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;profile_finder.find_by_id(id, &lt;span class="sy"&gt;:for&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:show&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;profile_finder.find_by_id(id, &lt;span class="sy"&gt;:for&lt;/span&gt; =&amp;gt; &lt;span class="sy"&gt;:update&lt;/span&gt;)&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;I may be able to see my friend’s profile, but I can’t update it. Finder objects already need some authorization information to restrict queries. It makes sense to block out access at the data layer. If I can’t fetch the record, I can’t update it. My code can be required to provide an intent with the :for attribute; you may rig your finder to raise an error if no intention is indicated.&lt;/p&gt;


	&lt;p&gt;Sometimes, even just the act of determining the permissions of the current user and the object at hand can be complicated, and given that you may need to apply action-oriented permissions for an entity type in more than one place in your code, it makes sense to implement a Permitter (in this case, a ProfilePermitter). The topic of permitters may warrant another blog post.&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=2oAVoI"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=2oAVoI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=XjJfLi"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=XjJfLi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=uwNgqi"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=uwNgqi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=5smRJi"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=5smRJi" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/312704893" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/06/16/finder-objects-in-rails</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>fogle</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-06-12:572</id>
    <published>2008-06-12T18:59:00Z</published>
    <updated>2008-06-12T18:59:30Z</updated>
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/310611309/google-i-o-impressions" rel="alternate" type="text/html" />
    <title>Google I/O Impressions</title>
<summary type="html">&lt;p&gt;Atomic Object provides some fantastic benefits. For example, Atomic sends each developer to one conference each year. They pay the registration, air fare, and hotel. They also pay as if I had been actually working. It’s fantastic to have an employer that not only wants me to be a better developer, but takes the extra steps to help me improve.&lt;/p&gt;


	&lt;p&gt;This year, I attended the &lt;a href="http://code.google.com/events/io/"&gt;Google I/O&lt;/a&gt; conference in San Francisco. The first day’s keynote was essentially a bird’s eye view of the conference content. The overall theme was that Google wants to advance capabilities of web applications. They understand that their growth is tied to the growth of internet use at large. They understand that internet use will increase as web applications improve. They want to facilitate the improvement of web applications by providing tools for developers to use. As much as possible, these tools are open source, and not bound to Google, as that is the best way to empower the advancement of the internet.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;Atomic Object provides some fantastic benefits. For example, Atomic sends each developer to one conference each year. They pay the registration, air fare, and hotel. They also pay as if I had been actually working. It’s fantastic to have an employer that not only wants me to be a better developer, but takes the extra steps to help me improve.&lt;/p&gt;


	&lt;p&gt;This year, I attended the &lt;a href="http://code.google.com/events/io/"&gt;Google I/O&lt;/a&gt; conference in San Francisco. The first day’s keynote was essentially a bird’s eye view of the conference content. The overall theme was that Google wants to advance capabilities of web applications. They understand that their growth is tied to the growth of internet use at large. They understand that internet use will increase as web applications improve. They want to facilitate the improvement of web applications by providing tools for developers to use. As much as possible, these tools are open source, and not bound to Google, as that is the best way to empower the advancement of the internet.&lt;/p&gt;
&lt;p&gt;From any other company, I would consider this pure rhetoric, but after those two days in San Francisco, I’m inclined to take Google at its word. All but one of the technologies I looked at is open source and/or an open standard. In addition, they have a financial interest in the continued improvement of internet applications.&lt;/p&gt;


	&lt;h2&gt;&lt;a href="http://code.google.com/apis/opensocial/"&gt;OpenSocial&lt;/a&gt;&lt;/h2&gt;


	&lt;p&gt;OpenSocial is an open standard for integration of third-party social applications. As OpenSocial is an integration standard, it has two sides. Social networks (&lt;a href="http://www.orkut.com"&gt;orkut&lt;/a&gt;, for example) act as OpenSocial containers, where their members can add third-party applications to their profile. Third-party developers provide OpenSocial applications which can then plug into an OpenSocial container. This allows, at least in theory, someone to create an application that can run on many different social networks without having to customize for each one. In reality, there will probably be some &lt;span class="caps"&gt;GUI&lt;/span&gt; customization required, and not all OpenSocial containers will allow all OpenSocial applications.&lt;/p&gt;


	&lt;h2&gt;&lt;a href="http://code.google.com/appengine/"&gt;Google App Engine&lt;/a&gt;&lt;/h2&gt;


	&lt;p&gt;Google App Engine is a web framework designed to scale, hosted on Google’s server cloud. Google App Engine is the one technology covered that is not open source. This is forgivable since it’s specifically designed to run on Google’s server cloud. Since I don’t (yet) have a sophisticated cluster of thousands of servers with crazy infrastructure, I don’t begrudge them the closed source nature.&lt;/p&gt;


	&lt;p&gt;The intent of Google App Engine is to make it so any application using this framework will automatically scale. The free version allows up to five million page views per month, and the pay version, while not yet available, will be metered, and should, by Google’s estimates, be somewhere in the ballpark of forty dollars a month for 10 million page views per month. The datastore uses &lt;a href="http://labs.google.com/papers/bigtable.html"&gt;Bigtable&lt;/a&gt;, Google’s custom database.&lt;/p&gt;


	&lt;h3&gt;Exciting features&lt;/h3&gt;


	&lt;p&gt;Google App Engine provides (almost) guaranteed scalability! In theory, the framework does not allow any operation that is not scalable. In practice, no framework will overcome bad development practices, but this framework makes it a lot harder to screw it up.&lt;/p&gt;


	&lt;p&gt;The application gets free, high quality hosting running on Google’s server cloud. Up to 5 million page views per month are free.&lt;/p&gt;


	&lt;p&gt;The application gets convenient access to most Google APIs. It’s super easy to do logins using Google Accounts.&lt;/p&gt;


	&lt;h3&gt;Disappointing limitations.&lt;/h3&gt;


	&lt;p&gt;There is currently no support for schema migrations. There is no support for batch data operations. The application may not fetch more than 1000 records in a query. Requests are time-limited. This means I cannot rename a field after it has been deployed. To be fair, it is technically possible, but I would have to make one request for each 1000 records, which would be tedious and error-prone at best.&lt;/p&gt;


	&lt;p&gt;Bigtable does not yet back-fill default values into existing records when you add new fields to a type.   For the old records, the field isn’t null, it’s just not there, and you cannot query for that condition.  (Fields may have null values and be queried as such, but these “missing” fields will not match a query for null.)  In a production environment, this can lead to vanishing data when querying based on such fields.  Currently there is no mechanism (like migrations in &lt;a href="http://www.rubyonrails.org/"&gt;Rails&lt;/a&gt;) to help you adjust your data.&lt;/p&gt;


	&lt;p&gt;BigTable does not support substring searches.  String equality and begins-with style conditions are supported, but searching based on partial matches mid-string doesn’t work yet.&lt;/p&gt;


	&lt;p&gt;While Bigtable encourages you to create most objects with a parent object, you cannot move them to a different parent after the fact. The record would have to be deleted and recreated, which would also require updating every reference to that object.&lt;/p&gt;


	&lt;p&gt;There is hope however. App Engine is a beta product, and the presenter mentioned that they are prioritizing support for offline processing, like schema migrations, as well as full text search.&lt;/p&gt;


	&lt;h2&gt;&lt;a href="http://gears.google.com/"&gt;Gears&lt;/a&gt;&lt;/h2&gt;


	&lt;p&gt;Last year, Google released Google Gears, which is a plugin available for all major browsers. It installs like flash, but provides a client-side JavaScript &lt;span class="caps"&gt;API&lt;/span&gt; for doing various things that are impossible in current browsers. The goal of the project is to make more desktop-like behavior possible for web apps. The major features currently in place center around making it possible to take the web application offline.&lt;/p&gt;


	&lt;p&gt;Google made a big deal about debranding Gears. It is now just Gears, rather than Google Gears. The rationale is that they don’t want corporate branding to hinder adoption. The purpose of Gears is to push browsers forward, not advance Google’s brand. They demoed some new features, that are currently only prototypes.&lt;/p&gt;


	&lt;p&gt;Gears will allow applications to upload multiple files with a smooth interface, using a multiple file selection box, instead of the current system of one input field per file. Progress information will also be available to the client. Uploads will also be resumable.&lt;/p&gt;


	&lt;p&gt;Gears will allow the application to give system notifications, in a system similar to growl. It may even provide growl integration. This is to replace informational messages that would normally get JavaScript alert boxes or custom design work.&lt;/p&gt;


	&lt;h2&gt;Closing Thoughts&lt;/h2&gt;


	&lt;p&gt;Google is putting a lot of effort into making it possible to do more powerful things with web applications. While I didn’t come away with anything that helps me today, I did come away with a new vision for the possibilities of the web, and some tools to achieve those possibilities.&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=xeZByI"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=xeZByI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=vzT8wi"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=vzT8wi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=41F2Ei"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=41F2Ei" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=vaJzvi"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=vaJzvi" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/310611309" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/06/12/google-i-o-impressions</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>bacon</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-06-10:575</id>
    <published>2008-06-10T19:53:00Z</published>
    <updated>2008-06-10T19:55:14Z</updated>
    <category term="Languages" />
    <category term="Tips" />
    <category term="Tools" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/309071356/ruby-and-unicode-win32-messageboxes" rel="alternate" type="text/html" />
    <title>Ruby and Unicode Win32 MessageBoxes</title>
<content type="html">
            &lt;p&gt;Have you ever needed to display Unicode characters in a Win32 MessageBox from a Ruby script? My pair and I needed to do just that and so I thought I would share what we found.&lt;/p&gt;


	&lt;p&gt;There are a number of ways to access Win32 calls from a Ruby script. The code we were working with did the following:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;dl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show_message_box&lt;/span&gt;(message, title)&lt;tt&gt;
&lt;/tt&gt;  mb_ok = &lt;span class="i"&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  mb_iconexclamation = &lt;span class="i"&gt;48&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  user32 = &lt;span class="co"&gt;DL&lt;/span&gt;.dlopen(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;user32&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  message_box = user32[&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;MessageBoxA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;ILSSI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;]&lt;tt&gt;
&lt;/tt&gt;  message_box.call(&lt;span class="i"&gt;0&lt;/span&gt;, message, title, mb_ok | mb_iconexclamation)&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;That works well until you need to display Unicode characters in the MessageBox. It turns out the &lt;strong&gt;MessageBoxA&lt;/strong&gt; version of the function is for &lt;span class="caps"&gt;ASCII&lt;/span&gt; characters. There is another version of the &lt;span class="caps"&gt;API&lt;/span&gt; call, &lt;strong&gt;MessageBoxW&lt;/strong&gt;, that can handle Unicode, or &lt;em&gt;wide&lt;/em&gt; characters. So the issue becomes converting your Ruby string into a wide string so it can be passed to &lt;strong&gt;MessageBoxW&lt;/strong&gt;.  The &lt;strong&gt;MultiByteToWideChar&lt;/strong&gt; Win32 call can do this for you. And the &lt;strong&gt;windows-pr&lt;/strong&gt; gem (from &lt;a href="http://rubyforge.org/projects/win32utils"&gt;win32utils&lt;/a&gt;) adds a nice ruby wrapper around the function.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;gem install windows-pr&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;windows/unicode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;include &lt;span class="co"&gt;Windows&lt;/span&gt;::&lt;span class="co"&gt;Unicode&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;&lt;span class="gv"&gt;$KCODE&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;UTF8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;str = multi_to_wide(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;This is a test&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;This seemed to work quite well for the most part. However, from time to time we would see garbage text showing up at the end of our messages. It could easily be reproduced if the message was very short.&lt;/p&gt;


	&lt;p&gt;&lt;img src="http://spin.atomicobject.com/assets/2008/6/10/multi_to_wide.png" /&gt;&lt;/p&gt;


	&lt;p&gt;Having done enough C/C++ coding to recognize a string that was not being null terminated, we experimented with adding null characters to the end of the string. It turns out a &lt;em&gt;wide&lt;/em&gt; null terminator (”\0\0”) is needed. The following code will properly display Unicode characters in a Win32 MessageBox:&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;dl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;require &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;windows/unicode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;include &lt;span class="co"&gt;Windows&lt;/span&gt;::&lt;span class="co"&gt;Unicode&lt;/span&gt; &lt;tt&gt;
&lt;/tt&gt;&lt;span class="gv"&gt;$KCODE&lt;/span&gt;=&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;UTF8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;win32_wide&lt;/span&gt;(text)&lt;tt&gt;
&lt;/tt&gt;  multi_to_wide(text) + &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="ch"&gt;\0&lt;/span&gt;&lt;span class="ch"&gt;\0&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;show_message_box&lt;/span&gt;(message, title)&lt;tt&gt;
&lt;/tt&gt;  mb_ok = &lt;span class="i"&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  mb_iconexclamation = &lt;span class="i"&gt;48&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;tt&gt;
&lt;/tt&gt;  user32 = &lt;span class="co"&gt;DL&lt;/span&gt;.dlopen(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;user32&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  message_box = user32[&lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;MessageBoxW&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;ILSSI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt;]&lt;tt&gt;
&lt;/tt&gt;  message_box.call(&lt;span class="i"&gt;0&lt;/span&gt;, win32_wide(message), win32_wide(title), &lt;tt&gt;
&lt;/tt&gt;                             mb_ok | mb_iconexclamation)&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;show_message_box &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;MessageBox displayed from Ρουμπίνι&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;¡Alert!&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=xQZ1MI"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=xQZ1MI" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=cQm4Xi"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=cQm4Xi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=vKuHJi"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=vKuHJi" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=3Dou3i"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=3Dou3i" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/309071356" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/06/10/ruby-and-unicode-win32-messageboxes</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>karlesky</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-05-29:338</id>
    <published>2008-05-29T01:54:00Z</published>
    <updated>2008-05-29T02:29:44Z</updated>
    <category term="Books, Papers, Articles &amp; Links" />
    <category term="Business of Software" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/300247866/make-strategy-like-you-make-software" rel="alternate" type="text/html" />
    <title>Make strategy like you make software?</title>
<content type="html">
            &lt;p&gt;&lt;a href="http://allankelly.blogspot.com"&gt;Allan Kelly&lt;/a&gt; draws some interesting conclusions about Agile software development methods as related to forming business strategy. The impetus for his post was a piece in the &lt;a href="http://sloanreview.mit.edu"&gt;&lt;span class="caps"&gt;MIT&lt;/span&gt; Sloan Review&lt;/a&gt; entitled “&lt;a href="http://sloanreview.mit.edu/smr/issue/2008/spring/15/"&gt;Should you build strategy like you build software?&lt;/a&gt;.”&lt;/p&gt;


	&lt;p&gt;From Allan’s “&lt;a href="http://allankelly.blogspot.com/2008/05/make-strategy-like-you-make-software.html"&gt;Make strategy like you make software?&lt;/a&gt;”:&lt;/p&gt;


&lt;blockquote&gt;
...for companies which use a lot of technology software and strategy are increasingly converging. Ultimately your software is your strategy – so much so that I sometimes imagine software code as liquid strategy.
&lt;/blockquote&gt;

&lt;blockquote&gt;
...many of the practices and techniques used in Agile software development can be applied to strategy formation and execution. McFarland focus on techniques such as small iterations, collective ownership, overlapping phases, direction changes (i.e. refactoring), organising around people not tools and abolishing big up front design.
&lt;/blockquote&gt;

&lt;blockquote&gt;
It is not only software development where managers and companies have suffered from the &lt;strong&gt;Illusion of Control&lt;/strong&gt; it occurs in strategy formation and planning. Strategy formation is an emergent process, in the same way that software design is emergent.
&lt;/blockquote&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=5NbyzH"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=5NbyzH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=6oanHh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=6oanHh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=6PgIIh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=6PgIIh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=C53zfh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=C53zfh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/300247866" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/05/29/make-strategy-like-you-make-software</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>karlesky</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-05-29:335</id>
    <published>2008-05-29T01:47:00Z</published>
    <updated>2008-06-09T00:51:52Z</updated>
    <category term="Announcements" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/300224883/thecommon-org-in-the-news" rel="alternate" type="text/html" />
    <title>TheCommon.org in the News</title>
<content type="html">
            &lt;p&gt;&lt;strong&gt;More client news:&lt;/strong&gt;&lt;/p&gt;


&lt;blockquote&gt;
The idea came to Rick DeVos while he listened to church leaders describe how they were going to use email to help people get involved in church activities.

	&lt;p&gt;“It seemed like a nightmare of useless emails filling up everyone’s mailboxes, and an administrative nightmare,” DeVos says. The more he thought about it, the more he realized there was no online tool to help organizations connect people in need with the people who could help.&lt;/p&gt;


So DeVos talked to Ben Gott and the two created &lt;a href="http://thecommon.org"&gt;TheCommon.org&lt;/a&gt;, an online site that helps people lend a helping hand. 
&lt;/blockquote&gt;

	&lt;p&gt;&lt;a href="http://www.rapidgrowthmedia.com/timnews/common0501.aspx"&gt;Grand Rapids innovators develop online ‘helping hand’&lt;/a&gt;&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=PZO6jH"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=PZO6jH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=PmKj4h"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=PmKj4h" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=Bz8iCh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=Bz8iCh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=jSnsZh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=jSnsZh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/300224883" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/05/29/thecommon-org-in-the-news</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>karlesky</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-05-29:333</id>
    <published>2008-05-29T01:29:00Z</published>
    <updated>2008-05-29T01:54:32Z</updated>
    <category term="Announcements" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/300224884/circlebuilder-in-the-news" rel="alternate" type="text/html" />
    <title>CircleBuilder in the News</title>
<content type="html">
            &lt;p&gt;&lt;strong&gt;Client news:&lt;/strong&gt;&lt;/p&gt;


&lt;blockquote&gt;
So far the 2-year-old firm based out of Franklin has three full-time employees and has raised more than $1.3 million in seed money from venture capital firms and angel investors.

	&lt;p&gt;&lt;a href="http://circlebuilder.com"&gt;CircleBuilder&lt;/a&gt;, which offers Yahoo Groups-like services to churches, is about to bring in another “big chunk of change” as it prepares to close another round of seed money investing. The firm hopes to open its site to the general public this summer and hire 15-20 people by the end of next year.&lt;/p&gt;


“I am trying to do all of my business in Michigan,” Brown says. “My lawyers, &lt;span class="caps"&gt;CPA&lt;/span&gt; and technology firm &lt;em&gt;[that’s &lt;a href="http://atomicobject.com"&gt;us&lt;/a&gt;! -ed.]&lt;/em&gt; are in Michigan. You have got to start something in this state because we’re too reliant on the auto industry.” 
&lt;/blockquote&gt;

	&lt;p&gt;&lt;a href="http://www.metromodemedia.com/innovationnews/circlebuilder0068.aspx"&gt;CircleBuilder paces its growth, looks to add 15-20 jobs in two years&lt;/a&gt;&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=kxKcOH"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=kxKcOH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=5DnUjh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=5DnUjh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=OJdTmh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=OJdTmh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=vdTFhh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=vdTFhh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/300224884" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/05/29/circlebuilder-in-the-news</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>fogle</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-04-30:167</id>
    <published>2008-04-30T21:30:00Z</published>
    <updated>2008-05-24T01:47:50Z</updated>
    <category term="Test Driven Development" />
    <category term="Tips" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/297273383/model-generation-coding-for-simplicity" rel="alternate" type="text/html" />
    <title>Model Generation - Coding for Simplicity</title>
<summary type="html">&lt;p&gt;Rails convention calls for test data to be stored in fixtures. This works very well for the first few iterations, but this approach quickly becomes unwieldy. Often, many records are required, which can lead to poor record names like &lt;b&gt;event_46&lt;/b&gt;. Even when naming conscientiously, it’s difficult to give each record a meaningful name in a global scope. When adding required fields to models, every record needs to be updated, which can be tedious for large test data sets, and very often the updates have an undesired impact on a broad set of tests.&lt;/p&gt;


	&lt;p&gt;We’d prefer to build up test data closer to home, in the test itself, so we can see the connection between the data and the expectations. Furthermore, if we structure our tests this way the data is isolated from other tests so we’re free to tinker with it without breaking those other tests. The drawback is that manual creation of ActiveRecord objects in your test code can become as tedious to write and maintain as fixtures.&lt;/p&gt;


	&lt;p&gt;Enter &lt;b&gt;Generate&lt;/b&gt; – a technique that centralizes ActiveRecord model generation for test data, removes the need for fixtures, and avoids the tedium and verbosity of in-test model creation.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;Rails convention calls for test data to be stored in fixtures. This works very well for the first few iterations, but this approach quickly becomes unwieldy. Often, many records are required, which can lead to poor record names like &lt;b&gt;event_46&lt;/b&gt;. Even when naming conscientiously, it’s difficult to give each record a meaningful name in a global scope. When adding required fields to models, every record needs to be updated, which can be tedious for large test data sets, and very often the updates have an undesired impact on a broad set of tests.&lt;/p&gt;


	&lt;p&gt;We’d prefer to build up test data closer to home, in the test itself, so we can see the connection between the data and the expectations. Furthermore, if we structure our tests this way the data is isolated from other tests so we’re free to tinker with it without breaking those other tests. The drawback is that manual creation of ActiveRecord objects in your test code can become as tedious to write and maintain as fixtures.&lt;/p&gt;


	&lt;p&gt;Enter &lt;b&gt;Generate&lt;/b&gt; – a technique that centralizes ActiveRecord model generation for test data, removes the need for fixtures, and avoids the tedium and verbosity of in-test model creation.&lt;/p&gt;
Example RSpec usage:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;describe &lt;span class="co"&gt;Event&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;#find_all_by_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  it &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;finds events with matching names&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    event = &lt;span class="co"&gt;Generate&lt;/span&gt;.event&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Event&lt;/span&gt;.find_all_by_name(event.name).should include(event)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  it &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;does not find events with different names&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    event = &lt;span class="co"&gt;Generate&lt;/span&gt;.event(&lt;span class="sy"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;some name&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Event&lt;/span&gt;.find_all_by_name(&lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;a different name&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).should include(event)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  it &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;does not find private events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    event = &lt;span class="co"&gt;Generate&lt;/span&gt;.event(&lt;span class="sy"&gt;:private&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;true&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Event&lt;/span&gt;.find_all_by_name(event.name).should_not include(event)&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Generate&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.event(options={})&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Event&lt;/span&gt;.create!(options.reverse_merge(&lt;span class="sy"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;sample event name&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:private&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;false&lt;/span&gt;))&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;The &lt;strong&gt;Generate&lt;/strong&gt; class makes building an &lt;strong&gt;Event&lt;/strong&gt; very convenient. The test only needs to specify attributes it cares about (if any) and &lt;strong&gt;Generate&lt;/strong&gt; will worry about making the &lt;strong&gt;Event&lt;/strong&gt; valid. If at some point the event requires a &lt;strong&gt;starts_at&lt;/strong&gt; field, the &lt;strong&gt;Generate&lt;/strong&gt; class can simply add that to the default attributes, and all tests will continue to pass without alteration. This becomes especially useful when building webs of objects.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;15&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;25&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;29&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;describe &lt;span class="co"&gt;Event&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;#attendee_count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  it &lt;span class="s"&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="k"&gt;returns the number of yes rsvps&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="r"&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    event = &lt;span class="co"&gt;Generate&lt;/span&gt;.event(&lt;span class="sy"&gt;:attendees&lt;/span&gt; =&amp;gt; [&lt;span class="co"&gt;Generate&lt;/span&gt;.user, &lt;span class="co"&gt;Generate&lt;/span&gt;.user])&lt;tt&gt;
&lt;/tt&gt;    event.attendee_count.should == &lt;span class="i"&gt;2&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;Generate&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.event(options={})&lt;tt&gt;
&lt;/tt&gt;    attendees = options.delete(&lt;span class="sy"&gt;:attendees&lt;/span&gt;) || []&lt;tt&gt;
&lt;/tt&gt;    event = &lt;span class="co"&gt;Event&lt;/span&gt;.create!(options.reverse_merge(&lt;span class="sy"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;sample event name&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="sy"&gt;:private&lt;/span&gt; =&amp;gt; &lt;span class="pc"&gt;false&lt;/span&gt;))&lt;tt&gt;
&lt;/tt&gt;    attendees.each &lt;span class="r"&gt;do&lt;/span&gt; |attendee|&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="co"&gt;Generate&lt;/span&gt;.rsvp(&lt;span class="sy"&gt;:event&lt;/span&gt; =&amp;amp;gt; event, &lt;span class="sy"&gt;:user&lt;/span&gt; =&amp;gt; attendee)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    event&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.user(options={})&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@user_count&lt;/span&gt; ||= &lt;span class="i"&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="iv"&gt;@user_count&lt;/span&gt; += &lt;span class="i"&gt;1&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;User&lt;/span&gt;.create!(options.reverse_merge(&lt;span class="sy"&gt;:email&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="il"&gt;&lt;span class="dl"&gt;#{&lt;/span&gt;user_count&lt;span class="dl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="k"&gt;@example.com&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;))&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="pc"&gt;self&lt;/span&gt;.rsvp(options={})&lt;tt&gt;
&lt;/tt&gt;    options[&lt;span class="sy"&gt;:user&lt;/span&gt;] ||= &lt;span class="co"&gt;Generate&lt;/span&gt;.user&lt;tt&gt;
&lt;/tt&gt;    options[&lt;span class="sy"&gt;:event&lt;/span&gt;] ||= &lt;span class="co"&gt;Generate&lt;/span&gt;.event&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="co"&gt;Rsvp&lt;/span&gt;.create!(options.reverse_merge(&lt;span class="sy"&gt;:response&lt;/span&gt; =&amp;gt; &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;))&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


	&lt;p&gt;Once again, the test is very clear. A reader can tell exactly what’s going on without hunting through fixtures.  Furthermore, adding attendees to the &lt;strong&gt;Event&lt;/strong&gt; in this test will not affect any other tests.&lt;/p&gt;


	&lt;p&gt;We’re using this approach on a large project currently under development. As our project has grown, the complexity of our data model has increased, making our ActiveRecord models more and more interrelated.  But rather than feeling the pain of fixture maintenance and test adjustment, we’re able to move freely and quickly by updating one or two &lt;strong&gt;Generate&lt;/strong&gt; functions at a time without impacting any other tests, while maintaining the flexibility to test anything new that comes our way.&lt;/p&gt;


	&lt;p&gt;The nice thing about &lt;strong&gt;Generate&lt;/strong&gt; is that it doesn’t require a supporting library or up-front investment in tool building, short of knowing how to use ActiveRecord to instantiate your models.  The trick is in the technique itself – leaving you the option to apply it in your projects in whatever way best meets your needs.&lt;/p&gt;


	&lt;h3&gt;References:&lt;/h3&gt;


	&lt;p&gt;The &lt;strong&gt;Generate&lt;/strong&gt; technique is very similar to the &lt;a href="http://martinfowler.com/bliki/ObjectMother.html"&gt;ObjectMother testing pattern&lt;/a&gt;. Please see &lt;a href="http://www.xpuniverse.com/2001/pdfs/Testing03.pdf"&gt;“ObjectMother: Easing Test Object Creation in XP”&lt;/a&gt; by Peter Schuh and Stephanie Punke.&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=qfye9H"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=qfye9H" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=L7Fzuh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=L7Fzuh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=X5zn6h"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=X5zn6h" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=8CSiah"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=8CSiah" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/297273383" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/04/30/model-generation-coding-for-simplicity</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>karlesky</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-04-24:166</id>
    <published>2008-04-24T19:42:00Z</published>
    <updated>2008-05-20T13:28:05Z</updated>
    <category term="Announcements" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/297273384/spiffy-updated-bios" rel="alternate" type="text/html" />
    <title>Spiffy Updated Bios</title>
<content type="html">
            &lt;p&gt;I resisted the urge for a pun about meeting the "Atoms Family" or some cringe-inducing line.&lt;/p&gt;

&lt;p&gt;We've had some new Atoms join the Molecule (&lt;a href="http://atomicobject.com/pages/Nate+Lokers"&gt;Nate Lokers&lt;/a&gt; &amp; &lt;a href="http://atomicobject.com/pages/Eric+Hass"&gt;Eric Hass&lt;/a&gt;). And, we decided it was time to update everybody's bios on our website. We get really positive feedback from customers and potential customers about our bios. The visibility into our company and culture via the bios seems to really connect well with the outside world. So much so that we give periodic attention to them and always feature them as a link on our website's front page.&lt;/p&gt;

&lt;p&gt;So, feel free to poke around and meet the &lt;a href="http://atomicobject.com/pages/Our+People"&gt;Atomic Object Molecule.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We continue to look for good people to hire. If you're interested, &lt;a href="http://atomicobject.com/pages/Working+at+Atomic+Object"&gt;start the process&lt;/a&gt;.&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=v9nPcH"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=v9nPcH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=CcBxZh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=CcBxZh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=niNL4h"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=niNL4h" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=aAmCAh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=aAmCAh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/297273384" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/04/24/spiffy-updated-bios</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>karlesky</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-04-02:164</id>
    <published>2008-04-02T14:34:00Z</published>
    <updated>2008-05-09T19:53:25Z</updated>
    <category term="UI/UX" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/297273385/simplicity-what-we-can-learn-about-usability" rel="alternate" type="text/html" />
    <title>Simplicity: What We Can Learn About Usability</title>
<content type="html">
            &lt;p&gt;&lt;strong&gt;A Comic by Eric Burke:&lt;/strong&gt;&lt;/p&gt;

&lt;img src="/assets/2008/4/4/usabilitysimplicitycomic.png" alt="Simplicity in User Interface Design" /&gt;

&lt;p&gt;&lt;em&gt;via [&lt;a href="http://stuffthathappens.com/blog/2008/03/05/simplicity/"&gt;stuff that happens&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=zQJNJH"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=zQJNJH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=05Aumh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=05Aumh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=4l94ch"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=4l94ch" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=4ig9Gh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=4ig9Gh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/297273385" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/04/02/simplicity-what-we-can-learn-about-usability</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>karlesky</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-03-31:163</id>
    <published>2008-03-31T18:31:00Z</published>
    <updated>2008-05-02T21:58:17Z</updated>
    <category term="Agile Practices" />
    <category term="Books, Papers, Articles &amp; Links" />
    <category term="Software Design" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/297273386/reality-driven-development" rel="alternate" type="text/html" />
    <title>Reality-Driven Development</title>
<content type="html">
            &lt;p&gt;A software developer by the name of &lt;a href="http://duartes.org/gustavo/about.aspx"&gt;Gustavo Duarte&lt;/a&gt; wrote something quite fascinating last month on &lt;strong&gt;&lt;a href="http://duartes.org/gustavo/blog/post/2008/02/27/Reality-Driven-Development.aspx"&gt;Reality-Driven Development&lt;/a&gt;&lt;/strong&gt;. Apparently, his writing drew many Slashdot readers to his humble blog.&lt;/p&gt;


	&lt;p&gt;In his post, Duarte draws from such disparate sources and ideas as physicist &lt;a href="http://duartes.org/gustavo/blog/post/2008/02/20/Richard-Feynman-Challenger-Disaster-Software-Engineering.aspx"&gt;Richard Feynman’s thoughts on the Challenger disaster&lt;/a&gt;, natural selection, a &lt;a href="http://www.ussg.iu.edu/hypermail/linux/kernel/0111.3/1957.html"&gt;linux discussion thread&lt;/a&gt;, the book &lt;em&gt;&lt;a href="http://www.amazon.com/Built-Last-Successful-Visionary-Companies/dp/0887306713/ref=ed_oe_h"&gt;Built to Last&lt;/a&gt;&lt;/em&gt;, and others to make his case for what he calls Reality-Driven Development.&lt;/p&gt;


	&lt;p&gt;Duarte says that “reality is invited in via experiments” and defines Reality-Driven Development thusly:&lt;/p&gt;


&lt;blockquote&gt;A good software development process should optimize experimentation and improve feedback from reality. This is what I mean by reality-driven development. And in software the most important realities are user experience and technical quality, while the primary experiments are working software and code.&lt;/blockquote&gt;

	&lt;p&gt;He criticizes big up-front design:&lt;/p&gt;


&lt;blockquote&gt;And rigid upfront design is a sure way to a crappy code base or engineering disasters. Alistair Cockburn put it best: “With design I can think very fast, but my thinking is full of little holes.”&lt;/blockquote&gt;

	&lt;p&gt;He also casts Agile development techniques in an interesting light:&lt;/p&gt;


&lt;blockquote&gt;There is no specific reality-driven methodology. The Agile principles have a lot in common with these ideas (and certainly influenced them), but the devil is in the details. I prefer to think of software engineering in terms of a toolbox, full of techniques we pick and choose for the right situation. Process tools for optimizing experimentation include iterative development, executable architecture, continuous integration, and unit testing.&lt;/blockquote&gt;

	&lt;p&gt;It’s difficult to summarize all the juicy todbits. Reading the &lt;a href="http://duartes.org/gustavo/blog/post/2008/02/27/Reality-Driven-Development.aspx"&gt;entire post&lt;/a&gt; is well worth the time.&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=3DYEUH"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=3DYEUH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=VRZLhh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=VRZLhh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=mdi7oh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=mdi7oh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=tB8HVh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=tB8HVh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/297273386" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/03/31/reality-driven-development</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>fox</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-03-24:162</id>
    <published>2008-03-24T14:25:00Z</published>
    <updated>2008-05-02T22:02:16Z</updated>
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/297273387/integrating-java-and-ruby-with-jruby" rel="alternate" type="text/html" />
    <title>Integrating Java and Ruby with JRuby</title>
<content type="html">
            &lt;p&gt;I gave a talk at Calvin College last Thursday for a joint meeting of the &lt;a href="http://gr-ruby.org/"&gt;Michigan Ruby Users Group&lt;/a&gt; and the &lt;a href="http://gr-jug.org/"&gt;Grand Rapids Java User’s Group&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;My topic was using the Java integration features in JRuby.  I’ve posted the slides &lt;a href="http://www.atomicobject.com/files/Java_integration_in_JRuby.pptx"&gt;here&lt;/a&gt;.  If you didn’t attend you may find the slides a little terse.  Nobody likes reading from slides, which often means that the resulting slides aren’t very informative for absentees.&lt;/p&gt;


	&lt;p&gt;After a brief introduction of using Java integration, I addressed a few of the tricks we learned while developing a large desktop application in JRuby from the ground up.  This includes calling Java libraries, overriding Java classes, including functionality from Java into your Ruby classes, and how to make the Java &lt;span class="caps"&gt;API&lt;/span&gt; more ruby-like.  Probably the most useful hint is the diagram and pointer to our &lt;a href="http://archive.jruby.codehaus.org/user/6eadfe0e0709110857t173a1bdlb00a6fbb04b49a9e%40mail.gmail.com"&gt;technique for bundling JRuby apps into a monolithic jar&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;There’s much more to Java integration than I have here; I only discussed the things that were ambiguous in the existing documentation or difficult to nail down.  Comments are welcome so feel free to ask questions about the scanty slides.&lt;/p&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=AMTxcH"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=AMTxcH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=aYwZhh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=aYwZhh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=t1kqSh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=t1kqSh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=di8QMh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=di8QMh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/297273387" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/03/24/integrating-java-and-ruby-with-jruby</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>bereza</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-03-18:161</id>
    <published>2008-03-18T18:12:00Z</published>
    <updated>2008-05-23T16:32:52Z</updated>
    <category term="Tips" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/297273388/trailing-backslash-problem-and-fix-for-rails-and-sqlserver-2005" rel="alternate" type="text/html" />
    <title>Trailing backslash problem and fix for Rails and SQLServer 2005</title>
<content type="html">
            &lt;p&gt;I came across a problem on project where trying to create a new record with a string attribute that ends in \ (backslash) was causing an &lt;span class="caps"&gt;OLE&lt;/span&gt; error to pop up through the &lt;span class="caps"&gt;DBI&lt;/span&gt; driver.&lt;/p&gt;


For example, if you had a model with a string attribute called &lt;em&gt;name&lt;/em&gt; and you tried to do &lt;code&gt;Model.create! :name =&amp;gt; 'foo\\'&lt;/code&gt; you would get the following error:
&lt;pre&gt;&lt;code&gt;
  DBI::DatabaseError: Execute
       OLE error code: 80040E14 in Microsoft OLE DB Provider for SQL Server
          Incorrect syntax near '\'.
       HRESULT error code:0x80020009
          Exception occured.: INSERT INTO completables ([project_id], [type],
                 [description], [completion]) VALUES(2, 'Deliverable', 'bar\', 0)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;This happens with Rails 1.2.3 running on Windows Server 2003 talking to SQLServer 2005 using ruby-dbi 0.0.23 and version 0.1 of the &lt;span class="caps"&gt;ADO&lt;/span&gt; driver.&lt;/p&gt;


	&lt;p&gt;Normally SQLServer allows backslashes anywhere in a string. It uses a single quote as an escape for a single quote. But for some reason a backslash at the end of a string is treated differently.&lt;/p&gt;


	&lt;p&gt;The following bit of code put into &lt;code&gt;environment.rb&lt;/code&gt; will replace backslashes with calls to &lt;acronym title="92"&gt;CHAR&lt;/acronym&gt;. 92 is the &lt;span class="caps"&gt;ASCII&lt;/span&gt; value of backslash.&lt;/p&gt;


&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td title="click to toggle" class="line_numbers"&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;5&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ActiveRecord&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;module&lt;/span&gt; &lt;span class="cl"&gt;ConnectionAdapters&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="r"&gt;class&lt;/span&gt; &lt;span class="cl"&gt;SQLServerAdapter&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="r"&gt;def&lt;/span&gt; &lt;span class="fu"&gt;quote_string&lt;/span&gt;(string)&lt;tt&gt;
&lt;/tt&gt;        string.gsub(&lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="ch"&gt;\'&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;''&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;).gsub(&lt;span class="rx"&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;span class="ch"&gt;\\&lt;/span&gt;&lt;span class="dl"&gt;/&lt;/span&gt;&lt;/span&gt;, &lt;span class="s"&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;' + CHAR + '&lt;/span&gt;&lt;span class="dl"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;      &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class="r"&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class="r"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
          &lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=gJTWnH"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=gJTWnH" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=ya42gh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=ya42gh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=7Lmtjh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=7Lmtjh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/atomic_spin?a=hjQDxh"&gt;&lt;img src="http://feeds.feedburner.com/~f/atomic_spin?i=hjQDxh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atomic_spin/~4/297273388" height="1" width="1"/&gt;</content>  <feedburner:origLink>http://spin.atomicobject.com/2008/03/18/trailing-backslash-problem-and-fix-for-rails-and-sqlserver-2005</feedburner:origLink></entry>
  <entry xml:base="http://spin.atomicobject.com/">
    <author>
      <name>fletcher</name>
    </author>
    <id>tag:spin.atomicobject.com,2008-03-12:160</id>
    <published>2008-03-12T14:03:00Z</published>
    <updated>2008-05-19T21:34:24Z</updated>
    <category term="Tools" />
    <link href="http://feeds.feedburner.com/~r/atomic_spin/~3/297273389/vmware-server-control-scripts" rel="alternate" type="text/html" />
    <title>VMware server control scripts</title>
<content type="html">
            &lt;p&gt;VMware’s free VMware Server product ships with several command line utilities for controlling the server; the vmware-cmd program, in particular, offers control over starting, stopping, registering, and what not on virtual machines. vmware-cmd is a little clunky to use, though, because it requires an absolute path to the virtual machine’s vmx file. And it only works against one virtual machine at a time.&lt;/p&gt;


So I made some Ruby scripts to make vmware-cmd a little easier for me to use. Here’s what I’ve got so far:
	&lt;ol&gt;
	&lt;li&gt;list: lists what virtual machines are running&lt;/li&gt;
		&lt;li&gt;register: registers a virtual machine&lt;/li&gt;
		&lt;li&gt;reset: resets a virtual machine&lt;/li&gt;
		&lt;li&gt;start: starts a virtual machine&lt;/li&gt;
		&lt;li&gt;stop: stops a virtual machine&lt;/li&gt;
		&lt;li&gt;unregister: unregisters a virtual machine&lt;/li&gt;
		&lt;li&gt;what_is_registered: lists the virtual machine registered on this server&lt;/li&gt;
		&lt;li&gt;what_is_not_registered: lists the virtual machines in the virtual machine directory that are not registered&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;The register, reset, start, stop, and unregister commands all take a list of virtual machines to act against. For example:
“register piggy” will register the piggy virtual machine
“stop beaker piggy tortoise” will stop the beaker, piggy, and tortoise virtual machines&lt;/p&gt;


To use these scripts, you need:
	&lt;ol&gt;
	&lt;li&gt;VMware server installed and running on Linux. I doubt the scripts will work on Windows.&lt;/li&gt;
		&lt;li&gt;Ruby installed and on your path&lt;/li&gt;
		&lt;li&gt;Edit the vmx_control.rb file and change the VmLocation constant, at the top, to contain the location your virtual machines are installed in.&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;These scripts a