<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~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:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
 <title type="text" xml:lang="en">Ternary Labs</title>
 
 <link type="text" href="http://ternarylabs.com" rel="alternate" />
 <updated>2011-12-12T14:44:41-08:00</updated>
 <id>http://ternarylabs.com/</id>
 
 <author>
   <name>Georges Auberger</name>
   <email>georges@ternarylabs.com</email>
 </author>
 

 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/ternarylabs" /><feedburner:info uri="ternarylabs" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><logo>http://ternarylabs.com/favicon.png</logo><entry>
   <title>Millisecond Precision Timestamp in Rails with MySQL</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/E97VD67epxg/millisecond-precision-timestamp-in-rails-with-mysql" />
   <updated>2011-09-26T08:21:52-07:00</updated>
   <id>http://ternarylabs.com/2011/09/26/millisecond-precision-timestamp-in-rails-with-mysql</id>
   <content type="html">&lt;p&gt;You may be surprised to hear that MySQL &lt;a href="http://dev.mysql.com/doc/refman/5.5/en/datetime.html"&gt;datetime&lt;/a&gt; column type only store timestamps with second accuracy. Even tho  Ruby Time class supports microsecond representation, this is lost when persisting as timestamp.&lt;/p&gt;

&lt;p&gt;If you need greater precision for your timestamp, you can't rely on the automated Rails support. Fortunately, there is an easy workaround. You can store time representation in the database as seconds, with decimal support, and convert back and worth.&lt;/p&gt;

&lt;p&gt;Here is an example of how to store milliseconds precision. First, define a column in your schema:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateLogEntries&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:log_entries&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decimal&lt;/span&gt; &lt;span class="ss"&gt;:occured_at_in_seconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:precision&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:scale&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; 
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;occured_at_in_seconds&lt;/code&gt; column can now contain decimal representation of time in seconds, with 3 decimal precision which is needed for milliseconds. If you need to go down to the microseconds level, you can increase this to 6.&lt;/p&gt;

&lt;p&gt;Now all we need is to map this field in our model. We could override attribute accessors, but there is a much more elegant way to deal with this in Rails. We're going to use &lt;code&gt;composed_of&lt;/code&gt; macro.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LogEntry&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;composed_of&lt;/span&gt; &lt;span class="ss"&gt;:occured_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;:class_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Time"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;:mapping&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sx"&gt;%w(occured_at_in_seconds to_r)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;:constructor&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="ss"&gt;:converter&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Because &lt;code&gt;Time.at(...)&lt;/code&gt; supports fractional seconds, we can simply retrieve the value from the database and create a &lt;code&gt;Time&lt;/code&gt; object from it.&lt;/p&gt;

&lt;p&gt;Conversely, we can assign a time object or map some other value. In my case, I wanted to be able to assign milliseconds time directly. So the following will be supported:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# Milliseconds since Epoch as returned by new Date().getTime() in Javascript&lt;/span&gt;
&lt;span class="n"&gt;log_entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LogEntry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:occured_at&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1317052347588&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;         

&lt;span class="c1"&gt;# Time object with millisecond precision&lt;/span&gt;
&lt;span class="n"&gt;log_entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LogEntry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:occured_at&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That's all there is to it.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=E97VD67epxg:r6QhlYKAPJ0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=E97VD67epxg:r6QhlYKAPJ0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=E97VD67epxg:r6QhlYKAPJ0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=E97VD67epxg:r6QhlYKAPJ0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=E97VD67epxg:r6QhlYKAPJ0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/E97VD67epxg" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2011/09/26/millisecond-precision-timestamp-in-rails-with-mysql</feedburner:origLink></entry>
 
 <entry>
   <title>Secure Cross Domain iFrame Communication</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/O4YYX1jnMtU/secure-cross-domain-iframe-communication" />
   <updated>2011-03-27T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2011/03/27/secure-cross-domain-iframe-communication</id>
   <content type="html">&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/03/porthole_210x209.jpg" width="210" height="209" class="left"&gt;&lt;/p&gt;

&lt;p&gt;The world of web development is full of limitations and workarounds to overcome those limitations.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.w3.org/TR/html4/present/frames.html#h-16.5"&gt;IFrames&lt;/a&gt; are commonly used as a secure way to embed content from another site. Unfortunately it’s often not enough to have the content live within the frame. A common use case is the display of so called &lt;a href="http://googleblog.blogspot.com/2009/04/whats-rich-media-ad-anyway.html"&gt;rich media ads&lt;/a&gt;. In these kind of ads, the creative within the ad is meant to expand beyond the boundary of the iFrame rectangle. In order to do so, it needs the cooperation of its parent page.&lt;/p&gt;

&lt;p&gt;The "state of the art" method to achieve this is via a mechanism known as &lt;a href="http://www.google.com/support/dfp_premium/bin/answer.py?hl=en&amp;amp;answer=1085693"&gt;iFrame busting&lt;/a&gt;. Unfortunately, this technique greatly compromises the security offered by iFrames because it enables the content in the iFrame to have access to every objects in the parent page context. As such, any ad content can have access to everything the parent page has access to (reading cookies, form fields, etc.), bypassing the &lt;a href="http://en.wikipedia.org/wiki/Same_origin_policy"&gt;same origin&lt;/a&gt; security policy. This overkill solution is being accepted as the de facto standard by everyone in the online ad industry. This needs to stop.&lt;/p&gt;

&lt;p&gt;For the most part, the communication between parent and iFrame can be narrowed down to a few messages (maximize me, shrink me, color me pink, etc.). By enabling a post/subscribe message type of system between the parent and the iFrame, most of the needs can be covered without having to give the iFrame &lt;em&gt;carte blanche&lt;/em&gt; to everything in the parent DOM.&lt;/p&gt;

&lt;p&gt;With HTML 5, a safe mechanism to post messages between windows was introduced ( &lt;a href="https://developer.mozilla.org/en/DOM/window.postMessage"&gt;window.postMessage&lt;/a&gt;). Until the adoption of HTML 5 compliant browsers is wide enough, we're left using other techniques to achieve similar results.&lt;/p&gt;

&lt;p&gt;Such communication techniques have been well described by Michael Mahemoff in his post about &lt;a href="http://softwareas.com/cross-domain-communication-with-iframes"&gt;Cross-Domain Communication with IFrames&lt;/a&gt;. It even has been further enhanced by Piers Lawson in his post about &lt;a href="http://shouldersofgiants.co.uk/Blog/post/2009/08/17/Another-Cross-Domain-iFrame-Communication-Technique.aspx"&gt;Another Cross Domain iFrame Communication Technique&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These posts have inspired the creation of &lt;a href="https://github.com/ternarylabs/porthole/"&gt;Porthole&lt;/a&gt;, a small Javascript library that makes it easy to incorporate this method into existing pages.&lt;/p&gt;

&lt;p&gt;Porthole relies on hidden iFrames (later referred to a as proxy) to exchange information. The caller sets a url fragment with the message to pass. The proxy by virtue of being served from the same origin as the callee, invokes a callback method with an event object that contains the message read from the url fragment. The message signaling mechanism is based on a resize event.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/03/porthole_browser_windows.png" width="465" height="572" class="center"&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Simplified view of iFrame content and iFrame proxies.
&lt;/p&gt;


&lt;p&gt;A proxy iFrame is needed for each direction of message passing (e.g. from Parent to iFrame and/or iFrame to Parent).&lt;/p&gt;

&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/03/porthole_seq_diagram_600x457.png" width="600" height="457" class="center"&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Calling sequence for sending and receiving a message.
&lt;/p&gt;


