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

  <title><![CDATA[Paul Roub]]></title>
  <link href="http://blog.roub.net/atom.xml" rel="self"/>
  <link href="http://blog.roub.net/"/>
  <updated>2013-05-01T11:56:19-04:00</updated>
  <id>http://blog.roub.net/</id>
  <author>
    <name><![CDATA[Paul Roub]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Handling Simultaneous Updates in Zumero: There Is No Step 2]]></title>
    <link href="http://blog.roub.net/2013/05/handling-simultaneous-updates-in-zumero-there-is-no-step-2.html"/>
    <updated>2013-05-01T10:51:00-04:00</updated>
    <id>http://blog.roub.net/2013/05/handling-simultaneous-updates-in-zumero-there-is-no-step-2</id>
    <content type="html"><![CDATA[<p>When we talk about the advantages of <a href="http://www.ericsink.com/entries/crud.html" title="Keep your CRUD off the Internet">always-local data for mobile apps</a>, the typical first reaction is &#8220;that sounds awesome&#8221;.</p>

<p>The next step is to start thinking about your apps, and how mobile users interact with your data, and how those users aren&#8217;t going to sit down and coordinate with each other:</p>

<blockquote><p>&#8220;It&#8217;s my turn to update the inventory records for store #42&#8221;</p>

<p>&#8220;Done with those yet?&#8221;</p>

<p>&#8220;Yep, go ahead and sync.&#8221;</p>

<p>&#8220;Cool, thanks. Now I can update the ordered-quantity data.&#8221;</p></blockquote>

<p>(a) That&#8217;s not how work gets done, (b) computers are supposed to take care of picky details for us, and (c) I don&#8217;t want to hang out with those guys at <em>all</em>.</p>

<p>Dealing with this scenario in a Zumero-enabled app boils down to:</p>

<ul>
<li>Users update and add data as they see fit.</li>
<li>At some point, probably in the background, the app syncs to the server.</li>
<li>Up-to-date, merged data now lives on the device.</li>
</ul>


<p>You don&#8217;t have to write any code that knows conflicting changes happened. Seriously. That&#8217;s our problem.</p>

<p>Even when the changes are tricky.</p>

<p>Consider the <a href="https://github.com/zumero/wiki">Zumero wiki sample app</a>. A wiki is a collaborative tool. People can, will, should edit the same pages. Sometimes at the same time.</p>

<p>What happens if they do? Andy creates a page on his iPad:</p>

<p class="shot"><img alt=" " src="http://blog.roub.net/i/ipad-initial.png" /></p>


<p>Colin pulls up the app on his iPhone, syncs, and gets his own copy. While offline, he adds a line to the page:</p>

<p class="shot"><img alt=" " src="http://blog.roub.net/i/iphone-added.png" /></p>


<p>At the same time, Andy completes one of the items on his list, and marks that line as completed.</p>

<p class="shot"><img alt=" " src="http://blog.roub.net/i/ipad-completed.png" /></p>


<p>Colin comes back online, his app checks in and syncs, and he sees this:</p>

<p class="shot"><img alt=" " src="http://blog.roub.net/i/iphone-merged.png" /></p>


<p>Here&#8217;s the code that caused their apps to reconcile the conflicting updates:</p>

<figure class='code'><figcaption><span>Sync a Zumero database </span><a href='https://github.com/zumero/wiki/blob/master/Zumero%20Wiki/ZWMasterViewController.m'>link</a></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">ok</span> <span class="o">=</span> <span class="p">[</span><span class="n">_db</span> <span class="nl">sync:</span><span class="n">scheme</span> <span class="nl">user:</span><span class="n">username</span> <span class="nl">password:</span><span class="n">password</span> <span class="nl">error:</span><span class="o">&amp;</span><span class="n">err</span><span class="p">];</span>
</span></code></pre></td></tr></table></div></figure>


<p>There&#8217;s no step 2.</p>

<p>To be fair, there were a couple of lines of code that ran when we initially set up the database, which boiled down to:</p>

<ol>
<li>If there are conflicting edits on the &#8216;text&#8217; field of a wiki page, try to merge them, Zumero. Thanks.</li>
</ol>


<p>No step 2.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Zumero: Background Sync in Objective-C]]></title>
    <link href="http://blog.roub.net/2013/04/zumero-background-sync-in-objective-c.html"/>
    <updated>2013-04-09T10:31:00-04:00</updated>
    <id>http://blog.roub.net/2013/04/zumero-background-sync-in-objective-c</id>
    <content type="html"><![CDATA[<h2>Mobile offline RSS reader, Part 5</h2>

<p>In the second &#8220;crossover episode&#8221; of our series (previous episodes begin on <a href="http://www.ericsink.com/entries/rss_cat_1.html" title="Using Zumero from C#">Eric&#8217;s blog</a>), we look at background operations.</p>

<p>Previously, we built <a href="http://zumero.com/">Zumero</a> databases full of RSS lists and feed contents.  The <a href="http://github.com/zumero/ZumeroReader">ZumeroReader</a> sample app pulls, reads and displays those feeds on iOS devices.  In action, it looks like this:</p>

<p class="shot"><img alt=" " src="http://blog.roub.net/i/ipadreader.png" /></p>


<p>At startup, it pulls the feed list like so:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">ZumeroDB</span> <span class="o">*</span><span class="n">db</span> <span class="o">=</span> <span class="p">[[</span><span class="n">ZumeroDB</span> <span class="n">alloc</span><span class="p">]</span>
</span><span class='line'>              <span class="nl">initWithName:</span><span class="s">@&quot;all_feeds&quot;</span>
</span><span class='line'>              <span class="nl">folder:</span><span class="n">path</span> <span class="nl">host:</span><span class="n">host</span><span class="p">];</span>
</span><span class='line'><span class="n">db</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="n">NSError</span> <span class="o">*</span><span class="n">err</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
</span><span class='line'><span class="kt">BOOL</span> <span class="n">ok</span> <span class="o">=</span> <span class="n">YES</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="p">[</span><span class="n">db</span> <span class="n">exists</span><span class="p">])</span>
</span><span class='line'>  <span class="n">ok</span> <span class="o">=</span> <span class="p">[</span><span class="n">db</span> <span class="nl">createDB:</span><span class="o">&amp;</span><span class="n">err</span><span class="p">];</span>
</span><span class='line'>
</span><span class='line'><span class="n">ok</span> <span class="o">=</span> <span class="n">ok</span> <span class="o">&amp;&amp;</span> <span class="p">([</span><span class="n">db</span> <span class="n">isOpen</span><span class="p">]</span> <span class="o">||</span> <span class="p">[</span><span class="n">db</span> <span class="nl">open:</span><span class="o">&amp;</span><span class="n">err</span><span class="p">]);</span>
</span><span class='line'>
</span><span class='line'><span class="n">NSDictionary</span> <span class="o">*</span><span class="n">scheme</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
</span><span class='line'><span class="n">NSString</span> <span class="o">*</span><span class="n">uname</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
</span><span class='line'><span class="n">NSString</span> <span class="o">*</span><span class="n">pwd</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="n">ok</span> <span class="o">=</span> <span class="n">ok</span> <span class="o">&amp;&amp;</span> <span class="p">[</span><span class="n">db</span> <span class="nl">sync:</span><span class="n">scheme</span> <span class="nl">user:</span><span class="n">uname</span> <span class="nl">password:</span><span class="n">pwd</span> <span class="nl">error:</span><span class="o">&amp;</span><span class="n">err</span><span class="p">];</span>
</span></code></pre></td></tr></table></div></figure>


<p>We&#8217;re creating a Zumero database file named &#8220;all_feeds&#8221;. It will sync with a similarly-named dbfile on the server side.</p>

<p>We don&#8217;t need any authentication info, since (as <a href="http://www.ericsink.com/entries/rss_cat_1.html" title="Using Zumero from C#">previously discussed</a>) the feed lists and feed databases are pullable by anyone.</p>

<p>The <code>ZumeroDB::sync</code> method <em>always</em> performs its network activity on a background thread, calling delegate methods when the action is completed (hence <code>db.delegate = self</code>).</p>