&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;Include the Javascript.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"porthole.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Create your content iFrame. This is where the guest content lives. Make sure to give it a name.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"guestFrame"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"guestFrame"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://other-domain.com/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Define an event handler if you want to receive messages.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="c"&gt;/*&lt;/span&gt;
&lt;span class="c"&gt;    messageEvent.origin: Protocol and domain origin of the message&lt;/span&gt;
&lt;span class="c"&gt;    messageEvent.data: Message itself&lt;/span&gt;
&lt;span class="c"&gt;    messageEvent.source: Window proxy object, useful to post a response &lt;/span&gt;
&lt;span class="c"&gt;    */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Create a window proxy object on the main page.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;windowProxy&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt; 
    &lt;span class="c"&gt;// Create a proxy window to send to and receive &lt;/span&gt;
    &lt;span class="c"&gt;// messages from the iFrame&lt;/span&gt;
    &lt;span class="nx"&gt;windowProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Porthole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WindowProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'http://other-domain.com/proxy.html'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'guestFrame'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;// Register an event handler to receive messages;&lt;/span&gt;
    &lt;span class="nx"&gt;windowProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Create a window proxy object in the iFrame.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;windowProxy&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt; 
    &lt;span class="c"&gt;// Create a proxy window to send to and receive &lt;/span&gt;
    &lt;span class="c"&gt;// messages from the parent&lt;/span&gt;
    &lt;span class="nx"&gt;windowProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Porthole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WindowProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'http://parent-domain.com/proxy.html'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;// Register an event handler to receive messages;&lt;/span&gt;
    &lt;span class="nx"&gt;windowProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="c"&gt;// handle event&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Send a message.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="nx"&gt;windowProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'supersizeme'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Note that if you have multiple iFrames, you can create as many WindowProxy objects as needed.&lt;/p&gt;

&lt;h2&gt;Compatibility&lt;/h2&gt;

&lt;p&gt;The library has been tested with IE 7-8, Safari 5, Firefox 3.6 and Google Chrome 6.0&lt;/p&gt;

&lt;h2&gt;Demo&lt;/h2&gt;

&lt;p&gt;See it in action at &lt;a href="http://sandbox.ternarylabs.com/porthole/"&gt;http://sandbox.ternarylabs.com/porthole/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Source&lt;/h2&gt;

&lt;p&gt;Download or fork from Github: &lt;a href="https://github.com/ternarylabs/porthole/"&gt;https://github.com/ternarylabs/porthole/&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=O4YYX1jnMtU:OTFoEHCucNU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=O4YYX1jnMtU:OTFoEHCucNU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=O4YYX1jnMtU:OTFoEHCucNU:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=O4YYX1jnMtU:OTFoEHCucNU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=O4YYX1jnMtU:OTFoEHCucNU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/O4YYX1jnMtU" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2011/03/27/secure-cross-domain-iframe-communication</feedburner:origLink></entry>
 
 <entry>
   <title>Control Your Facebook Friends with Geppetto</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/gs0pWG9RmrY/control-your-facebook-friends-with-geppetto" />
   <updated>2011-03-26T13:56:28-07:00</updated>
   <id>http://ternarylabs.com/2011/03/26/control-your-facebook-friends-with-geppetto</id>
   <content type="html">&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/03/marionette_167x250.jpg" width="167" height="250" class="left"&gt;&lt;/p&gt;

&lt;p&gt;When you're doing any significant development against the Facebook api, testing your application can be challenging. Because of the nature of social networks, the content generated by the users is highly unpredictable. If you're expecting any kind of repeatability, you need to control your users and their behavior.&lt;/p&gt;

&lt;p&gt;Fortunately, Facebook introduced not too long ago &lt;a href="http://developers.facebook.com/docs/test_users/"&gt;test users&lt;/a&gt;. Those users can easily be created, befriend each others and generate all sort of content on demand. The api to do so is straight forward and can be incorporated in unit tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ternarylabs/geppetto"&gt;Geppetto&lt;/a&gt; is a command line utility that exposes most of what's available in the api in an easy to use application. It's built on top of the excellent &lt;a href="https://github.com/arsduo/koala/"&gt;Koala&lt;/a&gt; ruby library to interface with Facebook api. Let's take a look at what you can do with it.&lt;/p&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;First let's start by creating a network of 5 friends.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ geppetto create 5 --networked
&lt;/pre&gt;


&lt;p&gt;For each user, you will get a &lt;code&gt;login_url&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;access_token&lt;/code&gt;. The &lt;code&gt;login_url&lt;/code&gt; is useful to view this user on the facebook.com site. Otherwise, you can simply use the &lt;code&gt;access_token&lt;/code&gt; to authenticate them within your own application.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/03/fb_new_user.png" width="573" height="375" class="center"&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
A new test user. Test users seem to always be female.
&lt;/p&gt;


&lt;p&gt;To list all your test users, simply type:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ geppetto list
&lt;/pre&gt;


&lt;p&gt;Now it's time to create some content by having all the users in the network update their status.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ geppetto generate_status
&lt;/pre&gt;


&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/03/fb_status_update.png" width="512" height="256" class="center"&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
A new status update.
&lt;/p&gt;


&lt;p&gt;Let's engage in some social interactions. Each user will comment and like each other's posts.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ geppetto generate_comments
$ geppetto generate_likes
&lt;/pre&gt;


&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/03/fb_like.png" width="486" height="172" class="center"&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Liking and commenting.
&lt;/p&gt;


&lt;p&gt;You can also create and upload "photos".&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ geppetto generate_images
&lt;/pre&gt;


&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/03/fb_photo.png" width="508" height="295" class="center"&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
A photo is posted.
&lt;/p&gt;


&lt;p&gt;Geppetto provides a couple of shortcuts to populate content.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ geppetto build
&lt;/pre&gt;


&lt;p&gt;Will build a network of test users and generate posts, likes, comments and images.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ geppetto frenzy
&lt;/pre&gt;


&lt;p&gt;Frenzy mode will continuously generate random content until you terminate the application.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;Via Ruby gems.&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ gem install geppetto
&lt;/pre&gt;


&lt;p&gt;or head over to Github and fork &lt;a href="https://github.com/ternarylabs/geppetto"&gt;Geppetto&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;
&lt;a href="https://github.com/ternarylabs/geppetto"&gt;https://github.com/ternarylabs/geppetto&lt;/a&gt;
&lt;/pre&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=gs0pWG9RmrY:DPOXqqtZyqA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=gs0pWG9RmrY:DPOXqqtZyqA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=gs0pWG9RmrY:DPOXqqtZyqA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=gs0pWG9RmrY:DPOXqqtZyqA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=gs0pWG9RmrY:DPOXqqtZyqA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/gs0pWG9RmrY" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2011/03/26/control-your-facebook-friends-with-geppetto</feedburner:origLink></entry>
 
 <entry>
   <title>Automatically Add Slugs To Pivotal Tracker Story</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/hPqAUjJguz4/sluglug-automatic-slugs-for-pivotal-tracker" />
   <updated>2011-02-28T00:00:00-08:00</updated>
   <id>http://ternarylabs.com/2011/02/28/sluglug-automatic-slugs-for-pivotal-tracker</id>
   <content type="html">&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/02/slug.png" width="103" height="100" class="left"&gt;&lt;/p&gt;

&lt;p&gt;Sluglug is a service that automagically adds human readable identifiers to stories from &lt;a href="http://pivotaltracker.com"&gt;Pivotal Tracker&lt;/a&gt;, a widely used Agile project management tool.&lt;/p&gt;

&lt;p&gt;Tracker identifies stories with a global number shared by all projects. As of this writing, newly generated story ids are in the ten million range. An 8-digit number is very impractical to refer to verbally. Sluglug lessens the pain of communicating the reference to a specific story to another human being by assigning name prefixes ("The Slug") to each story.&lt;/p&gt;

&lt;p&gt;Slugs are derived from the names of US cities. They are lower cased and blank spaces replaced with hyphens. They are enclosed in square brackets and added at the front of the story title. For example a slugged story title could look like:&lt;/p&gt;

&lt;pre&gt;
[hilton-village] As a user I should be able to login.
&lt;/pre&gt;


&lt;p&gt;If you're using Tracker and need to refer to stories verbally, head over to &lt;a href="http://sluglug.com/"&gt;http://sluglug.com/&lt;/a&gt; and start slugging your project now.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=hPqAUjJguz4:6mB76E-y0h0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=hPqAUjJguz4:6mB76E-y0h0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=hPqAUjJguz4:6mB76E-y0h0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=hPqAUjJguz4:6mB76E-y0h0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=hPqAUjJguz4:6mB76E-y0h0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/hPqAUjJguz4" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2011/02/28/sluglug-automatic-slugs-for-pivotal-tracker</feedburner:origLink></entry>
 
 <entry>
   <title>Skype: Essential Ingredient for Distributed Agile Teams</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/CaeJLnmmMBY/skype-essential-ingredient-for-distributed-agile-teams" />
   <updated>2011-02-17T00:00:00-08:00</updated>
   <id>http://ternarylabs.com/2011/02/17/skype-essential-ingredient-for-distributed-agile-teams</id>
   <content type="html">&lt;p&gt;&lt;a href="http://skype.com"&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/02/Skype-Screen-logos_250x124.png" width="250" height="124" class="left"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As most Agile practices are centered around human interactions, I often get asked the question: “How do you do Agile development with a distributed team?”. My answer usually involves a standard lecture on how you should really try to get all your team members together in the same location, at least initially. The reality is that it’s not always possible. Whether your team is working from home because you are a bootstrapped startup without office, or because you have hired specific talents wherever they happened to be located, it’s very likely that one or more member of your development team is going to be remote.&lt;/p&gt;

&lt;h2&gt;
Far and Away
&lt;/h2&gt;


&lt;p&gt;When dealing with remote engineers, physical location and time difference are important factors. As such, you need a tool that will provide rich remote presence and also allow for asynchronous communication. For example, the phone is a decent way to bring people in a live conversation but voicemail is terrible as an asynchronous tool. Conversely, e-mail is a great way to communicate asynchronously but lacks fidelity and context.&lt;/p&gt;

&lt;p&gt;It should be obvious by now that teams benefit greatly from daily use of collaboration tools such as instant messenger, chat, audio conference calls and video. Throughout the years, I’ve used countless combination of free and commercial voice conference, instant messengers, chat rooms and screen sharing services. They typically got the job done. Skype potency is that everything is available in a single application.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I’m currently consulting for Skype, so my views are somewhat biased. However, Skype employees excel at eating their proverbial own dog food. There are no phones in the office, teams are distributed and the majority of communication is done via Skype. Software development is organized around small agile teams following the SCRUM methodology. In that environment, I’ve seen first hand what this tool is capable of. It’s been battle field tested for you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://cdn.ternarylabs.com/media/2011/02/agile-poker-via-skype_600x422.jpg" width="600" height="422" class="center"&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
    Remote Participant in an Agile Poker Planning Game. Note the creative use of on screen display to communicate point estimate.
&lt;/p&gt;




&lt;h2&gt;
    Features: Check, Check, Check
&lt;/h2&gt;


&lt;p&gt;Here is a list of Skype important features and how they can impact your team's ability to communicate on a daily basis.&lt;/p&gt;

&lt;h3&gt;
    Presence
&lt;/h3&gt;


&lt;p&gt;This is an obvious one. We’ve all experienced a team getting together only to ask the question “Anyone seen Paul? Where is he?”.&lt;/p&gt;

&lt;h3&gt;
    Mood Messages
&lt;/h3&gt;


&lt;p&gt;Custom status messages are not new. They can be used to convey the current mood of the person. They can also be a powerful tool to signal a status. For instance set your mood to the story title you’re working on or update it with your upcoming travel plans so everyone on the team knows your whereabouts.&lt;/p&gt;

&lt;h3&gt;
    Persistent Chats
&lt;/h3&gt;


&lt;p&gt;In Skype, chats (one-to-one and multiparty) are persisted by default and messages are queued for offline delivery. This provides an easy way to pick up conversation asynchronously, or synchronously if the other party happens to be on-line.&lt;/p&gt;

&lt;h3&gt;
    Multi-person Chat, Audio and Video Calls
&lt;/h3&gt;


&lt;p&gt;One of my favorite feature for SCRUM teams is the multi-person chat room. If you’ve been on a private IRC or similar chat room you know how it works. With Skype you can easily create and name chat rooms on the fly. You can invite people to participate via text, voice or video. They can be on a computer, mobile device or a regular phone. A typical usage scenario might look like that. It’s Monday morning, Bob, the remote SCRUM master initiates a chat with Paul, one of the engineer on the team.&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;
        Bob: “Hey is this time for our daily Scrum?”
    &lt;/p&gt;
    &lt;p&gt;
        Paul: “Yep, let me bring my laptop in the room and gather the troops”
    &lt;/p&gt;
    &lt;p&gt;
        Bob: “Ok”
    &lt;/p&gt;
    &lt;p&gt;
        Paul: “Ok, we’re all here. Let’s switch to video call”
    &lt;/p&gt;
    &lt;p&gt;
        Bob: “Ok”
    &lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
    Bob clicks the call button and the video and audio starts up.
&lt;/p&gt;


&lt;blockquote&gt;
    &lt;p&gt;
        Paul (talking): “Hi Bob. Can you add Greg to the call he’s working from home today.”
    &lt;/p&gt;
    &lt;p&gt;
        Bob (talking): “Sure no problems.”
    &lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
    Bob click on the add participant button and search for Greg. He adds him to the call. Since there are multiple parties now in the chat, Bob also renames the conversation with a more relevant title: “Sprint 42 Daily Scrum”. The chat room will be persisted at the end of the call and all the invited participants will remain, so it will be easy to initiate another call with the same group for the rest of the week.
&lt;/p&gt;


&lt;blockquote&gt;
    &lt;p&gt;
        Greg (talking): “Hi guys, thanks for patching me in.”
    &lt;/p&gt;
    &lt;p&gt;
        Bob (talking): “Ok, let’s start with Greg and then go around the room.”
    &lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
    At one point, the team realize that they need some information from Serge, the night owl sysadmin. Serge just woke up an hour ago and now he’s on his way to the office. He’s driving and only has his cell with him.
&lt;/p&gt;


&lt;blockquote&gt;
    &lt;p&gt;
        Bob (talking): “Hey, what’s Serge’s mobile number?”
    &lt;/p&gt;
    &lt;p&gt;
        Greg (typing into the chat room): “Here you go: +16505551212”
    &lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
    Bob clicks on “add people to the conversation” and paste the number to dial Serge’s mobile directly. Serge is now called into the conference call and can answer questions from the team. This is simple example of what you can do with that. There are many more usage scenario. Whenever your team does not neatly fit together in the same room. You can create as many “rooms” as needed. For instance you could have a standing room for daily scrum, one for release going live, one for general team discussion, etc. In every instances, once you’ve invited the relevant people, they are automatically able to participate and read historical messages.
&lt;/p&gt;


&lt;h3&gt;
    File Sharing
&lt;/h3&gt;


&lt;p&gt;
    Once you’ve approved a person in your contact list, you can easily exchange files. No more attachments clogging emails. You can also send a file to multiple participant in a chat room.
&lt;/p&gt;


&lt;h3&gt;
    &lt;a href="http://www.skype.com/intl/en-us/features/allfeatures/call-phones-and-mobiles/"&gt;Bridge to PSTN&lt;/a&gt;
&lt;/h3&gt;