<p>In this case, on sync success, we grab the latest copies of the individual feed databases:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">ok</span> <span class="o">=</span> <span class="p">[</span><span class="n">db</span> <span class="nl">selectSql:</span><span class="s">@&quot;select feeds.feedid, url, title from feeds, about &quot;</span>
</span><span class='line'>    <span class="s">&quot;where (feeds.feedid = about.feedid)&quot;</span> <span class="nl">values:</span><span class="nb">nil</span>
</span><span class='line'>    <span class="nl">rows:</span><span class="o">&amp;</span><span class="n">rows</span> <span class="nl">error:</span><span class="o">&amp;</span><span class="n">err</span><span class="p">];</span>
</span><span class='line'><span class="c1">// ...</span>
</span><span class='line'>  
</span><span class='line'><span class="k">for</span> <span class="p">(</span><span class="n">NSDictionary</span> <span class="o">*</span><span class="n">row</span> <span class="k">in</span> <span class="n">rows</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>  <span class="n">NSNumber</span> <span class="o">*</span><span class="kt">id</span> <span class="o">=</span> <span class="p">[</span><span class="n">row</span> <span class="nl">objectForKey:</span><span class="s">@&quot;feedid&quot;</span><span class="p">];</span>
</span><span class='line'>  
</span><span class='line'>  <span class="p">[</span><span class="n">self</span> <span class="nl">syncFeed:</span><span class="kt">id</span><span class="p">];</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// ...</span>
</span><span class='line'>
</span><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="nl">syncFeed:</span><span class="p">(</span><span class="n">NSNumber</span> <span class="o">*</span><span class="p">)</span><span class="n">feedid</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>  <span class="c1">// ...</span>
</span><span class='line'>  <span class="n">NSString</span> <span class="o">*</span><span class="n">dbname</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSString</span> <span class="nl">stringWithFormat:</span><span class="s">@&quot;feed_%@&quot;</span><span class="p">,</span> <span class="n">feedid</span><span class="p">];</span>
</span><span class='line'>  <span class="n">ZumeroDB</span> <span class="o">*</span><span class="n">db</span> <span class="o">=</span> <span class="p">[[</span><span class="n">ZumeroDB</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithName:</span><span class="n">dbname</span> <span class="nl">folder:</span><span class="n">path</span> <span class="nl">host:</span><span class="n">host</span><span class="p">];</span>
</span><span class='line'>  <span class="c1">// ...</span>
</span><span class='line'>  <span class="n">db</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
</span><span class='line'>  <span class="n">ok</span> <span class="o">=</span> <span class="p">[</span><span class="n">db</span> <span class="nl">sync:</span><span class="nb">nil</span> <span class="nl">user:</span><span class="nb">nil</span> <span class="nl">password:</span><span class="nb">nil</span> <span class="nl">error:</span><span class="o">&amp;</span><span class="n">err</span><span class="p">];</span>
</span><span class='line'>  <span class="c1">// ...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>But what if feeds are updated while the app is running?  There are seemingly infinite strategies for launching background tasks in iOS, and you&#8217;ll use the one that fits your application best.  In this case, I cribbed a simple plan from a <a href="http://stackoverflow.com/questions/273450/iphone-detecting-user-inactivity-idle-time-since-last-screen-touch" title="iPhone: Detecting user inactivity/idle time since last screen touch">StackOverflow post</a> - every time a touch is detected, we restart a timer.  If that timer actually manages to expire, then we&#8217;ve seen no touches in 5 seconds.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">sendEvent:</span><span class="p">(</span><span class="n">UIEvent</span> <span class="o">*</span><span class="p">)</span><span class="n">event</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">[</span><span class="n">super</span> <span class="nl">sendEvent:</span><span class="n">event</span><span class="p">];</span>
</span><span class='line'>  
</span><span class='line'>    <span class="n">NSSet</span> <span class="o">*</span><span class="n">allTouches</span> <span class="o">=</span> <span class="p">[</span><span class="n">event</span> <span class="n">allTouches</span><span class="p">];</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">([</span><span class="n">allTouches</span> <span class="n">count</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">UITouchPhase</span> <span class="n">phase</span> <span class="o">=</span> <span class="p">((</span><span class="n">UITouch</span> <span class="o">*</span><span class="p">)[</span><span class="n">allTouches</span> <span class="n">anyObject</span><span class="p">]).</span><span class="n">phase</span><span class="p">;</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="n">phase</span> <span class="o">==</span> <span class="n">UITouchPhaseBegan</span> <span class="o">||</span> <span class="n">phase</span> <span class="o">==</span> <span class="n">UITouchPhaseEnded</span><span class="p">)</span>
</span><span class='line'>            <span class="p">[</span><span class="n">self</span> <span class="nl">resetIdleTimer:</span><span class="n">maxIdleTime</span><span class="p">];</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">resetIdleTimer:</span><span class="p">(</span><span class="n">NSTimeInterval</span><span class="p">)</span><span class="n">secs</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">idleTimer</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="p">[</span><span class="n">idleTimer</span> <span class="n">invalidate</span><span class="p">];</span>
</span><span class='line'>      <span class="n">idleTimer</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  
</span><span class='line'>    <span class="n">idleTimer</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSTimer</span> <span class="nl">scheduledTimerWithTimeInterval:</span><span class="n">secs</span>
</span><span class='line'>               <span class="nl">target:</span><span class="n">self</span> <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="n">idleTimerExceeded</span><span class="p">)</span>
</span><span class='line'>               <span class="nl">userInfo:</span><span class="nb">nil</span> <span class="nl">repeats:</span><span class="n">NO</span><span class="p">];</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>When that timer fires, we check to see if its been 5 minutes since our last sync, and that a sync is wanted. If so, we sync again:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">idleTimerExceeded</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">wantToSync</span><span class="p">)</span>
</span><span class='line'>  <span class="p">{</span>
</span><span class='line'>      <span class="n">NSTimeInterval</span> <span class="n">since</span> <span class="o">=</span> <span class="p">[</span><span class="n">nextSync</span> <span class="n">timeIntervalSinceNow</span><span class="p">];</span>
</span><span class='line'>      
</span><span class='line'>      <span class="k">if</span> <span class="p">(</span><span class="n">since</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span>
</span><span class='line'>      <span class="p">{</span>
</span><span class='line'>          <span class="n">wantToSync</span> <span class="o">=</span> <span class="nb">FALSE</span><span class="p">;</span>
</span><span class='line'>          
</span><span class='line'>          <span class="kt">BOOL</span> <span class="n">ok</span> <span class="o">=</span> <span class="nb">FALSE</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>          <span class="c1">// the view controller&#39;s sync method from earlier          </span>
</span><span class='line'>          <span class="k">if</span> <span class="p">(</span><span class="n">mvc</span><span class="p">)</span>
</span><span class='line'>              <span class="n">ok</span> <span class="o">=</span> <span class="p">[</span><span class="n">mvc</span> <span class="n">sync</span><span class="p">];</span>
</span><span class='line'>          
</span><span class='line'>          <span class="k">if</span> <span class="p">(</span><span class="n">ok</span><span class="p">)</span>
</span><span class='line'>              <span class="n">self</span><span class="p">.</span><span class="n">networkActivityIndicatorVisible</span> <span class="o">=</span> <span class="n">YES</span><span class="p">;</span>
</span><span class='line'>          <span class="k">else</span>
</span><span class='line'>              <span class="c1">// the sync call failed; try again later</span>
</span><span class='line'>              <span class="p">[</span><span class="n">self</span> <span class="nl">waitForSync:</span><span class="p">(</span><span class="mi">10</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)];</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>      <span class="k">else</span>
</span><span class='line'>      <span class="p">{</span>
</span><span class='line'>          <span class="c1">// nope, check again next idle time</span>
</span><span class='line'>          <span class="p">[</span><span class="n">self</span> <span class="nl">resetIdleTimer:</span><span class="n">since</span><span class="p">];</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>      
</span><span class='line'>      <span class="k">return</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  
</span><span class='line'>  <span class="p">[</span><span class="n">self</span> <span class="nl">resetIdleTimer:</span><span class="n">maxIdleTime</span><span class="p">];</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>We also kill our timers when exiting, restart them when waking up or activating, etc.</p>

<p>Finally, when we go into the background, we try for one last sync before our process is suspended:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">applicationDidEnterBackground:</span><span class="p">(</span><span class="n">UIApplication</span> <span class="o">*</span><span class="p">)</span><span class="n">application</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>  <span class="c1">// ...</span>
</span><span class='line'>  <span class="n">bgtask</span> <span class="o">=</span> <span class="p">[</span><span class="n">application</span> <span class="nl">beginBackgroundTaskWithExpirationHandler:</span><span class="o">^</span><span class="p">{</span>
</span><span class='line'>        <span class="p">[</span><span class="n">self</span> <span class="nl">endBackgroundTask:</span><span class="n">bgtask</span><span class="p">];</span>
</span><span class='line'>        <span class="n">bgtask</span> <span class="o">=</span> <span class="n">UIBackgroundTaskInvalid</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}];</span>
</span><span class='line'>    <span class="n">dispatch_async</span><span class="p">(</span><span class="n">dispatch_get_global_queue</span><span class="p">(</span>
</span><span class='line'>      <span class="n">DISPATCH_QUEUE_PRIORITY_DEFAULT</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="o">^</span><span class="p">{</span>
</span><span class='line'>      <span class="p">[</span><span class="n">mvc</span> <span class="n">sync</span><span class="p">];</span>
</span><span class='line'>      <span class="c1">// finishBackgroundTask will be called by the sync handlers</span>
</span><span class='line'>    <span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Like any good sample app, there&#8217;s so much that this app <em>doesn&#8217;t</em> do.  You&#8217;re invited to experiment with adding them.</p>

<ol>
<li>We don&#8217;t maintain any read/unread information, either at the feed level or for individual items.
This would be a good place to try creating/syncing new databases.</li>
<li>We naïvely grab the first <code>&lt;link&gt;</code> element we see and assume that it&#8217;s our permalink, without ever
checking its <code>rel</code> attribute or content type.</li>
<li>A feed&#8217;s contents are displayed as one big chunk of HTML. Individual table cells might be nice.</li>
<li>We don&#8217;t make any attempt to become a long-running background process, since we don&#8217;t actually play audio, collect
location information, etc. Might be fun to play with that.</li>
<li>Configuring the app is done by editing the source and rebuilding. That seems rude.</li>
<li>If you <em>did</em> update some sort of last-read database, wouldn&#8217;t that be a great moment to kick off a background sync? (The <a href="https://github.com/zumero/wiki">Wiki</a> app included in the <a href="http://zumero.com/dev-center">Zumero SDK</a> does this when a page is created or saved)</li>
</ol>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Altering a Zumero Table]]></title>
    <link href="http://blog.roub.net/2013/04/altering-a-zumero-table.html"/>
    <updated>2013-04-09T10:26:00-04:00</updated>
    <id>http://blog.roub.net/2013/04/altering-a-zumero-table</id>
    <content type="html"><![CDATA[<h2>Mobile offline RSS reader, Part 4</h2>

<p>In <a href="http://www.ericsink.com/entries/rss_cat_3.html" title="Zumero: Efficient sync by using multiple SQLite files">part 3</a> of this series, Eric discussed three programs that do the back-end work
of RSS aggregation - scanning feed lists, caching summaries and IDs, creating data files as
we go.</p>

<p>Before we get into <em>using</em> that information in part 5, we need to adjust one of the schemas a bit.</p>

<p>The <code>items</code> table in each feed&#8217;s database is set up as:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">CREATE</span> <span class="n">VIRTUAL</span> <span class="k">TABLE</span> <span class="n">items</span>
</span><span class='line'><span class="k">USING</span> <span class="n">zumero</span>
</span><span class='line'><span class="p">(</span>
</span><span class='line'>  <span class="n">id</span> <span class="nb">TEXT</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
</span><span class='line'>  <span class="n">title</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
</span><span class='line'>  <span class="n">summary</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
</span><span class='line'>  <span class="n">pubdate_unix_time</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span>
</span><span class='line'><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>Where the <code>id</code> field comes from the feed items&#8217;s <code>id</code> or <code>guid</code> element, or from the first <code>link</code>
found in the entry.  In many RSS feeds, <code>id</code> will also be the permalink to the entry&#8217;s
&#8220;destination&#8221;, e.g.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;item&gt;</span>
</span><span class='line'>  <span class="nt">&lt;title&gt;</span>Enterprise mobile will have a lot of SQL going on<span class="nt">&lt;/title&gt;</span>
</span><span class='line'>  <span class="nt">&lt;guid&gt;</span>http://www.ericsink.com/entries/mobile_sql.html<span class="nt">&lt;/guid&gt;</span>
</span><span class='line'>  ...
</span></code></pre></td></tr></table></div></figure>


<p>But that&#8217;s not mandatory. For example:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;entry&gt;</span>
</span><span class='line'>  <span class="nt">&lt;id&gt;</span>http://zumero.com/2013/04/04/just-released-the-zumero-development-server<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>  <span class="nt">&lt;link</span> <span class="na">type=</span><span class="s">&quot;text/html&quot;</span> <span class="na">rel=</span><span class="s">&quot;alternate&quot;</span>
</span><span class='line'>   <span class="na">href=</span><span class="s">&quot;http://zumero.com/2013/04/04/just-released-the-zumero-development-server.html&quot;</span><span class="nt">/&gt;</span>
</span><span class='line'>  <span class="nt">&lt;title&gt;</span>Just Released: the Zumero Development Server<span class="nt">&lt;/title&gt;</span>
</span><span class='line'>  ...
</span></code></pre></td></tr></table></div></figure>


<p>We still want that unique <code>id</code>, but if a link is available, we&#8217;ll want to store that, too.</p>

<p>Easy enough when creating a new <code>items</code> table. We&#8217;ll alter <a href="https://github.com/paulroub/z_rss/blob/master/z_rss_create.cs"><code>z_rss_create.cs</code></a> to read:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='c#'><span class='line'><span class="n">db</span><span class="p">.</span><span class="n">Execute</span><span class="p">(</span>
</span><span class='line'>        <span class="s">@&quot;CREATE VIRTUAL TABLE </span>
</span><span class='line'><span class="s">        cur.items </span>
</span><span class='line'><span class="s">        USING zumero</span>
</span><span class='line'><span class="s">        (</span>
</span><span class='line'><span class="s">          id TEXT PRIMARY KEY NOT NULL, </span>
</span><span class='line'><span class="s">          title TEXT NOT NULL,</span>
</span><span class='line'><span class="s">          summary TEXT NOT NULL,</span>
</span><span class='line'><span class="s">          pubdate_unix_time INTEGER NOT NULL,</span>
</span><span class='line'><span class="s">          permalink TEXT</span>
</span><span class='line'><span class="s">        );&quot;</span>
</span><span class='line'>        <span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>But what about <code>items</code> tables that already exist? Unfortunately, SQLite <a href="http://www.sqlite.org/vtab.html" title="The Virtual Table Mechanism Of SQLite">does not allow</a> <code>alter table ... add column</code> to run against a virtual table like Zumero&#8217;s.  We&#8217;ll use the Zumero-provided alternative, <a href="http://zumero.com/docs/zumero_core.html#chapter_functions"><code>zumero_alter_table_add_column()</code></a>.</p>

<p>In <a href="https://github.com/paulroub/z_rss/blob/master/z_rss_update.cs"><code>z_rss_update.cs</code></a>, we&#8217;ll see if the new column already exists:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='c#'><span class='line'><span class="n">var</span> <span class="n">query</span> <span class="p">=</span> <span class="s">&quot;pragma cur.table_info(\&quot;items\&quot;)&quot;</span><span class="p">;</span>
</span><span class='line'><span class="n">List</span><span class="p">&lt;</span><span class="n">SQLiteConnection</span><span class="p">.</span><span class="n">ColumnInfo</span><span class="p">&gt;</span> <span class="n">cols</span> <span class="p">=</span> <span class="n">db</span><span class="p">.</span><span class="n">Query</span><span class="p">&lt;</span><span class="n">SQLiteConnection</span><span class="p">.</span><span class="n">ColumnInfo</span><span class="p">&gt;</span> <span class="p">(</span><span class="n">query</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">foreach</span> <span class="p">(</span><span class="n">var</span> <span class="n">c</span> <span class="k">in</span> <span class="n">cols</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">found</span> <span class="p">=</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="n">Compare</span> <span class="p">(</span><span class="s">&quot;permalink&quot;</span><span class="p">,</span> <span class="n">c</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">StringComparison</span><span class="p">.</span><span class="n">OrdinalIgnoreCase</span><span class="p">)</span> <span class="p">==</span> <span class="m">0</span><span class="p">);</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">found</span><span class="p">)</span>
</span><span class='line'>        <span class="k">break</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>If not&#8230;</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='c#'><span class='line'><span class="n">db</span><span class="p">.</span><span class="n">Execute</span><span class="p">(</span><span class="s">&quot;BEGIN TRANSACTION;&quot;</span><span class="p">);</span>
</span><span class='line'><span class="n">db</span><span class="p">.</span><span class="n">ExecuteScalar</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="s">@&quot;select zumero_alter_table_add_column(</span>
</span><span class='line'><span class="s">   &#39;cur&#39;, &#39;items&#39;, &#39;permalink TEXT&#39;);&quot;</span><span class="p">);</span>
</span><span class='line'><span class="n">db</span><span class="p">.</span><span class="n">Execute</span><span class="p">(</span><span class="s">&quot;COMMIT TRANSACTION;&quot;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// after altering a zumero table, the dbfile connection must be reopened</span>
</span><span class='line'>
</span><span class='line'><span class="n">db</span><span class="p">.</span><span class="n">Execute</span><span class="p">(</span><span class="s">&quot;DETACH cur;&quot;</span><span class="p">,</span> <span class="n">dbfile_name_for_this_feed</span><span class="p">);</span>
</span><span class='line'><span class="n">db</span><span class="p">.</span><span class="n">Execute</span><span class="p">(</span><span class="s">&quot;ATTACH ? AS cur;&quot;</span><span class="p">,</span> <span class="n">dbfile_name_for_this_feed</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>See <a href="https://github.com/paulroub/z_rss">this fork</a> of <code>z_rss</code> for all the necessary code.</p>

<p>In part 5, we&#8217;ll see a simple iOS RSS reader that pulls these databases in the background, and
lets you browse the feed summaries and link through to the full articles.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Veracity 1.5 Released]]></title>
    <link href="http://blog.roub.net/2011/12/veracity-1-dot-5-released.html"/>
    <updated>2011-12-20T16:27:00-05:00</updated>
    <id>http://blog.roub.net/2011/12/veracity-1-dot-5-released</id>
    <content type="html"><![CDATA[<p>Just a quick post to note that we&#8217;ve released <a href="http://veracity-scm.com/">Veracity</a> 1.5.</p>

<p>The most-visible new features:</p>

<ul>
<li>A Tortoise-style extension for Windows users. Jeremy gives you an
illustrated once over on the <a href="http://veracity-scm.com/qa/questions/561/how-do-i-get-started-with-the-veracity-tortoise-client" title="How do I get started with the Veracity Tortoise Client?">Veracity Q and A</a> site.</li>
</ul>


<p><a href="http://veracity-scm.com/qa/questions/561/how-do-i-get-started-with-the-veracity-tortoise-client" title="How do I get started with the Veracity Tortoise Client?"><img src="http://blog.roub.net/i/workingcopy.jpg" alt="Alt text" /></a></p>

<ul>
<li>The afore-blogged <a href="http://localhost:4000/2011/08/building-a-veracity-module-part-1.html" title="Building a Veracity Module - Part 1">wiki module</a> is now installed by default,
and brings a variety of bug fixes and improvements.</li>
</ul>


<p>More details to come soon, but I have a race condition to bang my head
against.</p>

<p>Meantime, checkout the <a href="http://veracity-scm.com/releasenotes/1.5.html" title="Veracity 1.5 Release Notes">release notes</a> and <a href="http://veracity-scm.com/downloads.html" title="Download Veracity">downloads</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building a Veracity Module - Part 4]]></title>
    <link href="http://blog.roub.net/2011/08/building-a-veracity-module-part-4.html"/>
    <updated>2011-08-19T15:16:17-04:00</updated>
    <id>http://blog.roub.net/2011/08/building-a-veracity-module-part-4</id>
    <content type="html"><![CDATA[<p>Finishing up our <a href="http://veracity-scm.com/" title="Veracity - a new open source Distributed Version Control System">Veracity</a> module overview, let&#8217;s look at a few &#8220;fitting in&#8221; considerations.</p>

<h2>How&#8217;d We End Up in the Menu?</h2>

<p>If your <code>server_files/ui/modules/yourmodule</code> folder contains a <code>menu.js</code> file, that file will be loaded via a <code>&lt;script&gt;</code> tag in the footer of every Veracity web page.</p>

<p>Whatever you want to add/change in the Veracity menu, do it here.  In our case (and most cases), we just append an item to the <code>&lt;ul&gt;</code> named <code>topmenu</code>, which is (surprise!) Veracity&#8217;s top-level menu:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="kd">var</span> <span class="nx">tm</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;#topmenu&#39;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">mi</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&quot;&lt;li id=&#39;topmenuwiki&#39;&gt;&lt;/li&gt;&quot;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">ln</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&quot;&lt;a class=&#39;menulink&#39;&gt;wiki&lt;/a&gt;&quot;</span><span class="p">).</span>
</span><span class='line'>    <span class="nx">attr</span><span class="p">(</span><span class="s1">&#39;href&#39;</span><span class="p">,</span> <span class="nx">sgCurrentRepoUrl</span> <span class="o">+</span> <span class="s2">&quot;/wiki.html&quot;</span><span class="p">).</span>
</span><span class='line'>    <span class="nx">appendTo</span><span class="p">(</span><span class="nx">mi</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="nx">tm</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="nx">mi</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>Notice that you can count on <a href="http://jquery.com/" title="jQuery: The Write Less, Do More, JavaScript Library">jQuery</a> being available to your code.</p>

<h2>What About the Activity Stream?</h2>

<p>Wiki page changes show up in Veracity&#8217;s activity stream, alongside commits, bug updates, etc.  The activity stream interface is a simple one: you need to create an object supporting three methods:</p>

<ul>
<li><code>name()</code>: returns a string describing this particular activity component, for debug logging. Totally up to you.</li>
<li><code>dagsUsed()</code>: returns an array of database DAG IDs, for caching. Include any DAGs your activity stream might query.  In our case, it&#8217;s <code>[sg.dagnum.WIKI, sg.dagnum.USERS]</code></li>
<li><code>getActivity()</code>: where all of the work happens</li>
</ul>


<p><code>getActivity()</code> returns an array of objects, with (at least) the following members:</p>

<ul>
<li><code>what</code>: A short description of the object that changed, updated, etc.</li>
<li><code>title</code>: Usually redundant to &#8220;what&#8221;. Used for Atom entry titles.</li>
<li><code>action</code>: What happened to that thing (created, updated, deleted, fixed&#8230;)</li>
<li><code>who</code>: The Veracity user ID to whom this activity should be attributed (a committer, the editor of this particular Wiki change, etc.)</li>
<li><code>when</code>: The (Unix timestamp) time when this activity occurred.</li>
<li><code>link</code>: Optional, a link to the object, its history, etc.</li>
</ul>


<p>Bug updates, for example, contain (among other things):</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="s2">&quot;what&quot;</span><span class="o">:</span> <span class="s2">&quot;Work items that reference missing changesets can not be viewed&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="s2">&quot;title: &quot;</span><span class="nx">Work</span> <span class="nx">items</span> <span class="nx">that</span> <span class="nx">reference</span> <span class="nx">missing</span> <span class="nx">changesets</span> <span class="nx">can</span> <span class="nx">not</span> <span class="nx">be</span> <span class="nx">viewed</span><span class="s2">&quot;,</span>
</span><span class='line'><span class="s2">    &quot;</span><span class="nx">action</span><span class="s2">&quot;: &quot;</span><span class="nx">Fixed</span> <span class="nx">X1384</span><span class="s2">&quot;,</span>
</span><span class='line'><span class="s2">    &quot;</span><span class="nx">who</span><span class="s2">&quot;:&quot;</span><span class="nx">g02d63075631e47bc8a29dad7027f59d382cff0ac413311e0838c60fb42f09aca</span><span class="s2">&quot;,</span>
</span><span class='line'><span class="s2">    &quot;</span><span class="nx">when</span><span class="s2">&quot;:1313620280696.000000,</span>
</span><span class='line'><span class="s2">    &quot;</span><span class="nx">link</span><span class="s2">&quot;:&quot;</span><span class="o">/</span><span class="nx">workitem</span><span class="p">.</span><span class="nx">html</span><span class="o">?</span><span class="nx">recid</span><span class="o">=</span><span class="nx">gdbb98600a5114533a0a936226f4b2efb8e381b80c91811e0b40f1c6f65d71da9</span><span class="err">&quot;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>You should return the most recent <em>N</em> items. The activity stream wrappers will sort them in with other activity sources before returning the JSON or Atom stream.</p>

<p>In the wiki&#8217;s case, we build records like so:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="kd">var</span> <span class="nx">record</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">what</span><span class="o">:</span> <span class="nx">thispage</span><span class="p">.</span><span class="nx">title</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">title</span><span class="o">:</span> <span class="nx">thispage</span><span class="p">.</span><span class="nx">title</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">who</span><span class="o">:</span> <span class="nx">thispage</span><span class="p">.</span><span class="nx">userid</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">when</span><span class="o">:</span> <span class="nx">thispage</span><span class="p">.</span><span class="nx">timestamp</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="nx">first</span><span class="p">)</span>
</span><span class='line'>    <span class="nx">record</span><span class="p">.</span><span class="nx">action</span> <span class="o">=</span> <span class="s2">&quot;Created Wiki page&quot;</span><span class="p">;</span>
</span><span class='line'><span class="k">else</span>
</span><span class='line'>    <span class="nx">record</span><span class="p">.</span><span class="nx">action</span> <span class="o">=</span> <span class="s2">&quot;Edited Wiki page&quot;</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="nx">lastpage</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="nx">lastpage</span><span class="p">.</span><span class="nx">title</span> <span class="o">!=</span> <span class="nx">thispage</span><span class="p">.</span><span class="nx">title</span><span class="p">)</span>
</span><span class='line'>    <span class="p">{</span>
</span><span class='line'>        <span class="nx">record</span><span class="p">.</span><span class="nx">action</span> <span class="o">=</span> <span class="s2">&quot;Renamed Wiki page&quot;</span><span class="p">;</span>
</span><span class='line'>        <span class="nx">record</span><span class="p">.</span><span class="nx">what</span> <span class="o">+=</span> <span class="s2">&quot; (was &quot;</span> <span class="o">+</span> <span class="nx">lastpage</span><span class="p">.</span><span class="nx">title</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">record</span><span class="p">.</span><span class="nx">link</span> <span class="o">=</span> <span class="s1">&#39;/wiki.html?page=&#39;</span> <span class="o">+</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">title</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>The first time a page is seen, we report it as &#8220;created&#8221;; thereafter, as &#8220;edited&#8221;.  If the title changes along the way, we note that instead.</p>

<p class="shot"><img alt="Activity stream including wiki and bug updates" src="http://blog.roub.net/i/activity-wiki.png" width="209" height="345" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>


<p>That&#8217;s about it, as module high points go.  Further questions are very much welcome at the <a href="http://veracity-scm.com/qa" title="Veracity Q&amp;A">Veracity Q/A</a> site.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building a Veracity Module - Part 3]]></title>
    <link href="http://blog.roub.net/2011/08/building-a-veracity-module-part-3.html"/>
    <updated>2011-08-18T11:13:59-04:00</updated>
    <id>http://blog.roub.net/2011/08/building-a-veracity-module-part-3</id>
    <content type="html"><![CDATA[<p>So how does the Wiki module work, anyway?  Pretty much the way you&#8217;d
expect a web app to work.</p>

<ol>
<li>The Wiki form is displayed (and maybe pre-populated with the page&#8217;s
title and text)</li>
<li>You edit the title and text, and submit</li>
<li>We bundle those fields up into a JSON package</li>
<li>And send that along to the server-side Wiki code</li>
<li>That code either

<ol>
<li>Updates the page if it already existed, or</li>
<li>Creates a new page</li>
</ol>
</li>
<li>On success, we reload the wiki page</li>
</ol>


<p>All of the Veracity-specific stuff happens on the server side.</p>

<h3>Retrieving a Page</h3>

<p>We retrieve the existing page in the GET
<code>/wiki/pages/&lt;pagename&gt;.json</code> route (mentioned in <a href="http://blog.roub.net/2011/08/building_a_veracity_module_part_2.html" title="Building a Veracity Module - Part 2">Part 2</a> the other day).  Normally (q.v.), that&#8217;s as simple as:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="kd">var</span> <span class="nx">db</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">zingdb</span><span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">repo</span><span class="p">,</span> <span class="nx">sg</span><span class="p">.</span><span class="nx">dagnum</span><span class="p">.</span><span class="nx">WIKI</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">w</span> <span class="o">=</span> <span class="nx">vv</span><span class="p">.</span><span class="nx">where</span><span class="p">(</span> <span class="p">{</span> <span class="s2">&quot;title&quot;</span><span class="o">:</span> <span class="nx">request</span><span class="p">.</span><span class="nx">pagename</span> <span class="p">}</span> <span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">recs</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="s1">&#39;page&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;text&#39;</span><span class="p">,</span><span class="s1">&#39;title&#39;</span><span class="p">,</span><span class="s1">&#39;recid&#39;</span><span class="p">],</span> <span class="nx">w</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">return</span><span class="p">(</span> <span class="nx">recs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>Which translates to &#8220;Open the wiki database, find any records matching
our pagename, grab their <code>text</code>, <code>title</code> and <code>recid</code> fields, and
return the first one.&#8221;  We can get away with this since our database
template requires the pagename to be unique.</p>

<p>A JSON representation of that object is returned (you&#8217;ll also see some
caching logic in there, but that&#8217;s strictly a performance measure,
ignorant of the Wiki data).</p>

<h3>Creating and Updating</h3>

<p>Updates work like so:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="kd">var</span> <span class="nx">csid</span> <span class="o">=</span> <span class="nx">newrec</span><span class="p">.</span><span class="nx">_csid</span> <span class="o">||</span> <span class="kc">null</span><span class="p">;</span>
</span><span class='line'><span class="k">delete</span> <span class="nx">newrec</span><span class="p">.</span><span class="nx">_csid</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="nx">ztx</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">begin_tx</span><span class="p">(</span><span class="nx">csid</span><span class="p">,</span> <span class="nx">request</span><span class="p">.</span><span class="nx">headers</span><span class="p">.</span><span class="nx">From</span><span class="p">);</span>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="nx">newrec</span><span class="p">.</span><span class="nx">recid</span><span class="p">)</span>
</span><span class='line'>    <span class="nx">rec</span> <span class="o">=</span> <span class="nx">ztx</span><span class="p">.</span><span class="nx">open_record</span><span class="p">(</span><span class="s1">&#39;page&#39;</span><span class="p">,</span> <span class="nx">newrec</span><span class="p">.</span><span class="nx">recid</span><span class="p">);</span>
</span><span class='line'><span class="k">else</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="nx">rec</span> <span class="o">=</span> <span class="nx">ztx</span><span class="p">.</span><span class="nx">new_record</span><span class="p">(</span><span class="s1">&#39;page&#39;</span><span class="p">);</span>
</span><span class='line'>    <span class="nx">newrec</span><span class="p">.</span><span class="nx">recid</span> <span class="o">=</span> <span class="nx">rec</span><span class="p">.</span><span class="nx">recid</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">rec</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="nx">newrec</span><span class="p">.</span><span class="nx">title</span><span class="p">;</span>
</span><span class='line'><span class="nx">rec</span><span class="p">.</span><span class="nx">text</span> <span class="o">=</span> <span class="nx">newrec</span><span class="p">.</span><span class="nx">text</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="nx">vv</span><span class="p">.</span><span class="nx">txcommit</span><span class="p">(</span><span class="nx">ztx</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<ol>
<li>Get the changeset ID (if any) that this update is based on (more on
this in a moment)</li>
<li>Start a new transaction</li>
<li>Do we already have a record ID? If so, this is an update. Open that
record.</li>
<li>If not, this is a new page. Create a record.</li>
<li>Set the record&#8217;s title and text to those passed in from the form.</li>
<li>Commit.</li>
</ol>


<p>We then return <code>OK</code> to our caller, the page is reloaded, the circle of
life continues.</p>

<h3>Merges</h3>

<p>So what&#8217;s up with the changeset ID, and why did we have to say
&#8220;normally&#8221; before?</p>

<p>It&#8217;s possible that, by the time you&#8217;re saving your
changes, someone else has updated the same page. Or maybe your changes
are in a nice straight line locally, but a push or pull brings in
someone else&#8217;s previously-unknown edits.  Veracity doesn&#8217;t get to
throw up its hands and fail.  It needs to merge.</p>

<p>And to merge your changes and mine, it needs to know where we each
started from.  That&#8217;s why we pass the changeset IDs around; it tells
Veracity &#8220;here&#8217;s my latest changes, and the <em>last</em> version I knew of
was rev 1234&#8221;.  Later, when Veracity merges that with someone else&#8217;s
updates, it knows <em>those</em> were based on rev 1235; it finds a common
ancestor, does a smart 3-way merge, and all&#8217;s well. Almost always.</p>

<p>&#8220;Almost always&#8221; is not &#8220;absolutely always&#8221;, though.</p>

<p>What if we both started with:</p>

<pre><code>line 1
line 2
</code></pre>

<p>as our text.  Then I edited it to read:</p>

<pre><code>line 1
line one and a half
line 2
</code></pre>

<p>while off on your machine, you edited it to:</p>

<pre><code>line 1
line 1.5
line 2
</code></pre>

<p>Then you pull my changes.  Now what?  Should your changes be thrown
away?  Should mine?  Should both lines be included?  Any of these are
possible, but in the template we have to pick one.</p>

<p>The &#8220;merge strategy&#8221; the Wiki template uses is to concatenate our two
texts, and let a human being sort things out.  Elsewhere (e.g. in the
scrum module) we use all sorts of other strategies, including
automatically changing the ID of a work item when it conflicts with
one created elsewhere).  Since Wiki text is intended for human usage
only, and is completely arbitrary, there&#8217;s no sense trying to guess
the &#8220;appropriate&#8221; conflict resolution between two edits.</p>

<p>So in this situation, anyone opening the merged page will see:</p>

<p class="shot"><img alt="Wiki page needing manual merge" src="http://blog.roub.net/i/mergeshot2.png" width="579" height="305" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>


<p>Edit that as needed, and all&#8217;s well with the world again.</p>

<p>Next (and hopefully final) time: plugging into Veracity&#8217;s activity stream
and cache.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building a Veracity Module - Part 2]]></title>
    <link href="http://blog.roub.net/2011/08/building-a-veracity-module-part-2.html"/>
    <updated>2011-08-15T12:04:20-04:00</updated>
    <id>http://blog.roub.net/2011/08/building-a-veracity-module-part-2</id>
    <content type="html"><![CDATA[<p><a href="http://blog.roub.net/2011/08/building_a_veracity_module_part_1.html" title="Building a Veracity Module - Part 1">Last time</a>, we installed a Wiki module for <a href="http://veracity-scm.com/" title="Veracity - a new open source Distributed Version Control System">Veracity</a>.  This time, we&#8217;ll look at the pieces that make a module work.</p>

<p>Veracity modules can add to several different parts of the Veracity infrastructure; not every module will touch all of these.  They are:</p>

<ol>
<li>New templated data types (in our case, Wiki pages), and the database <acronym title="Directed Acyclic Graphs">DAGs</a> to hold them.</li>
<li>Server-side Javascript code, run in response to either:

<ol>
<li>Web server requests (creating, updating, viewing Wiki pages), or</li>
<li>Version control hooks (not used in this example)</li>
</ol>
</li>
<li>New Veracity web pages.</li>
<li>Client-side Javascript, run as part of Veracity&#8217;s Web UI.</li>
</ol>


<p>The client-side Javascript is found under <code>server_files/ui/modules/wiki</code>; the rest lives under <code>server_files/modules/wiki</code>.  A portion of <code>init.js</code> in that folder bears closer inspection:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">area</span><span class="o">:</span> <span class="s2">&quot;wiki&quot;</span><span class="p">,</span>
</span><span class='line'><span class="nx">vendor</span><span class="o">:</span> <span class="nx">sg</span><span class="p">.</span><span class="nx">vendor</span><span class="p">.</span><span class="nx">SOURCEGEAR</span><span class="p">,</span>
</span><span class='line'><span class="nx">grouping</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span>
</span><span class='line'><span class="nx">dagnums</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="s2">&quot;WIKI&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">dagid</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">template</span><span class="o">:</span> <span class="s1">&#39;sg_ztemplate__wiki.json&#39;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">},</span>
</span></code></pre></td></tr></table></div></figure>


<p>We&#8217;re creating a new database &#8220;area&#8221; - a group of DAGs that are related to one another. Veracity ships with areas like <code>version_control</code>, and the scrum module defines (not surprisingly) <code>scrum</code>.  For modules, the area name should match the containing folder name.</p>

<p>Each module definition needs a vendor ID.  Right now, that&#8217;s just us.  <code>sg.vendor.SOURCEGEAR == 1</code>.  If you&#8217;re adding your own areas, <a href="mailto:paul.roub@sourcegear.com" title="Paul Roub at SourceGear">get in touch</a>, and I&#8217;ll make sure you have a vendor ID that doesn&#8217;t conflict with anyone else&#8217;s.</p>

<p>The <code>grouping</code> property is the number of <em>this</em> area within the vendor&#8217;s space.  1-4 were already used (including Scrum), so I added <code>1</code>. Clever me.</p>

<p>Similarly, <code>dagid</code> is the number of each DAG within this area.  If you looked at the scrum module, you&#8217;d see the <code>WORK_ITEMS</code> dag has <code>dagid == 1</code>, and the <code>BUILDS</code> dag has <code>dagid == 2</code>.</p>

<p>Every DAG in Veracity needs a template - a description of its record types, their fields, and the merging rules involved for each.  All rectypes <em>must</em> be fully, automatically mergeable - failure is, literally, not an option.  This allows distributed databases and their owners to remain sane.   Merge strategies include &#8220;last first&#8221;, &#8220;greatest&#8221;, &#8220;uniqify&#8221;, etc.  We&#8217;ll look at the Wiki&#8217;s choice next time.</p>

<p>In our init file, we specify the JSON file describing each database template.  The templates for core Veracity DAGs can be found in <code>@/src/libraries/templates</code>.</p>

<p>At some magical hand-wavy time that you needn&#8217;t worry about, Veracity will look at this init file and:</p>

<ol>
<li>Make sure the repo we&#8217;re playing with has this area installed</li>
<li>Make sure that area has the DAGs we need</li>
<li>Make sure that the DAGs have the right templates set</li>
</ol>


<p>Once this has happened, the DAGs are available for Javascript use just like the built-in types.  If you run the <code>vscript</code> interpreter on a wiki-enabled repo, you can see this:</p>

<pre><code>vscript&gt; sg.to_json__pretty_print( repo.list_areas() )
{
    "core" : 257,
    "version_control" : 258,
    "scrum" : 259,
    "wiki" : 261
}

vscript&gt; sg.to_json__pretty_print(sg.dagnum)
{
    "VERSION_CONTROL" : "0000000010201001",
    "USERS" : "0000000010102062",
    "AREAS" : "0000000010101042",
    "VC_COMMENTS" : "00000000102021c2",
    "VC_TAGS" : "00000000102040c2",
    "VC_BRANCHES" : "0000000010205142",
    "VC_STAMPS" : "00000000102031c2",
    "VC_HOOKS" : "00000000102071c2",
    "TESTING_DB" : "0000000010401002",
    "TESTING2_DB" : "0000000010402002",
    "WORK_ITEMS" : "0000000010301002",
    "BUILDS" : "0000000010302002",
    "WIKI" : "0000000010501002"
}

vscript&gt; db = new zingdb(repo, sg.dagnum.WIKI)
[object zingdb]
vscript&gt; sg.to_json__pretty_print( db.get_template() )
{
    "version" : 1,
    "rectypes" : 
    {
        "page" : 
        {
            "merge" : 
            {
                "merge_type" : "field",
            // ...
            }
        // ...
        }
    }
    // ...
}
</code></pre>

<p>And after creating a page or two:</p>

<pre><code>vscript&gt; db = new zingdb(repo, sg.dagnum.WIKI)
vscript&gt; records = db.query('page', ['*'])
[object Object],[object Object]
vscript&gt; sg.to_json__pretty_print(records[1])
{
    "recid" : "ge9dbadde62004708abd960d58a99753f191e0f24c42b11e0a1a0c8bcc8e13b9a",
    "text" : "This is, in fact, another page entirely.  \n\n[[Yet Another Page]]",
    "title" : "Another Page"
}
</code></pre>

<p>Veracity will also install any URIs added by the module&#8217;s server-side Javascript. Look at <code>server_files/modules/wiki/wiki.js</code> to see how those are specified:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="s2">&quot;/repos/&lt;repoName&gt;/wiki/page/&lt;pagename&gt;.json&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="s2">&quot;GET&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">onDispatch</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">request</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>           <span class="c1">// this handles the request for a Wiki page&#39;s current contents</span>
</span><span class='line'>           <span class="c1">// ...</span>
</span><span class='line'>       <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">},</span>
</span><span class='line'>
</span><span class='line'><span class="s2">&quot;/repos/&lt;repoName&gt;/wiki/page&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="s2">&quot;POST&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">onJsonReceived</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">newrec</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>           <span class="c1">// here we receive JSON (in newrec) describing a page to be updated or created</span>
</span><span class='line'>           <span class="c1">// ...</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>     <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>These are used in Ajax calls from <code>server_files/ui/modules/wiki/wiki.js</code>.  For example, when rendering links to other wiki pages, we validate those links by attempting to retrieve them:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="kd">var</span> <span class="nx">purl</span> <span class="o">=</span> <span class="nx">sgCurrentRepoUrl</span> <span class="o">+</span> <span class="s2">&quot;/wiki/page/&quot;</span> <span class="o">+</span> <span class="nx">pageName</span> <span class="o">+</span> <span class="s2">&quot;.json&quot;</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="nx">vCore</span><span class="p">.</span><span class="nx">ajax</span><span class="p">(</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="nx">url</span><span class="o">:</span> <span class="nx">purl</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">dataType</span><span class="o">:</span> <span class="s1">&#39;json&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">reportErrors</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">success</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">vvWiki</span><span class="p">.</span><span class="nx">setGoodPage</span><span class="p">(</span><span class="nx">ln</span><span class="p">,</span> <span class="nx">pageName</span><span class="p">);</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="nx">error</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">vvWiki</span><span class="p">.</span><span class="nx">setBadPage</span><span class="p">(</span><span class="nx">ln</span><span class="p">,</span> <span class="nx">pageName</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>


<p>Next up: we follow the code through the creation, update, and merge of a Wiki page.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building a Veracity Module - Part 1]]></title>
    <link href="http://blog.roub.net/2011/08/building-a-veracity-module-part-1.html"/>
    <updated>2011-08-12T09:37:10-04:00</updated>
    <id>http://blog.roub.net/2011/08/building-a-veracity-module-part-1</id>
    <content type="html"><![CDATA[<p><a href="http://veracity-scm.com/" title="Veracity - a new open source Distributed Version Control System">Veracity</a> was built to be extensible.  Not just by virtue of the source being <a href="http://www.veracity-scm.com/downloads.html" title="Veracity Downloads">available</a> (although that helps), but also via the embedded JavaScript interpreter.  Without writing a line of C, new HTML pages and REST urls can be added, supporting entirely new data types and functionality.</p>

<p>The &#8220;native&#8221; Scrum functions in Veracity (work items, build tracking, time tracking, milestones, filters, etc.) are nowhere to be found in the Veracity library code.  It&#8217;s Javascript all the way down, helped out by client-side scripts and HTML.  As we add new (or alternative) features ourselves, expect to see many of them show up as new &#8220;modules&#8221; of pluggable (and un-pluggable) code.</p>

<p>We were going to be adding a Wiki module to Veracity eventually, anyway; and since we wanted to show you how modules are built, a simple Wiki seemed like a nice place to start.  This isn&#8217;t production code (yet), but it&#8217;s an instructive start.</p>

<p>For today, let&#8217;s just get the Wiki installed in your Veracity server and make sure everything&#8217;s up and running.</p>

<p>Whether you&#8217;re building from source or running from a pre-built installer, you&#8217;ll need to download the latest <a href="http://download.sourcegear.com/Veracity/nightly/">nightly build</a> (1.0.1.10527 or later) to retrieve the Wiki module.  If you&#8217;ve cloned from our <a href="http://public.veracity-scm.com/repos/veracity">public repository</a>, you&#8217;ll want to pull the latest.</p>

<p>Within your source folder or the unpacked archive, look for <code>.../src/modules/wiki</code>.  Within that directory, you&#8217;ll see a README file, some license material, a test directory, and the part you actually care about: a <code>server_files</code> folder.</p>

<p>You&#8217;ll want to copy the <em>contents</em> of that folder into Veracity&#8217;s server files folder.  If you&#8217;re unsure where that is, run <code>vv config</code>. Towards the end, you&#8217;ll see something like:</p>

<pre><code>server/files: /home/alanswann/veracity/src/server_files
</code></pre>

<p>in this case, from within <code>modules/wiki</code>, you&#8217;d say (on Unix-y systems):</p>

<pre><code>[~/veracity/src/modules/wiki]
$ cp -R server_files/* /home/alanswann/veracity/src/server_files
</code></pre>

<p>and your Wiki code should now included with the rest of Veracity.</p>

<pre><code>[~/veracity/src/modules/wiki]
$ ls ~/veracity/src/server_files/modules
scrum  wiki
</code></pre>

<p><em>Note the above-mentioned scrum module alongside the wiki.</em></p>

<p>Start (or restart) <code>vv serve</code>, and you should see a <code>wiki</code> item in the top menu.</p>

<p class="shot"><img alt="Veracity menu with wiki" src="http://blog.roub.net/i/menushot.png" width="598" height="103" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>


<p>Click that. You&#8217;ll see a default home page, explaining that, hey, it&#8217;s a default home page.  You can edit this page (using <a href="http://daringfireball.net/projects/markdown/">Markdown</a>), or click the <code>new page</code> link to add another page.</p>

<p>As pages are created, they&#8217;ll show up in the right sidebar, to be clicked and viewed, or inserted into another page&#8217;s edit box as links.</p>

<p class="shot"><img alt="Editing a Wiki entry" src="http://blog.roub.net/i/editshot.png" width="530" height="329" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></p>


<p>Next post, we&#8217;ll start looking at the code.  The good news?  All of the heavy lifting is done for us, from the flexibly-licensed editor and preview tools, to the Veracity libraries that handle saves, updates and merges (gotta have merges, it&#8217;s a <em>distributed</em> Wiki, after all).  I just glued them together.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[A little merging Jiu-Jitsu]]></title>
    <link href="http://blog.roub.net/2011/07/a-little-merging-jiu-jitsu.html"/>
    <updated>2011-07-11T16:02:03-04:00</updated>
    <id>http://blog.roub.net/2011/07/a-little-merging-jiu-jitsu</id>
    <content type="html"><![CDATA[<p>This may well be obvious to anyone bothering to read this blog, but it&#8217;s helpful enough for me that I thought I&#8217;d scribble it down, anyway.</p>

<p>You&#8217;ve made a minor change, just adding some validation code:</p>

<pre><code>$ vv status
Modified:  @/validate.c

$ vv diff
=== ================
===   Modified: File @/validate.c
--- @/validate.c    4b8c0a0278cc18fdeb8592a6b56b81ba4c4b6841
+++ @/validate.c    2011/07/11 19:41:01.000 +0000
@@ -4,4 +4,5 @@
 void validate(const char *user)
 {
         printf("%s, you're awesome.\n", user);
+        printf("And attractive.\n");
 }
</code></pre>

<p>You commit, then pull the latest from your team before pushing your changes up.</p>

<p>There are inded some changes, so you&#8217;ll need to merge:</p>

<pre><code>$ vv heads

    revision:  7:11e7676e2d8c96071d6ae6748afb29fbea291d3c
      branch:  master
         who:  otherguy@example.com
        when:  2011/07/11 15:44:24.448 -0400
     comment:  more changes you don't care about
      parent:  5:54675c9beeab003fce135282654cf36f9032f326

    revision:  6:3926e0614eb2164bb0839eb1c6ba2c4954107dcf
      branch:  master
         who:  me@example.com
        when:  2011/07/11 15:42:08.610 -0400
     comment:  additional validation
      parent:  4:55205503daa35db9fd3699473da84f49493ef03c

$ vv merge
4 updated, 0 deleted, 3 added, 0 merged, 0 unresolved

$ vv status
   Added:  @/othercode.c
   Added:  @/othercode.h
   Added:  @/whatisthisidonteven.c
Modified:  @/addsprint.js
Modified:  @/connect.js
Modified:  @/reqtest.js
Modified:  @/validate.h
</code></pre>

<p>Hrm. A lot of code, none of it yours. The resulting diff is so long I won&#8217;t bother to fake it up for the blog.  But are you <em>sure</em> none of it&#8217;s yours?  Skimming through hundreds of lines of diff output looking for problem code is not fun.  And all for your little one-liner.</p>

<p>So turn the merge on its head. Starting from the other side, you should easily be able to tell if your changes are being merged in a sane fashion.</p>

<pre><code># clean slate
$ vv revert --all

# start from the other guy's changes
$ vv update -r 7

# merge in yours
$ vv merge
1 updated, 0 deleted, 0 added, 0 merged, 0 unresolved

$ vv status
Modified:  @/validate.c

$ vv diff
=== ================
===   Modified: File @/validate.c
--- @/validate.c        4b8c0a0278cc18fdeb8592a6b56b81ba4c4b6841
+++ @/validate.c        2011/07/11 19:46:21.000 +0000
@@ -4,4 +4,5 @@
 void validate(const char *user)
 {
        printf("%s, you're awesome.\n", user);
+       printf("And attractive.\n");
 }

# looks familiar. we're good.
$ vv commit -m"merge"
</code></pre>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Distributed Bug Tracking Avoids Out-of-Sync Bugs and Code]]></title>
    <link href="http://blog.roub.net/2011/07/distributed-bug-tracking-dvcs-.html"/>
    <updated>2011-07-01T16:27:06-04:00</updated>
    <id>http://blog.roub.net/2011/07/distributed-bug-tracking-dvcs-</id>
    <content type="html"><![CDATA[<p>Chipping away at my <a href="http://sourcegear.com/veracity/">Veracity</a> tasks today, I ran across an issue that&#8217;s common in DVCS + Centralized Bug Tracking scenarios, but unnecessary and easily-avoided in Veracity.</p>

<p>In a nutshell, it&#8217;s this:</p>

<p class="shot"><img alt="Centralized Bug Tracking + DVCS Workflow" src="http://blog.roub.net/i/centralized.png" /></p>


<ol>
<li>I commit some changes, locally, to fix a bug.</li>
<li>I mark the bug fixed, on our one and only bug tracker instance.</li>
<li>I don&#8217;t push my code just yet.</li>
</ol>


<p>Until that code is pushed to the shared server, QA will have my bug in their to-verify list, but won&#8217;t have the code to do so.</p>

<p>I might delay the push for a number of reasons - waiting to finish a couple of related bugs and push as a unit; needing to merge and test someone else&#8217;s latest code before pushing the results back; etc.  Until I&#8217;m done, though, we&#8217;re out of sync.</p>

<p>You can keep track via special statuses, tags, stamps, etc. (and remember to reset them once you&#8217;ve pushed), but why?</p>

<p>When I&#8217;m working on Veracity, I&#8217;m usually working against a local instance of the bug tracking / web UI.  It&#8217;s faster, I&#8217;m not competing for resources, and I get to do things like associate commits to bugs right from the command line while I work.</p>

<p>It also avoids the problem above, since my workflow looks like this:</p>

<p class="shot"><img alt="Distributed Bug Tracking + DVCS Workflow" src="http://blog.roub.net/i/distributed.png" /></p>


<p>This is normal in the Veracity world, and unremarkable except in those instances, like this afternoon, when <em>someone</em> marks a bug closed without pushing up the associated code. Not naming names. We all make mistakes, and I&#8217;m sure <a href="http://www.ericsink.com/entries/book2_reviewers_needed.html">writing a book</a> can be pretty distracting.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[About]]></title>
    <link href="http://blog.roub.net/2011/06/index.html"/>
    <updated>2011-06-23T15:13:05-04:00</updated>
    <id>http://blog.roub.net/2011/06/index</id>
    <content type="html"><![CDATA[<p>I&#8217;m a developer at <a href="http://sourcegear.com/">SourceGear</a>, working on <a href="http://sourcegear.com/veracity" title="Veracity - Distributed Version Control and Database">Veracity</a> these days.</p>

<p>Elsewhere in my life, there&#8217;s mainly music - you can find out more about that at <a href="http://paulroub.com/">paulroub.com</a>.</p>

<p>My complaining-about-air-travel needs are handled on Twitter as <a href="http://twitter.com/paulroub">@paulroub</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Keeping Up With Your Team via Veracity's Activity Stream]]></title>
    <link href="http://blog.roub.net/2011/06/keeping-up-with-your-team-via-.html"/>
    <updated>2011-06-22T20:23:41-04:00</updated>
    <id>http://blog.roub.net/2011/06/keeping-up-with-your-team-via-</id>
    <content type="html"><![CDATA[<p>So far, most of the discussions about <a href="http://sourcegear.com/veracity">Veracity</a> have been about its <acronym title="Distributed Version Control System">DVCS</acronym> abilities.  I thought I&#8217;d talk a bit about the other side of Veracity, the bug/work tracking side, from the users&#8217; point of view.  I&#8217;ll start with the Activity Stream.</p>

<p>One choice we made for the initial releases was to forego email notifications as a means of alerting users to new work, updated bug reports, code changes, and so on.  There were a number of reasons for this:</p>

<ul>
<li>Since the work item activity is distributed, bugs may be added and updated from multiple machines, sometimes behind-the-scenes, sometimes offline (I&#8217;m sitting in an airport right now, waiting to act out the canonical &#8220;dev on a plane&#8221; DVCS scenario). When should email be sent? By whom?</li>
<li>Cross-platform email support would take time away from features we really <em>want</em> in the first release.</li>
<li>We all get enough email as it is.</li>
<li>There are less-irritating ways to keep people in the loop.</li>
</ul>


<p>Of course, Veracity has really nice <strong>search</strong> and <strong>filter</strong> tools, allowing you to see bugs assigned to you; bugs you need to verify; what&#8217;s up for you in the next sprint; any bug mentioning &#8220;snickers&#8221; or stamped with &#8220;SOOPER URGENT&#8221;; etc.</p>

<p>But we also wanted to give you a view of what&#8217;s going on with your project in a more ambient way, so we added the <strong>Activity Stream</strong>.  Every 30 seconds, if anything new of interest has happened in your repository, it quietly updates to let you know.</p>

<p class="shot"><img alt="Veracity menu - Show Activity Stream" src="http://blog.roub.net/i/show%20activity%20stream.png" width="153" height="57" /></p>


<h2>What&#8217;s In There?</h2>

<p>The activity stream shows you the most-recent 25 things you might care about. In particular, you&#8217;ll see:</p>

<ul>
<li>Newly-created work items</li>
<li>Modified work items (status changes, comments, relations, etc.)</li>
<li>Version control commits</li>
<li>Build failures (if your build system is using our reporting scripts)</li>
<li>And perhaps more, by the time you read this</li>
</ul>


<p>Here&#8217;s a shot of the last few items in dev@example.com&#8217;s activity stream:</p>

<p class="shot"><img alt="Veracity's Activity Stream" src="http://blog.roub.net/i/all%20activity.png" width="211" height="424" /></p>


<h2>What&#8217;s Not?</h2>

<p>The goal of the activity stream is to be <em>useful</em>, as opposed to complete.  If you look at the activity stream side-by-side with our version control history graph, you&#8217;ll notice that rev #7 isn&#8217;t listed, even though it&#8217;s the latest.  Why not?  Its comment was simply &#8220;merge&#8221;.  That&#8217;s not terribly interesting, you probably don&#8217;t care, so we hide it.  We also hide no-op work item updates, successful builds (our continuous integration setup spins those out constantly, and they&#8217;d push the more-urgent stuff off screen), etc.</p>

<p class="shot"><img alt="Activity stream hiding a merge" src="http://blog.roub.net/i/side%20by%20side.png" width="550" height="135" /></p>


<h2>The &#8220;my updates only&#8221; option</h2>

<p>You can also narrow down the work items shown in the stream to just those that you&#8217;re involved with.  By checking &#8220;my updates only&#8221;:</p>

<p class="shot"><img alt="Veracity menu - My Updates Only" src="http://blog.roub.net/i/my%20updates%20only%20menu.png" width="281" height="77" /></p>


<p>you&#8217;ll only see work items where your username is either the Reporter, Assignee, or Verifier, or where the item has been stamped with your username.</p>

<p class="shot"><img alt="Veracity activity - my updates only" src="http://blog.roub.net/i/my%20activity.png" width="204" height="409" /></p>


<p>You&#8217;ll still see all commits, since other people&#8217;s commits are certainly likely to be of interest to you.  You&#8217;ll also still see your own commits, which are not likely to surprise you, so that you can drag them around.</p>

<h2>Dragging From the Activity Stream</h2>

<p>Veracity work items can be related to each other (this depends on that, this is a duplicate of the other, etc.).  They can also be associated with version control commits &#8211; check in some changes, mark the relevant bug.</p>

<p>All that is nicely searchable in the work item edit page, but when you want to associate with another recent bug, or with a recent commit, why type anything?  You&#8217;re already looking at that item in the stream, so just drag it into the appropriate slot and Save.</p>

<p class="shot"><img alt="Dragging a work item from the activity stream to relate it to the item being edited" src="http://blog.roub.net/i/dragging_cur.png" width="544" height="364" /></p>


<h2>Your Stream in Your Feed Reader</h2>

<p>For several of us on the Veracity team, the first we hear of a newly-assigned or reopened bug is when it shows up in our feed reader of choice.  The Veracity activity streams for any repository (either the all-inclusive or my-updates-only version) are also served as Atom feeds. The link for whichever version of the stream you&#8217;re viewing is always available via the feed icon at the bottom of the stream:</p>

<p class="shot"><img alt="Veracity activity feed icon" src="http://blog.roub.net/i/feed%20link.png" width="152" height="58" /></p>


<p>Here&#8217;s this dev&#8217;s full-blown stream:</p>

<p class="shot"><img alt="All Veracity activity in a feed reader" src="http://blog.roub.net/i/all%20feed.png" width="357" height="162" /></p>


<p>and his mine-mine-mine stream:</p>

<p class="shot"><img alt="&quot;My updates only&quot; in a feed reader" src="http://blog.roub.net/i/my%20feed.png" width="338" height="166" /></p>


<p>Note that the titles are live links to the relevant Veracity work item, changeset detail, etc. pages.</p>

<h2>Keep In Touch</h2>

<p>As ever, feedback, questions, and suggestions are very much welcome on the
<a href="http://sourcegear.com/veracity/mailinglist.html">Veracity users mailing list</a>. Drop in.</p>

<p>We&#8217;ll also have a booth at <a href="http://oscon.com/" title="The O'Reilly Open Source Convention">OSCON</a> again this July, so stop by, say &#8220;hi&#8221;, kick the tires, argue about rebase&#8230;</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Adding Veracity Work Items via todo.txt]]></title>
    <link href="http://blog.roub.net/2011/05/adding-veracity-items-via-todotxt.html"/>
    <updated>2011-05-29T14:55:05-04:00</updated>
    <id>http://blog.roub.net/2011/05/adding-veracity-items-via-todotxt</id>
    <content type="html"><![CDATA[<p>I think, with this post, I&#8217;ll have reached a new peak in my quest to share scripts that almost no one else will use. What <em>is</em> the overlap between &#8220;users of the Todo.txt Touch Android app&#8221; and &#8220;pre-release Veracity users&#8221;? The odds of that being &#8220;just me&#8221; are favorable.  I expect those odds to change. In the meantime, I&#8217;ve already written the script; it would be rude not to share.</p>

<p><a href="http://sourcegear.com/veracity/">Veracity</a>, if you&#8217;ve been paying attention, is Sourcegear&#8217;s upcoming Distributed Version Control / Bug Tracking / more system with an open sourced core. The main reason this blog&#8217;s been so dead? &#8220;Busy. Busy coding. Can&#8217;t talk right now.&#8221; That kinda thing.</p>

<p><a href="http://todotxt.com/">todo.txt</a> is Gina Trapani&#8217;s script/app that aims to make simple todo.txt files more useful and mobile, while still maintaining the simplicity we love. I&#8217;m a fan and a daily user of the command-line script and the Android app.</p>

<p>One thing Veracity doesn&#8217;t have, as yet, is a mobile interface. Since all the boxes on which I deal with Veracity are behind firewalls, I couldn&#8217;t use one right now, anyway.  But that doesn&#8217;t stop me from realizing I need to log myself a task or two when I&#8217;m out and away from the keyboard. What to do?</p>

<p>One of the thing&#8217;s Gina&#8217;s Android app does is sync that todo.txt file via <a href="http://db.tt/fK4zN9k">Dropbox</a>. I add a todo item, and in a few seconds, it&#8217;s also in todo.txt on my laptop, where an instance of the Veracity team&#8217;s repository resides. Also, we have a nice little library of Javascript functions (on which much of Veracity&#8217;s web API is built), all callable from our vscript command-line JS tool. See where this is going?</p>

<p>I add a new item in the todo.txt app, like so:</p>

<p class="shot"><img alt="Adding items via the phone" src="http://blog.roub.net/i/adding.png" width="240" height="400" class="mt-image-none"  /></p>

<p>See how I set a &#8220;context&#8221; of @veracity? That will matter later. If I wished, I could also add tags (things prefixed by &#8220;+&#8221; in todo.txt&#8217;s world), and set a priority from (A) to (E).  Here&#8217;s a few of those, after the auto-sync:</p>

<p class="shot"><img alt="List of synced items in todo.txt" src="http://blog.roub.net/i/listitems.png" width="425" height="113" class="mt-image-none" /></p>

<p>I have a cron job that runs every 20 minutes, along the lines of:</p>

<pre>
0,20,40 * * * * vscript todo2vv.js /path/of/todo.txt paul.roub@sourcegear.com veracity-pr
</pre>

<p>Which tells the the todo2vv.js script (<a href="http://blog.roub.net/2011/05/29/assets/todo2vv.js">download</a>) to look in todo.txt for any @veracity items, mark them done in the todo.txt list, and add them instead to my Veracity todo list in the veracity-pr repository. Here they are after import:</p>

<p class="shot"><img alt=" " src="http://blog.roub.net/i/allitems.png" width="411" height="137" class="mt-image-none" /></p>

<p>And filtered by priority:</p>

<p class="shot"><img alt="Medium-priority bug only" src="http://blog.roub.net/i/Medium.png" width="328" height="130" class="mt-image-none"  /></p>

<p>All have been assigned to our current milestone-in-progress (Sprint 18, the Veracity team being a scrummy one), stamped &#8220;todo&#8221; (along with any of those todo.txt tags), and given priorities matching any seen in the text file.</p>

<p>The script is fairly simple; it spends more time parsing todo.txt and managing files than creating the actual work items.  Feedback, questions, comments are most welcome; at the moment, they&#8217;re probably best asked on the <a href="http://sourcegear.com/veracity/mailinglist.html">veracity-users mailing list</a>.</p>

<p>Back to work. Gotta change the activity stream sorting.</p>
 
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SourceGear PDC Materials]]></title>
    <link href="http://blog.roub.net/2009/11/sourcegear-pdc-materials.html"/>
    <updated>2009-11-23T10:18:51-05:00</updated>
    <id>http://blog.roub.net/2009/11/sourcegear-pdc-materials</id>
    <content type="html"><![CDATA[<p>Upside: we had a great time at PDC, got to talk to tons of users and potential users, and went through all of our T-Shirts and CDs very quickly.</p>

<p>Downside: if you dropped by the booth after Tuesday, you might be without a CD.</p>

<p>We&#8217;ve collected all of our PDC booth materials (not including the shirt, of course) in one place, at <a href="http://sourcegear.com/pdc/">sourcegear.com/pdc</a>. And yes, that includes the <a href="http://vssisdead.com/">VSS Must Die</a> desktop wallpaper.</p>

 
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[PDC Bound]]></title>
    <link href="http://blog.roub.net/2009/11/pdc-bound.html"/>
    <updated>2009-11-13T16:13:58-05:00</updated>
    <id>http://blog.roub.net/2009/11/pdc-bound</id>
    <content type="html"><![CDATA[<p>I&#8217;ll be part of the booth team at <a href="http://microsoftpdc.com/">Microsoft PDC &#8216;09</a> next week in Los Angeles &mdash; we&#8217;ll be showing off Vault 5, Fortress 2, SoS 5&#8230 and maybe giving away some new T-shirts.</p>

<p>So by all means, drop by booth 511, say &#8220;hi&#8221;, and tell Eric that <a href="http://www.ericsink.com/entries/microsoft_teamprise.html">Product Managers need SSDs, too</a>.</p>

 
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SourceOffSite 5.0.1 Released]]></title>
    <link href="http://blog.roub.net/2009/11/sourceoffsite-5-0-1.html"/>
    <updated>2009-11-10T08:58:52-05:00</updated>
    <id>http://blog.roub.net/2009/11/sourceoffsite-5-0-1</id>
    <content type="html"><![CDATA[<p>Hot on the heels of <a href="http://sourcegear.com/sos/">SourceOffSite</a> 5.0&#8217;s release, 
<a href="http://sourcegear.com/sos/releases/5.0.1.html" title="SourceOffSite 5.0.1 Release Notes"><abbr title="SourceOffSite">SoS</abbr> 5.0.1</a> is
out with some important bug fixes.  The SoS dev and QA team put a lot
of effort into getting a solid, well-tested release out in a very short
time span. Nice work.</p>

<p>Key changes in this release:</p>

<h3>Client</h3>

<ul>
  <li>Get latest on a folder now correctly retrieves missing files in the immediate folder.</li>

  <li>Project History maintains History options for &#8220;Include Files&#8221;, &#8220;Include Labels&#8221;, and &#8220;Labels Only&#8221; between client sessions.</li>

  <li>View/Edit tool has been corrected to save the settings in the User options.</li>

  <li>The IDE can be configured to not emit a disconnect message which may crash IDEs such as VB 6.</li>

  <li>The Auto connect option used from within the IDE client now connects upon opening a project.</li>

</ul>


<h3>Server</h3>


<ul>

  <li>The server corrected a problem where project history incorporating a
FROM or TO label to sets the boundaries for the History results.
  </li>
</ul>

<p>If you&#8217;re a SourceOffSite user and haven&#8217;t yet seen <a href="http://sourcegear.com/sos/" title="SourceGear SourceOffSite">SoS 5</a>&#8230; you really, 
<em>really</em> should. Looks, features, performance &mdash; this is a
<em>huge</em> upgrade.</p>

 
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Now available: Vault 5 beta 2, Fortress 2 beta 2]]></title>
    <link href="http://blog.roub.net/2009/07/now-available-vault-5-beta-2-f.html"/>
    <updated>2009-07-01T13:36:37-04:00</updated>
    <id>http://blog.roub.net/2009/07/now-available-vault-5-beta-2-f</id>
    <content type="html"><![CDATA[The second beta releases of SourceGear Vault 5 and Fortress 2 are now available: Vault 5b2 is <a href="http://sourcegear.com/vault/downloads.html">here</a>, Fortress 2b2 is <a href="http://sourcegear.com/fortress/downloads.html">here</a>.

This should be the last preview before the final release of Vault 5 and Fortress 2, so speak now or forever hold your peace (or at least, hold it until the <em>next</em> release).

Jeremy has the <a href="http://vaultblog.sourcegear.com/articles/2009/07/01/beta-2-is-out" title="Beta 2 is out!">details on beta 2</a>.  Previously, I&#8217;d talked about the <a href="http://blog.roub.net/2009/07/beta_2_is_coming_lets_take_a_l.html" title="Beta 2 is coming; let's take a look">updated web interface</a> and the new <a href="http://blog.roub.net/2009/04/itch_scratched_webdav_in_vault.html" title="Itch: Scratched. WebDav in Vault and Fortress.">WebDAV interface</a>;  Jeremy dropped hints about the <a href="http://vaultblog.sourcegear.com/articles/2009/01/19/give-us-your-old-vss-database-and-youll-get-a-year-of-free-vault-fortress-maintenance" title="Give us your old VSS database, and you'll get a year of free Vault/Fortress maintenance">new import tool</a> (now in place, and known as &#8220;Handoff&#8221;) and <a href="http://vaultblog.sourcegear.com/articles/2008/07/28/shelve">Shelve</a>.
 
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Beta 2 is coming; let's take a look]]></title>
    <link href="http://blog.roub.net/2009/07/beta-2-is-coming-lets-take-a-l.html"/>
    <updated>2009-07-01T10:24:38-04:00</updated>
    <id>http://blog.roub.net/2009/07/beta-2-is-coming-lets-take-a-l</id>
    <content type="html"><![CDATA[We should be releasing Beta 2 of Vault 5 and Fortress 2 later today (watch the <a href="http://vaultblog.sourcegear.com/">Development Blog</a> for details).  You&#8217;ll notice pretty quickly that the web interface has been revamped, especially on the work item tracking side; seemed worth a walkthrough of its own.  

<a href="http://sourcegear.com/images3/queryall.png" rel="lightbox" title="Fortress Query page" class="shotthumb"><img src="http://sourcegear.com/images3/queryall-thumb.png" /></a>

The Query page is not terribly different; but cleaner and easier to navigate.

Sometimes it&#8217;s as simple as letting you &#8220;roll up&#8221; any projects you&#8217;re not interested in at the moment, keeping your front page cleaner:

<a href="http://sourcegear.com/images3/rollup.png" rel="lightbox" title="Fortress selective Project display"  class="shotthumb"><img src="http://sourcegear.com/images3/rollup-thumb.png" /></a>


Once we start viewing query results, the changes really make themselves known.  As always, we aim toward making it easier to find the bug you&#8217;re after. Hovering over the magnifying glass icon next to any bug in the list gives you a quick summary of that bug, without needing to leave the list and load the whole thing:

<a href="http://sourcegear.com/images3/infohover.png" rel="lightbox" title="Fortress Bug preview"  class="shotthumb"><img src="http://sourcegear.com/images3/infohover-thumb.png" /></a>

We&#8217;ve removed the sidebar, giving more space over to the actual bug information; selection of Customized and Grouped views has moved to the list header.  Easy to find, but out of the way.

<a href="http://sourcegear.com/images3/viewmenu.png" rel="lightbox" title="Fortress View menu"  class="shotthumb"><img src="http://sourcegear.com/images3/viewmenu-thumb.png" /></a>

We&#8217;ve taking the friction out of adding and editing bugs, as well &mdash; getting a bug into Fortress takes just a couple of clicks from any bug list.  We&#8217;ll (optionally) use the current query as a template, so a new bug added from the &#8220;Milestone 1&#8221; list will default to &#8220;Milestone 1&#8221;, tags will be copied, etc.

<a href="http://sourcegear.com/images3/quickadd.png" rel="lightbox" title="Fortress Quick Add"  class="shotthumb"><img src="http://sourcegear.com/images3/quickadd-thumb.png" /></a>

Updating a bunch of issues at once is easier, too: want to reassign or close some of the items you&#8217;re looking at?  Check them off, and use the quick-edit tools at the end of the list to update them in-page.

<a href="http://sourcegear.com/images3/quickedit.png" rel="lightbox" title="Fortress Quick Edit"  class="shotthumb"><img src="http://sourcegear.com/images3/quickedit-thumb.png" /></a>

We&#8217;ve also eliminated the &#8220;printable view&#8221; menu item from all pages; bugs, bug lists, etc. will print properly without any extra steps.

<a href="http://sourcegear.com/images3/printable.png" rel="lightbox" title="Fortress Print View"  class="shotthumb"><img src="http://sourcegear.com/images3/printable-thumb.png" /></a>

Editing a bug&#8217;s details is easier than ever &mdash; when you&#8217;re viewing a bug, clicking on any of its fields lets you edit the field in place.

<a href="http://sourcegear.com/images3/clicktoedit.png" rel="lightbox" title="Fortress Click-to-Edit"  class="shotthumb"><img src="http://sourcegear.com/images3/clicktoedit-thumb.png" /></a>

The Source Control web interface has been revamped as well; the new repository tree is <em>much</em> faster, cleaner and easier to navigate.

<a href="http://sourcegear.com/images3/history.png" rel="lightbox" title="Fortress File History"  class="shotthumb"><img src="http://sourcegear.com/images3/history-thumb.png" /></a>

There&#8217;s a lot more to the beta, from Shelve improvements to the new full-fledged VSS Handoff feature.  I&#8217;ll post again later when it&#8217;s out; give it a shot, and please let us know what you think in the <a href="http://support.sourcegear.com/viewforum.php?f=44">Beta Support Forum</a>.



 
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SourceOffSite 5 Community Technology Preview (CTP)]]></title>
    <link href="http://blog.roub.net/2009/06/sourceoffsite-5-ctp.html"/>
    <updated>2009-06-18T14:33:46-04:00</updated>
    <id>http://blog.roub.net/2009/06/sourceoffsite-5-ctp</id>
    <content type="html"><![CDATA[<p>Remember that SourceOffSite CTP release <a href="http://blog.roub.net/2009/06/sourceoffsite_5_preview_releas.html">I mentioned</a>?  It&#8217;s here.  Jeff has <a href="http://vaultblog.sourcegear.com/articles/2009/06/17/sourceoffsite-ctp">the details</a> if you&#8217;d like to try it out.</p>

<p>Why are we doing another update of SoS?  Shouldn&#8217;t everybody just get away from SourceSafe and into Vault or Fortress?  In a perfect world, sure.</p>

<p>But we know a lot of you are still living with SourceSafe, now and in the forseeable future, for all the usual reasons &#8211; time, budget, inertia, IT priorities that outrank your SCC provider.  And we feel for you, and want to continue helping via SoS.</p>

<p>And now, we can help even <em>more</em>.</p>

<p>A folder-spanning &#8220;pending changes&#8221; window, so you don&#8217;t forget to check in one of the 50 file in your latest web update?  Got that.</p>

<p>Multiple tabs, allowing you to keep tabs on your searches, your history, and everything else in your multitasking day?  That too.</p>

<p>Performance? Yep. Stability? Sure. Modern UI? Gotcha.</p>

<p><a href="http://vaultblog.sourcegear.com/articles/2009/06/17/sourceoffsite-ctp">Give it a shot</a> (Windows only right now, BTW) and <a href="http://support.sourcegear.com/viewforum.php?f=45">let us know</a> what you think.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SourceOffSite 5 Preview release is imminent]]></title>
    <link href="http://blog.roub.net/2009/06/sourceoffsite-5-preview-releas.html"/>
    <updated>2009-06-04T08:21:02-04:00</updated>
    <id>http://blog.roub.net/2009/06/sourceoffsite-5-preview-releas</id>
    <content type="html"><![CDATA[<p>As Jeff notes at the <a href="http://vaultblog.sourcegear.com/">SourceGear Development Blog</a>, a <a href="http://vaultblog.sourcegear.com/articles/2009/06/04/sourceoffsite-5-customer-preview-release">preview release of SOS 5</a> is coming your way very soon.</p>

<p>Watch the Development Blog for details.  Jeff and the team are justifiably proud of this release.
</p>
 
]]></content>
  </entry>
  
</feed>