&lt;p&gt;
    One of the most undiscovered feature of Skype is that you can use it to &lt;a href="http://www.skype.com/intl/en-us/features/allfeatures/call-phones-and-mobiles/"&gt;make call&lt;/a&gt; to regular phones. This is a great way to bring in “legacy” participant into a conference call.
&lt;/p&gt;


&lt;h3&gt;
    &lt;a href="http://www.skype.com/intl/en-us/get-skype/"&gt;Ubiquity of Platforms&lt;/a&gt;
&lt;/h3&gt;


&lt;p&gt;
    Windows, OS X, Linux, iPhone, Android are &lt;a href="http://www.skype.com/intl/en-us/get-skype/"&gt;supported&lt;/a&gt;.
&lt;/p&gt;


&lt;h3&gt;
    Low cost
&lt;/h3&gt;


&lt;p&gt;
    For the most part, the Skype service is free. Calls to phone are charged at a ridiculously low rate and some advanced feature like group video calls require a premium subscription.
&lt;/p&gt;


&lt;h3&gt;
    &lt;a href="http://www.skype.com/intl/en-us/business/skype-manager/"&gt;Enterprise Management Tool&lt;/a&gt;
&lt;/h3&gt;


&lt;p&gt;
    One of the biggest drawback of using a consumer tool in a corporate environment is the lack of user management. You might be a small team now, but sooner or later the team will grow, some people will leave the company and you might have to deal with access control to revoke someone's access. Skype has introduced a new &lt;a href="http://www.skype.com/intl/en-us/business/skype-manager/"&gt;user management&lt;/a&gt; system that allows an administrator to manage accounts, create a directory of users and allocated credits to be used by individual for paid features.
&lt;/p&gt;


&lt;h3&gt;
    Screen Sharing
&lt;/h3&gt;


&lt;p&gt;
    This is probably Skype's weakest feature at this point. Screen sharing is only available during one-to-on calls, which means you can't share your screen with a group. The feature is essentially using your desktop as a source for video call. The resulting image quality and frame rate is adequate but not great. As long as your other tools for planning (bug tracking, wiki, etc.) are available to everyone, it's not necessarily a problem as each participant can follow along on their own instance. If screen sharing is a big part of your activity, you might want to look for one of the many free option available (Google "free screen sharing").
&lt;/p&gt;


&lt;h2&gt;
    Try It Out for Yourself
&lt;/h2&gt;


&lt;p&gt;
    I hope this list has inspired you to try it out for yourself. &lt;a href="http://skype.com/download/"&gt;Download Skype&lt;/a&gt; and get started.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=CaeJLnmmMBY:q7uuCBaBpbg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=CaeJLnmmMBY:q7uuCBaBpbg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=CaeJLnmmMBY:q7uuCBaBpbg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=CaeJLnmmMBY:q7uuCBaBpbg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=CaeJLnmmMBY:q7uuCBaBpbg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/CaeJLnmmMBY" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2011/02/17/skype-essential-ingredient-for-distributed-agile-teams</feedburner:origLink></entry>
 
 <entry>
   <title>Confirmation Dialog Bug for Rails 3 + jQuery 1.4.3 UJS</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/RLWBJKQAo4I/confirmation-dialog-bug-for-rails-3-jquery-1-4-3-ujs" />
   <updated>2010-10-20T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/10/20/confirmation-dialog-bug-for-rails-3-jquery-1-4-3-ujs</id>
   <content type="html">&lt;p&gt;Rails 3 has introduced Unobtrusive JavaScript (UJS), which means that the html generated for certain elements no longer contains JavaScript for various client side actions. It relies instead on a declarative markup and runtime binding.&lt;/p&gt;

&lt;p&gt;A common use for this is to pop up a dialog to confirm a delete action.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"nofollow"&lt;/span&gt; &lt;span class="na"&gt;data-method=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt; &lt;span class="na"&gt;data-confirm=&lt;/span&gt;&lt;span class="s"&gt;"Are you sure?"&lt;/span&gt; 
 &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/user/1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;This way you can choose which JavaScript framework fits you best. In order to use it, you need to install a Rails JavaScript driver for your framework of choice. For instance, you can install the jQuery one from GitHub &lt;a href="http://github.com/rails/jquery-ujs"&gt;http://github.com/rails/jquery-ujs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, the latest version of jquery-ujs has a bug that can have severe consequences. The confirmation dialog pops up but the user selection is ignored, resulting in the action &lt;strong&gt;always&lt;/strong&gt; being performed.&lt;/p&gt;

&lt;p&gt;There is tentative patch waiting to be reviewed.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://github.com/alexcrichton/jquery-ujs/commit/2b0e408559f9c7b29c1c5590e4bf6520311df35c"&gt;http://github.com/alexcrichton/jquery-ujs/commit/2b0e408559f9c7b29c1c5590e4bf6520311df35c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've applied it and it seems to fix the problem.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=RLWBJKQAo4I:mloDveNQrao:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=RLWBJKQAo4I:mloDveNQrao:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=RLWBJKQAo4I:mloDveNQrao:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=RLWBJKQAo4I:mloDveNQrao:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=RLWBJKQAo4I:mloDveNQrao:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/RLWBJKQAo4I" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/10/20/confirmation-dialog-bug-for-rails-3-jquery-1-4-3-ujs</feedburner:origLink></entry>
 
 <entry>
   <title>Kanban and Whiteboard to Track Agile Projects</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/cWcUoE2c6uw/kanban-and-whiteboard-to-track-agile-projects" />
   <updated>2010-09-16T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/09/16/kanban-and-whiteboard-to-track-agile-projects</id>
   <content type="html">&lt;p&gt;Whenever I first introduce a team to Agile development, I start with a physical board and cards to track and plan iterations. Engineers not familiar with the process often see this as a very archaic practice and sometime deride it. They miss the point entirely. This is the best way for the team to figure out how to interact with each other and get a sense of how the process is going to work. This allows everyone to focus on the task at hand versus getting lost in the distraction and complexity of a software tool. There is something intangible and satisfying about handling physical objects and interacting with them.&lt;/p&gt;

&lt;p&gt;I was pleased to learn that the team at Vodafone (formely &lt;a href="http://www.crunchbase.com/company/zyb-2"&gt;Zyb&lt;/a&gt;) that recently launched the version 2.0 of their &lt;a href="http://360.com"&gt;360.com&lt;/a&gt; service has been using Agile development for a while.&lt;/p&gt;

&lt;p&gt;They’ve published a time lapse video of their &lt;a href="http://en.wikipedia.org/wiki/Kanban"&gt;Kanban&lt;/a&gt; that tracks progress from start to finish.&lt;/p&gt;

&lt;iframe class="center" src="http://player.vimeo.com/video/15012026" width="400" height="320" frameborder="0"&gt;&lt;/iframe&gt;


&lt;p&gt;It’s great to see that such a nimble development process can exist in a large carrier organization.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=cWcUoE2c6uw:2qo1Q9Gc1Ug:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=cWcUoE2c6uw:2qo1Q9Gc1Ug:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=cWcUoE2c6uw:2qo1Q9Gc1Ug:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=cWcUoE2c6uw:2qo1Q9Gc1Ug:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=cWcUoE2c6uw:2qo1Q9Gc1Ug:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/cWcUoE2c6uw" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/09/16/kanban-and-whiteboard-to-track-agile-projects</feedburner:origLink></entry>
 
 <entry>
   <title>Automatically Configure Hostname for New EC2 Instances</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/ByZA1Q1CS8c/automatically-configure-hostname-for-new-ec2-instances" />
   <updated>2010-09-15T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/09/15/automatically-configure-hostname-for-new-ec2-instances</id>
   <content type="html">&lt;p&gt;When working with Amazon EC2 instances, you've probably experienced the need to change the default hostname to something more meaningful than "ip-10-205-14-33" for instance. While simply issuing a &lt;code&gt;hostname&lt;/code&gt; command will do the trick, it requires login into the machine. If you also need to properly define the fqdn (fully qualified domain name) of the machine, it requires a modification of the &lt;code&gt;/etc/hosts&lt;/code&gt; file as well or more if you've setup a DNS server.&lt;/p&gt;

&lt;p&gt;Setting the desired hostname and fqdn for the instance can be critical if you're automating deployment with &lt;a href="http://www.opscode.com/chef"&gt;Chef&lt;/a&gt; and expect to retrieve those values later on. A simple approach is to pass user data when creating the instance and use a script to read it and populate the hostname.&lt;/p&gt;

&lt;p&gt;Create a new instance from your favorite AMI. Save this script as &lt;code&gt;/usr/local/ec2/ec2-hostname.sh&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="bash"&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Replace this with your domain&lt;/span&gt;
&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-domain.com

&lt;span class="nv"&gt;USER_DATA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/usr/bin/curl -s http://169.254.169.254/latest/user-data&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;HOSTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$USER_DATA&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;IPV4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;/usr/bin/curl -s http://169.254.169.254/latest/meta-data/public-ipv4&lt;span class="sb"&gt;`&lt;/span&gt;

&lt;span class="c"&gt;# Set the host name&lt;/span&gt;
hostname &lt;span class="nv"&gt;$HOSTNAME&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$HOSTNAME&lt;/span&gt; &amp;gt; /etc/hostname

&lt;span class="c"&gt;# Add fqdn to hosts file&lt;/span&gt;
cat&lt;span class="s"&gt;&amp;lt;&amp;lt;EOF &amp;gt; /etc/hosts&lt;/span&gt;
&lt;span class="s"&gt;# This file is automatically genreated by ec2-hostname script&lt;/span&gt;
&lt;span class="s"&gt;127.0.0.1 localhost&lt;/span&gt;
&lt;span class="s"&gt;$IPV4 $HOSTNAME.$DOMAIN $HOSTNAME&lt;/span&gt;

&lt;span class="s"&gt;# The following lines are desirable for IPv6 capable hosts&lt;/span&gt;
&lt;span class="s"&gt;::1 ip6-localhost ip6-loopback&lt;/span&gt;
&lt;span class="s"&gt;fe00::0 ip6-localnet&lt;/span&gt;
&lt;span class="s"&gt;ff00::0 ip6-mcastprefix&lt;/span&gt;
&lt;span class="s"&gt;ff02::1 ip6-allnodes&lt;/span&gt;
&lt;span class="s"&gt;ff02::2 ip6-allrouters&lt;/span&gt;
&lt;span class="s"&gt;ff02::3 ip6-allhosts&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Mark it as executable&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ chmod o+x ec2-hostanmes.sh
&lt;/pre&gt;


&lt;p&gt;and add the following line to &lt;code&gt;/etc/rc.local&lt;/code&gt; so that it runs every time the instance restart&lt;/p&gt;

&lt;pre class="terminal"&gt;
/usr/local/ec2/ec2-hostname.sh
&lt;/pre&gt;


&lt;p&gt;Save this instance as a new AMI (easiest to do from the AWS console).&lt;/p&gt;

&lt;p&gt;Next time you want to create a new instance, pass the desired hostname in the &lt;code&gt;user-data&lt;/code&gt; option.&lt;/p&gt;

&lt;pre class="terminal"&gt;
ec2-run-instances ami-99f510f1 --user-data "YOUR-HOSTNAME" \
  --instance-type t1.micro --group default --region us-east-1 \
  --key YOUR-KEY 
&lt;/pre&gt;


&lt;p&gt;This technique was adapted from a &lt;a href="http://www.ducea.com/2009/06/01/howto-update-dns-hostnames-automatically-for-your-amazon-ec2-instances/"&gt;more comprehensive example&lt;/a&gt; published by Marius Ducea.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=ByZA1Q1CS8c:pCwoRqCYPSc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=ByZA1Q1CS8c:pCwoRqCYPSc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=ByZA1Q1CS8c:pCwoRqCYPSc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=ByZA1Q1CS8c:pCwoRqCYPSc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=ByZA1Q1CS8c:pCwoRqCYPSc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/ByZA1Q1CS8c" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/09/15/automatically-configure-hostname-for-new-ec2-instances</feedburner:origLink></entry>
 
 <entry>
   <title>Ubuntu won't Restart on Amazon EC2 Micro Instance</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/ArVrR7y1Ypk/ubuntu-wont-restart-on-amazon-ec2-micro-instance" />
   <updated>2010-09-14T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/09/14/ubuntu-wont-restart-on-amazon-ec2-micro-instance</id>
   <content type="html">&lt;p&gt;Amazon recently introduced a &lt;a href="http://aws.amazon.com/ec2/#instance"&gt;micro size instance&lt;/a&gt; for their popular EC2 service, making it very affordable to run infrequently used web applications.&lt;/p&gt;

&lt;p&gt;We've been running tests to benchmark this type of instances against &lt;a href="https://manage.slicehost.com/customers/new?referrer=5e68278d55a8dc39fde8a563fb489353"&gt;Slicehost 256&lt;/a&gt; offering. Before you go down the path of setting up an instance for your own use, there is one big problem to be aware of. If you're using an Ubuntu image, the instance won't come back properly after a reboot or restart.&lt;/p&gt;

&lt;p&gt;Because micro instance have no ephemeral storage at all, after a restart mountall will wait indefinitely for /dev/sda2, which is never going to be present. You can follow this &lt;a href="https://bugs.launchpad.net/ubuntu/+source/cloud-init/+bug/634102"&gt;bug&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;As a workaround, make sure to login into your instance right after it's been created and edit the &lt;code&gt;/etc/fstab&lt;/code&gt; file as follow to add the &lt;code&gt;nobootwait&lt;/code&gt; parameter.&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="text"&gt;/dev/sda2    /mnt    auto  defaults,comment=cloudconfig,nobootwait
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=ArVrR7y1Ypk:2U_UJq2moPc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=ArVrR7y1Ypk:2U_UJq2moPc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=ArVrR7y1Ypk:2U_UJq2moPc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=ArVrR7y1Ypk:2U_UJq2moPc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=ArVrR7y1Ypk:2U_UJq2moPc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/ArVrR7y1Ypk" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/09/14/ubuntu-wont-restart-on-amazon-ec2-micro-instance</feedburner:origLink></entry>
 
 <entry>
   <title>Migrating to Rails 3.0 Gotchas: as_json bug</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/eE57psFlE0Q/migrating-to-rails-3-0-gotchas-as_json-bug" />
   <updated>2010-09-07T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/09/07/migrating-to-rails-3-0-gotchas-as_json-bug</id>
   <content type="html">&lt;p&gt;Since Rails 2.3.3, the best practice to take control over what attributes are exposed during a JSON serialization was to override the  &lt;code&gt;as_json&lt;/code&gt; method as opposed to the &lt;code&gt;to_json&lt;/code&gt; method as it was done previously. The idea is that &lt;code&gt;as_json&lt;/code&gt; should return the object structure of the JSON (most likely a Hash) and &lt;code&gt;to_json&lt;/code&gt; encode that object into a JSON string. Jonathan Julian wrote a good &lt;a href="http://jonathanjulian.com/2010/04/rails-to_json-or-as_json/"&gt;reference post&lt;/a&gt; on that subject.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;as_json&lt;/code&gt; method takes an &lt;code&gt;options&lt;/code&gt; hash that you can use to include/exclude certain attributes.&lt;/p&gt;

&lt;p&gt;So let say you have a &lt;code&gt;Product&lt;/code&gt; model with &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt;, &lt;code&gt;updated_at&lt;/code&gt; and &lt;code&gt;account_id&lt;/code&gt; as attributes. You don't want to expose the relationship to account and only include &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;created_at&lt;/code&gt;. You would override &lt;code&gt;as_json&lt;/code&gt; in your model as follows:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;as_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:only&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;You can verify that it works in the Rails console:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="irb"&gt;&lt;span class="gp"&gt;irb(main):003:0&amp;gt; &lt;/span&gt;&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_json&lt;/span&gt;
&lt;span class="go"&gt;=&amp;gt; {"name"=&amp;gt;"Test", "created_at"=&amp;gt;Mon, 06 Sep 2010 21:38:29 PDT -07:00, "id"=&amp;gt;2}&lt;/span&gt;
&lt;span class="gp"&gt;irb(main):004:0&amp;gt; &lt;/span&gt;&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_json&lt;/span&gt;
&lt;span class="go"&gt;=&amp;gt; "{\"name\":\"Test\",\"created_at\":\"2010-09-06T21:39:11-07:00\",\"id\":3}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Unfortunately, Rails 3.0 introduced a &lt;a href="https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/5374"&gt;bug&lt;/a&gt;, where the &lt;code&gt;as_json&lt;/code&gt; method in the super class (ActiveRecord::Base) will just return the object and ignore the options passed. As a result in this case &lt;code&gt;to_json&lt;/code&gt; will output &lt;strong&gt;all the attributes contained in your model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the Rails 3.0.0 console, you get:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="irb"&gt;&lt;span class="gp"&gt;irb(main):001:0&amp;gt; &lt;/span&gt;&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_json&lt;/span&gt;
&lt;span class="go"&gt;=&amp;gt; #&amp;lt;Product id: 4, name: "Test", account_id: nil, created_at: "2010-09-07 04:40:44", &lt;/span&gt;
&lt;span class="go"&gt;   updated_at: "2010-09-07 04:40:44"&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;irb(main):002:0&amp;gt; &lt;/span&gt;&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_json&lt;/span&gt;
&lt;span class="go"&gt;=&amp;gt; "{\"name\":\"Test\",\"created_at\":\"2010-09-06T21:40:49-07:00\",\"updated_at\":&lt;/span&gt;
&lt;span class="go"&gt;   \"2010-09-06T21:40:49-07:00\",\"account_id\":null,\"id\":5}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Whoops, &lt;code&gt;account_id&lt;/code&gt; is being included.&lt;/p&gt;

&lt;p&gt;As a workaround, you can either hand-craft the attributes your want to return or use the following to simply output a hash:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;as_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;serializable_hash&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:only&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Note that in these examples, the &lt;code&gt;ActiveRecord::Base.include_root_in_json&lt;/code&gt; setting was set to &lt;code&gt;false&lt;/code&gt; so that the root object class name is not included.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=eE57psFlE0Q:ad8cX58HRzI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=eE57psFlE0Q:ad8cX58HRzI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=eE57psFlE0Q:ad8cX58HRzI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=eE57psFlE0Q:ad8cX58HRzI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=eE57psFlE0Q:ad8cX58HRzI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/eE57psFlE0Q" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/09/07/migrating-to-rails-3-0-gotchas-as_json-bug</feedburner:origLink></entry>
 
 <entry>
   <title>Migrating to Rails 3.0 Gotchas: lib folder</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/M8mQ7r7fumo/migrating-to-rails-3-0-gotchas-lib-folder" />
   <updated>2010-09-06T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/09/06/migrating-to-rails-3-0-gotchas-lib-folder</id>
   <content type="html">&lt;p&gt;The &lt;code&gt;lib&lt;/code&gt; folder is not included by default in Rails 3.0. This is usually the place where you'll find miscellaneous Ruby code that does not neatly belong to other better defined placed such as model, view, controller or helper directories.&lt;/p&gt;

&lt;p&gt;A quick fix is to include the following line&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autoload_paths&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sx"&gt;%W( &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sx"&gt;/lib )&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;in the &lt;code&gt;config/application.rb&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;A better approach is to use the pattern described in this post about "&lt;a href="http://www.strictlyuntyped.com/2008/06/rails-where-to-put-other-files.html"&gt;Where to put the 'other' files&lt;/a&gt;".&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=M8mQ7r7fumo:4J37IiGtX3s:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=M8mQ7r7fumo:4J37IiGtX3s:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=M8mQ7r7fumo:4J37IiGtX3s:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=M8mQ7r7fumo:4J37IiGtX3s:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=M8mQ7r7fumo:4J37IiGtX3s:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/M8mQ7r7fumo" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/09/06/migrating-to-rails-3-0-gotchas-lib-folder</feedburner:origLink></entry>
 
 <entry>
   <title>Migrating to Rails 3.0 Gotchas: concat</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/4edeCt4tqos/migrating-to-rails-3-0-gotchas-concat" />
   <updated>2010-09-03T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/09/03/migrating-to-rails-3-0-gotchas-concat</id>
   <content type="html">&lt;h2&gt;Concat in View Helpers&lt;/h2&gt;


&lt;p&gt;A common technique to simplify logic in views is to use helpers to define blocks that conditionally include content. For instance a helper to only include a block if the user is logged in could look like:&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ApplicationHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;logged_in_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;logged_in?&lt;/span&gt;
      &lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="n"&gt;content_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:div&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;and would be used like this in a view&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="erb"&gt;&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;logged_in_block&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;  &amp;lt;div id="user-bar-greeting"&amp;gt;Logged in as ... &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In Rails 3.0, this results in the block being included twice. The new convention is that if a Ruby code block in an erb is going to output something, if should be coded as &lt;code&gt;&amp;lt;%= %&amp;gt;&lt;/code&gt;. In this case the &lt;code&gt;concat&lt;/code&gt; method is no longer necessary. The &lt;code&gt;&lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag"&gt;content_tag&lt;/a&gt;&lt;/code&gt; method can directly take a block as a parameter as well.&lt;/p&gt;

&lt;p&gt;This can be simplified to&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;logged_in_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;content_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:div&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;logged_in?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;and used&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="erb"&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;logged_in_block&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;  &amp;lt;div id="user-bar-greeting"&amp;gt;Logged in as ... &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=4edeCt4tqos:fIwwrN5aJdc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=4edeCt4tqos:fIwwrN5aJdc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=4edeCt4tqos:fIwwrN5aJdc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=4edeCt4tqos:fIwwrN5aJdc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=4edeCt4tqos:fIwwrN5aJdc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/4edeCt4tqos" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/09/03/migrating-to-rails-3-0-gotchas-concat</feedburner:origLink></entry>
 
 <entry>
   <title>Migrating Existing Applications to Rails 3.0</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/Vv6sLmwtFXg/migrating-existing-applications-to-rails-3-0" />
   <updated>2010-09-03T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/09/03/migrating-existing-applications-to-rails-3-0</id>
   <content type="html">&lt;p&gt;This week &lt;a href="http://weblog.rubyonrails.org/2010/8/29/rails-3-0-it-s-done"&gt;Rails 3.0 reached final&lt;/a&gt;. The are many interesting new features such as &lt;a href="http://guides.rubyonrails.org/active_record_querying.html"&gt;Active Record Query Interface&lt;/a&gt; and lots of improvements (&lt;a href="http://edgeguides.rubyonrails.org/routing.html"&gt;Routing&lt;/a&gt;, &lt;a href="http://edgeguides.rubyonrails.org/action_mailer_basics.html"&gt;Action Mailer&lt;/a&gt;, Gem dependencies with &lt;a href="http://gembundler.com/"&gt;Bundler&lt;/a&gt;, XSS protection, configuration cleanup, etc.) in this major upgrade.&lt;/p&gt;

&lt;p&gt;Like with any major platform upgrade, many developers will be confronted with the dilemma of either staying on the 2.x branch — which will eventually be obsolete — or put in a lot of effort to migrate an otherwise perfectly good working application.&lt;/p&gt;

&lt;p&gt;To make matter worse, the entire Rails eco-system of gems and plugin is in the process of  upgrading to take advantage of new functionality. So even if you don't think you need any of the new features, you better get on the three-oh &lt;del datetime="2010-09-03T18:05:04+00:00"&gt;bandwagon&lt;/del&gt; bullet train soon.&lt;/p&gt;

&lt;p&gt;To ease the pain of migration, &lt;a href="http://jeremymcanally.com/"&gt;Jeremy McAnally&lt;/a&gt; created a very useful &lt;a href="http://omgbloglol.com/post/364624593/rails-upgrade-is-now-an-official-plugin"&gt;plugin&lt;/a&gt;. It analyzes your current Rails application and generates a pre-flight checklist, highlighting areas that use deprecated api and will likely cause problems with Rails 3.0. The plugin also create a &lt;code&gt;Gemfile&lt;/code&gt; used by &lt;a href="http://gembundler.com/"&gt;Bundler&lt;/a&gt;, extracting all the gems dependencies from &lt;code&gt;environment.rb&lt;/code&gt;. Lastly, it will rejigger &lt;code&gt;routes.rb&lt;/code&gt; to use the new routing syntax.&lt;/p&gt;

&lt;p&gt;Jeremy also wrote an extensive &lt;a href="http://www.railsupgradehandbook.com/"&gt;guide book&lt;/a&gt; on migrating to Rails 3.0. Although it's a paid publication, it is very affordable, comprehensive and well written.&lt;/p&gt;

&lt;p&gt;Pratik Naik has also a good &lt;a href="http://m.onkey.org/2010/1/22/active-record-query-interface"&gt;post&lt;/a&gt; describing the changes in Active Record.&lt;/p&gt;

&lt;p&gt;With those tools in hands we've migrated an existing Rails 2.3.8 application to 3.0 and got it to boot in a couple of hours.&lt;/p&gt;

&lt;p&gt;Hope you'll hop on the train too.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=Vv6sLmwtFXg:YqVFwHsHT28:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=Vv6sLmwtFXg:YqVFwHsHT28:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=Vv6sLmwtFXg:YqVFwHsHT28:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=Vv6sLmwtFXg:YqVFwHsHT28:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=Vv6sLmwtFXg:YqVFwHsHT28:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/Vv6sLmwtFXg" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/09/03/migrating-existing-applications-to-rails-3-0</feedburner:origLink></entry>
 
 <entry>
   <title>Real World Agile Development Practices</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/Di0DTywXnWI/real-world-agile-development-practices" />
   <updated>2010-08-12T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/08/12/real-world-agile-development-practices</id>
   <content type="html">&lt;p&gt;I often get asked about Agile development and what it means for a team to be agile. Of course nowadays every self-respecting engineering organization claims to espouse at least some level of agility, but few are able to describe in details what their development process consists of.&lt;/p&gt;

&lt;p&gt;There is a gap between the theory of what "should be done" and what is really going on in the real hectic world of engineering, especially as it exists in startups. There is a lack of information about how development is really being done in software companies around the world. Often for good reasons; they don't want you to know.&lt;/p&gt;

&lt;p&gt;I thought I'd share a series of blog post I wrote about the development process we created when I was leading the engineering team at &lt;a href="http://getsongbird.com"&gt;Songbird&lt;/a&gt;, the popular media player. This is by no means the only or best approach, but something that was tailored to our needs, capabilities and constraints.&lt;/p&gt;

&lt;p&gt;The inherent complexity of building, testing and releasing an open-source desktop software platform supporting hundreds of devices, integrated with dozens of services and running on multiple operating systems drove a lot of the decisions we made to find our optimum release process.&lt;/p&gt;

&lt;p&gt;I hope you'll enjoy reading these and perhaps learn something you can use.&lt;/p&gt;

&lt;h3&gt;Introduction of Agile Practices&lt;/h3&gt;


&lt;p&gt;A three part series view of a team embarking on an agile development process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://auberger.com/2008/06/25/songbird-path-to-agility-part-i-2"&gt;Songbird Path to Agility Part I&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://auberger.com/2008/09/04/songbird-path-to-agility-part-ii-2"&gt;Songbird Path to Agility Part II&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://auberger.com/2008/12/15/songbird-path-to-agility-part-iii"&gt;Songbird Path to Agility Part III&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;Tracking&lt;/h3&gt;


&lt;p&gt;An overview of a tool I developed to improve tracking visibility of a release.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://auberger.com/2010/01/14/dashboard-for-agile-project-tracking"&gt;Dashboard for Agile Project Tracking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;Planning&lt;/h3&gt;


&lt;p&gt;An analysis on how Agile data can be used for long term planning purpose.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://auberger.com/2010/02/01/make-long-term-planning-possible-in-an-agile-environment"&gt;Make Long Term Planning Possible in an Agile Environment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=Di0DTywXnWI:q_U_5FyEimA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=Di0DTywXnWI:q_U_5FyEimA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=Di0DTywXnWI:q_U_5FyEimA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=Di0DTywXnWI:q_U_5FyEimA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=Di0DTywXnWI:q_U_5FyEimA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/Di0DTywXnWI" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/08/12/real-world-agile-development-practices</feedburner:origLink></entry>
 
 <entry>
   <title>Cappuccino Tag Editor Control in Action</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/O0y2QRBmsbc/cappuccino-tag-editor-control-in-action" />
   <updated>2010-08-11T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/08/11/cappuccino-tag-editor-control-in-action</id>
   <content type="html">&lt;p&gt;As promised, a live demo of TLTagControl — a tag editor control implemented in Cappuccino — is now available. Head over to the &lt;a href="http://sandbox.ternarylabs.com/"&gt;sandbox&lt;/a&gt; and &lt;a href="http://sandbox.ternarylabs.com/cappuccino/TLTagControlExample/"&gt;give it a spin&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=O0y2QRBmsbc:T9aZcurxSUM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=O0y2QRBmsbc:T9aZcurxSUM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=O0y2QRBmsbc:T9aZcurxSUM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=O0y2QRBmsbc:T9aZcurxSUM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=O0y2QRBmsbc:T9aZcurxSUM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/O0y2QRBmsbc" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/08/11/cappuccino-tag-editor-control-in-action</feedburner:origLink></entry>
 
 <entry>
   <title>Fancy Tag Editor with Cappuccino</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/udmG7-WIEeo/fancy-tag-editor-with-cappuccino" />
   <updated>2010-08-05T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/08/05/fancy-tag-editor-with-cappuccino</id>
   <content type="html">&lt;p&gt;Tags are present in many applications as a powerful way for users to organize content. For the most part, web applications provide adequate level of tagging, but often fall short on the editing side of things, especially compared to desktop applications.&lt;/p&gt;

&lt;p&gt;To remedy this, we've build a tag editor control using Cappuccino that attempts to provide a richer and more natural tag editing experience.&lt;/p&gt;

&lt;p&gt;You can see it in action in the screencast below.&lt;/p&gt;

&lt;p&gt;Once you click on the edit field, most of the editing can be done via the keyboard. It features an autosuggest box that filters itself automatically as you type and inserts the selected tag upon hitting enter. Tagging can be performed very fast this way.&lt;/p&gt;

&lt;p&gt;The control also supports drag and drop, to reorder the list for instance. What's more it's 100% pure HTML + CSS + Javascript.&lt;/p&gt;

&lt;p&gt;A live demo will be available soon. Stay tuned.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://cdn.ternarylabs.com/media/2010/08/tag-editor-cappuccino.mov" class="media center"&gt;Tag Editor in Cappuccino&lt;/a&gt;&lt;/p&gt;

&lt;script type="text/javascript"&gt;
$('.media').media({ width: 520, height: 200, autoplay: true, caption:   false  });
&lt;/script&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=udmG7-WIEeo:d_reCADxsy0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=udmG7-WIEeo:d_reCADxsy0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=udmG7-WIEeo:d_reCADxsy0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=udmG7-WIEeo:d_reCADxsy0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=udmG7-WIEeo:d_reCADxsy0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/udmG7-WIEeo" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/08/05/fancy-tag-editor-with-cappuccino</feedburner:origLink></entry>
 
 <entry>
   <title>Customize Objective-J objects output to debug console</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/MXl1n105pZ0/customize-objective-j-objects-output-to-debug-console" />
   <updated>2010-06-17T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/06/17/customize-objective-j-objects-output-to-debug-console</id>
   <content type="html">&lt;p&gt;If you've experimented with printing out Objective-J objects to the console, you've seen output such as&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SomeClass&lt;/span&gt; &lt;span class="mh"&gt;0x004d90&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Knowing the object class is nice, but not super useful. In Javascript, the toString() method is normally invoked when an object is requested to dump it's content into a string. With Objective-J, the selector to override is called description(). The default implementation is as follow:&lt;/p&gt;

&lt;p&gt;Override it to return information that is more specific to your object. It will help when debugging.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=MXl1n105pZ0:lD9gYE7uBZ0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=MXl1n105pZ0:lD9gYE7uBZ0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=MXl1n105pZ0:lD9gYE7uBZ0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=MXl1n105pZ0:lD9gYE7uBZ0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=MXl1n105pZ0:lD9gYE7uBZ0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/MXl1n105pZ0" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/06/17/customize-objective-j-objects-output-to-debug-console</feedburner:origLink></entry>
 
 <entry>
   <title>Adding arbitrary controls to CPToolbar</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/CvngRA7m7aY/adding-arbitrary-controls-to-cptoolbar" />
   <updated>2010-06-04T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/06/04/adding-arbitrary-controls-to-cptoolbar</id>
   <content type="html">&lt;p&gt;The CPToolbar class allows for easy setup of button controls. In order to add other type of controls, those need to be sub-classes of CPView. Here is an example of how to add a slider control to a toolbar.&lt;/p&gt;

&lt;p&gt;The following class implements a volume control and inherits from CPView:&lt;/p&gt;

&lt;p&gt;As the slider value changes, a notification is posted to the default notification center and the slider object is being sent along. Let's look at how the slider can be added to a toolbar.&lt;/p&gt;

&lt;p&gt;If you've created a toolbar already, this code should look very familiar. On line 31, an instance of the volume object is simply set as a view on the CPToobarItem.&lt;/p&gt;

&lt;p&gt;Now we're almost done. In order to be notified of volume changes, we need to register a method to listen to the notification. Here is how it's done&lt;/p&gt;

&lt;p&gt;and then the following method will read the value of the slider and can act accordingly.&lt;/p&gt;

&lt;p&gt;That's all there is to it.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=CvngRA7m7aY:hWZX_PxJaaY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=CvngRA7m7aY:hWZX_PxJaaY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=CvngRA7m7aY:hWZX_PxJaaY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=CvngRA7m7aY:hWZX_PxJaaY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=CvngRA7m7aY:hWZX_PxJaaY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/CvngRA7m7aY" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/06/04/adding-arbitrary-controls-to-cptoolbar</feedburner:origLink></entry>
 
 <entry>
   <title>Unit testing Objective-J apps with OJUnit</title>
   <link href="http://feedproxy.google.com/~r/ternarylabs/~3/qC9m90xhIgs/unit-testing-objective-j-apps-with-ojunit" />
   <updated>2010-06-03T00:00:00-07:00</updated>
   <id>http://ternarylabs.com/2010/06/03/unit-testing-objective-j-apps-with-ojunit</id>
   <content type="html">&lt;p&gt;For a new framework/language to rapidly be adopted is has to provide an easy way for developers to add and run tests. Not only unit tests are the cornerstone of maintainable code, but they can also help you learn a new language by writing tests to figure out behaviors.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://github.com/280north/ojtest" target="_blank"&gt;OJTest&lt;/a&gt; is the latest test framework from 280North. It contains OJUnit - an xUnit framework and OJMoq - a mocking framework as well a couple of tools (OJAutotest - a tool to automatically test your Cappuccino application when you make changes and OJCov - a coverage tool to help you identify code not covered by tests).&lt;/p&gt;

&lt;p&gt;We're going to focus on OJUnit first. OJUnit is a simple unit test tool written in and for Objective-J. It's based on theÂ &lt;a href="http://en.wikipedia.org/wiki/XUnit"&gt;xUnit&lt;/a&gt; design. If you used JUnit, you should feel right at home.&lt;/p&gt;

&lt;h3&gt;Installing&lt;/h3&gt;


&lt;p&gt;Assuming that you have Cappuccino 0.8 or later installed, you should be able to install OJUnit with the following command:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ tusk install ojunit
&lt;/pre&gt;




&lt;h3&gt;Writing tests&lt;/h3&gt;


&lt;p&gt;Like any other xUnit framework, test classes must respect a few conventions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test class names should follow the pattern ClassNameTest.&lt;/li&gt;
    &lt;li&gt;Test classes must subclass OJTestCase.&lt;/li&gt;
    &lt;li&gt;Test case methods name must start with &lt;strong&gt;test&lt;/strong&gt;.&lt;/li&gt;
    &lt;li&gt;Tests should be placed in a Test folder at the root level of your project.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Here is a skeleton test for testing SomeClass.j class&lt;/p&gt;

&lt;p&gt;Here is a list of assertions methods available:&lt;/p&gt;

&lt;p&gt;And here are the methods to fail a test:&lt;/p&gt;

&lt;p&gt;For more details, look at the source code for the &lt;a href="http://github.com/280north/OJTest/blob/master/Frameworks/OJUnit/OJTestCase.j"&gt;OJTestCase&lt;/a&gt; class.&lt;/p&gt;

&lt;h3&gt;Running tests&lt;/h3&gt;


&lt;p&gt;OJUnit tests can be run using the ojtest command. At the top-level directory of your project simply run&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ ojtest SomeClassTest.j
&lt;/pre&gt;


&lt;p&gt;with the filenames of all your tests. It is important that you are at your project's top-level directory, or Objective-J may not be able to locate your files correctly.&lt;/p&gt;

&lt;p&gt;Alternatively, you can run all your tests using wildcards, like:&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ ojtest Test/*Test.j
&lt;/pre&gt;


&lt;p&gt;This will run all the tests with filenames that end in Test.j located in the directory Test/.&lt;/p&gt;

&lt;p&gt;To simplify test execution, you can add a task to your jakefile&lt;/p&gt;

&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;code class="js"&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FileList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test/*Test.j'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ojtest"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;cmdString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enquote&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmdString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;OS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;and then invoke it with&lt;/p&gt;

&lt;pre class="terminal"&gt;
$ jake test
&lt;/pre&gt;


&lt;p&gt;Happy testing!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=qC9m90xhIgs:DHzhvLWw6G0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=qC9m90xhIgs:DHzhvLWw6G0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=qC9m90xhIgs:DHzhvLWw6G0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ternarylabs?a=qC9m90xhIgs:DHzhvLWw6G0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ternarylabs?i=qC9m90xhIgs:DHzhvLWw6G0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ternarylabs/~4/qC9m90xhIgs" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://ternarylabs.com/2010/06/03/unit-testing-objective-j-apps-with-ojunit</feedburner:origLink></entry>
 
</feed>

