
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>The Exciter</title>
 <link href="http://theexciter.com/atom.xml" rel="self"/>
 <link href="http://theexciter.com/"/>
 <updated>2008-12-29T23:03:23+01:00</updated>
 <id>http://theexciter.com/</id>
 <author>
   <name>Johan S??rensen</name>
   <email>johan@johansorensen.com</email>
 </author>

  
   <entry>
     <title>Touches and UIScrollView inside a UITableView</title>
     <link href="/articles/touches-and-uiscrollview-inside-a-uitableview.html"/>
     <updated>2008-12-28T18:40:07+01:00</updated>
     <id>tag:theexciter.com,2008-12-28:1230486007</id>
     <content type="html">
       &lt;p&gt;&lt;a href=&quot;/articles/trafikanten-for-the-iphone.html&quot;&gt;&amp;quot;Trafikanten&amp;quot; for the iPhone&lt;/a&gt; is the iPhone incarnation of the betabrite-style signs hanging around Oslo, providing travellers with real-time departure information on busses, trams and subways. So incorporating some of that feeling into the application, while still maintaining that iPhone look n&amp;#8217; feel was a crucial UI design issue for us.&lt;/p&gt;
&lt;p style=&quot;float:right;&quot;&gt;&lt;img src=&quot;http://kunder.shortcut.no/shortcutting/trafikanten-departure.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A betabrite sign is basically a set of LED lamps that turn on and off in sequences, usually to portray text scrolling across the screen. Bringing this metaphor over to the iPhone means that the user should be able to scroll said text across the screen with his fingers. When this text lives in a subview of UITableView (which in turn is a UIScrollView subclass), there be dragons ahead!&lt;/p&gt;
&lt;p&gt;The thing is, a UITableView takes completely control of the responder chain (and therefore touches) so that it can try and figure out if the user intents to scroll the scrollview, as described in the documentation overview for the UIScrollView class. So in order to be able to scroll part of a single cell sideways, while still being able to scroll the entire tableview up and down, we have to do some careful juggling with events and the responder chain.&lt;/p&gt;
&lt;p&gt;Please observe this artist rendition of the view hierarchy:&lt;br /&gt;
&lt;notextile&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ______________________
|  UITableView         |
|  __________________  |
| | UITableViewCell  | |
| |  ______________  | |
| | | UIScrollView | | |
| | |______________| | |
| |__________________| |
|  __________________  |
| | UITableViewCell  | |
| |  ______________  | |
| | | UIScrollView | | |
| | |______________| | |
| |__________________| |
|______________________|
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/notextile&gt;The innermost UIScrollView holds the departure times and it&amp;#8217;s the view we want to be able to move horizontally. It&amp;#8217;s worth pointing out that generally, for UITableViewCells, you&amp;#8217;d want to flatten the view hierarchy as much as posible (e.g. less subviews) to achieve smooth scrolling performance. But in this case we knew that there would always be less than two dozens or so of UITableViewCells displayed in the tableview.&lt;/p&gt;
&lt;p&gt;I created a UIScrollView subclass for the innermost one, that overrides all the &lt;code&gt;touchesMoved:withEvent:&lt;/code&gt; and friends delegates, and then in each one try to figure out which direction an ongoing touch was heading. If the touch went up or down we&amp;#8217;d call super, otherwise &lt;em&gt;pass it along the responder chain&lt;/em&gt; (up to the tableview), so that it can do its thing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
  if ([self isTouchGoingLeftOrRight:[touches anyObject]]) {
    [super touchesMoved:touches withEvent:event];
  } else {
    [self.nextResponder touchesMoved:touches withEvent:event];
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;isTouchGoingLeftOrRight:&lt;/code&gt; tries to figure out the general direction of the gesture, by comparing the current location with the previous location, and sets an instance variable that things like &lt;code&gt;touchesEnded:withEvent:&lt;/code&gt; can react to. Make sure to leave some &amp;#8220;wiggle room&amp;#8221; in the direction detection, since the user seldom moves completely linearly.&lt;/p&gt;
&lt;p&gt;So that takes care of that right? We can go up and down, otherwise we pass it along to the next responder. Turns out there&amp;#8217;s one more thing you&amp;#8217;d need to pull out of the sleeve; hit detection.&lt;/p&gt;
&lt;p&gt;We have to override &lt;code&gt;hitTest:withEvent:&lt;/code&gt; on the actual UITableView itself. Remember that it takes control of the responder chain, so the subviews won&amp;#8217;t get a chance to handle it first, unless we explicitly override that behavior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
  if (self.decelerating) {
    // don't try anything when the tableview is moving..
    return [super hitTest:point withEvent:event];
  }
  
  // Find the cell
  NSIndexPath *indexPathAtHitPoint = [self indexPathForRowAtPoint:point];
  id cell = [self cellForRowAtIndexPath:indexPathAtHitPoint];
  // if the cell has a scrollView property, it's the one we want
  if (cell != nil &amp;&amp; [cell respondsToSelector:@selector(scrollView)]) {
    [[cell scrollView] setScrollEnabled:YES];
    // Return the innermost scrollview
    return (UIView *)[cell scrollView];
  }
  
  return [super hitTest:point withEvent:event];
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;hitTest:withEvent:&lt;/code&gt; is responsible for telling the system which view that was &lt;em&gt;hit&lt;/em&gt;, by default UITableView assumes itself (or one of its cells), so we have to figure out if the user touches our inner scrollview, and if so return that view instead.&lt;/p&gt;
&lt;p&gt;I can&amp;#8217;t help but feel that there must be a better way of doing this, rather than try and outsmart the UIScrollView&amp;#8217;s intended behaviour. But I guess that&amp;#8217;s the tax for straying just a tad off the beaten path.&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Getting Webby With It</title>
     <link href="/articles/getting-webby-with-it.html"/>
     <updated>2008-12-28T17:00:46+01:00</updated>
     <id>tag:theexciter.com,2008-12-28:1230480046</id>
     <content type="html">
       &lt;p&gt;This site have been through its share of blogging systems over the years, wordpress, textpattern and mephisto in recent times. I finally grew tired of trying to bring my now ancient Mephisto install back to life whenever I wanted to write something (thankfully it writes completely cached pages on the frontend).&lt;/p&gt;
&lt;p&gt;I decided to go with something a little more low-fi, and thus comfortable this time around. &lt;a href=&quot;http://webby.rubyforge.org&quot;&gt;Webby&lt;/a&gt; is a nice little website generator framework that generates markup from static text files. No databases and myriad of frameworks to deal with, just plain old textfiles in a git repository with a post-commit hook. Another upside is the fact that I can &amp;#8220;update&amp;#8221; the site during offline periods, like a holiday season like this.&lt;/p&gt;
&lt;p&gt;The only problem with static sites is the fact that they are static; no comments. So I&amp;#8217;m trying out &lt;a href=&quot;http://disqus.com&quot;&gt;Disqus&lt;/a&gt;, even though I hate being dependent on other services like this.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the script I used to write out all the existing entries from Mephisto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require &quot;rubygems&quot;
require &quot;activerecord&quot;

ActiveRecord::Base.establish_connection({
  :adapter =&gt;  &quot;mysql&quot;,
  :database =&gt; &quot;theexciter_mephisto_prod&quot;,
  :username =&gt; &quot;user&quot;,
  :password =&gt;  &quot;pass&quot;,
  :host =&gt; &quot;localhost&quot;,
})

class Content &lt; ActiveRecord::Base; end
class Article &lt; Content
  has_many :comments
end
class Comment &lt; Content
  belongs_to :article
end

Content.find(:all, :conditions =&gt; &quot;type = 'Article'&quot;).each do |article|
  outfile = File.join(File.dirname(__FILE__), &quot;content/articles&quot;, &quot;#{article.permalink}.txt&quot;)
  header = &lt;&lt;-eoh
#{'-'*3}
layout:     post
title:      #{article.title}
created_at: #{article.published_at.to_yaml.sub(/^-\-\-\s/, &quot;&quot;).chomp}
filter:
  - erb
  - textile
#{'-'*3}
eoh
  
  File.open(outfile, &quot;w&quot;) do |f|
    f.puts header
    f.puts
    f.puts article.body
    f.puts
    article.comments.each do |comment|
      # comment markup omitted for brevity
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Changing blog software is the ultimate exercise in yak-shaving, since what you really should be doing is writing posts instead???&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>&quot;Trafikanten&quot; for the iPhone</title>
     <link href="/articles/trafikanten-for-the-iphone.html"/>
     <updated>2008-12-27T17:30:10+01:00</updated>
     <id>tag:theexciter.com,2008-12-27:1230395410</id>
     <content type="html">
       &lt;p&gt;Looking over my rather stale archives I realize I never wrote about the fact that I started a company together with three other guys back in march, called &lt;a href=&quot;http://shortcut.no&quot;&gt;Shortcut&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So far, we&amp;#8217;ve mostly spent our time on consulting and it&amp;#8217;s still a bigger part of our core business. But we&amp;#8217;ve also struck out into some product development, including acquiring a company that provides white-label mobile services such as SMS/MMS integration solutions. Good fun all around, including having a few employees now and a really exiting next year ahead of us. But enough of that.&lt;/p&gt;
&lt;p&gt;Ever since the iPhone SDK was released, we&amp;#8217;ve all been playing around with it. One of the most useful apps that we could think up for ourselves was something that combined the GPS, network connection and realtime information &lt;em&gt;of some sort&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We all use public transportation between home, work and pubs. All the busses, tram-lines and subways in Oslo is equipped with a GPS that transmits information on whether they&amp;#8217;re on time or not. This information is then in turn displayed on betabrite-like signs on various stops. However, what we really wanted is being able to get this information regardless on whether you&amp;#8217;re waiting at the actual stop or not.&lt;/p&gt;
&lt;p style=&quot;float:right;&quot;&gt;&lt;img src=&quot;http://kunder.shortcut.no/shortcutting/trafikanten-departure.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Enter the &amp;#8220;Trafikanten&amp;#8221; iPhone app. Using either CoreLocation, searching by name or the recently used list it fetches realtime departure info for the greater Oslo area. Being able decide whether or not to wait another five minutes before walking out into Scandinavia&amp;#8217;s notoriously bad weather is turning out to be pretty damn useful. &lt;br /&gt;
Next to MobileMail, &lt;a href=&quot;http://products.shortcut.no/trafikanten&quot;&gt;Trafikanten&lt;/a&gt; has turned out to be one of the most used applications I have on my iPhone.&lt;/p&gt;
&lt;p&gt;Developing the application was a lot fun, the Cocoa Touch API quickly became familiar from when I dabbled with Cocoa on the desktop, and the framework is overall well thought out. &lt;br /&gt;
But there&amp;#8217;s still a few things that turned out to be slightly non-trivial which I hope to blog some more about. Things such as how UITableView really deals with touches and how to work around it. In the case of the Trafikanten app, each UITableViewCell has information that can be slided sideways to mimic the way the public betabrite-style signs scrolls their departure info across (turns out most users we&amp;#8217;ve tested this on understands the metaphor perfectly) as well as how we did custom font rendering, but had to cut it from the initial release due to unsatisfactional results.&lt;/p&gt;
&lt;p style=&quot;float:left;&quot;&gt;&lt;img src=&quot;http://kunder.shortcut.no/shortcutting/trafikanten-nearby.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But to be honest, the most important lesson I&amp;#8217;ve learned developing for the iPhone is how much UI matters. Most, if not all, of the slightly &amp;#8220;heated&amp;#8221; discussions we&amp;#8217;ve had internally has been about the UI and interaction, not the actual implementation in code. The device already looks and feels so polished that you&amp;#8217;d feel like a fool pushing out something that looks and feels unpolished.&lt;/p&gt;
&lt;p&gt;After release the application jumped to the number one spot in the &amp;#8220;Top Free Apps&amp;#8221; category of the norwegian App Store, after less than a day out in the wild.&lt;/p&gt;
&lt;p&gt;The application is in Norwegian, and only useful in the general Oslo area. But if you&amp;#8217;re in this area, you now as the chance to always appear superhuman by always arriving &lt;em&gt;exactly&lt;/em&gt; when the bus arrives!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://products.shortcut.no/trafikanten/itms&quot;&gt;iTunes link&lt;/a&gt;&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt;
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li class=&quot;alt&quot; id=&quot;comment-741&quot;&gt;
&lt;p&gt;&lt;cite&gt;&lt;a href=&quot;http://blog.youngbloods.org/&quot;&gt;Carl Youngblood&lt;/a&gt;&lt;/cite&gt; Says:&lt;br /&gt;
		&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-741&quot; title=&quot;&quot;&gt;December 19th, 2008 at 05:20 PM&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
		&lt;p&gt;Congrats on a great iphone app Johan! We are also starting to develop iphone apps in addition to our Rails work here at Surgeworks. There are even rumors that iphone development in Ruby will someday be possible. Right now it&amp;#8217;s definitely more painful than it needs to be, despite Apple&amp;#8217;s nice &lt;span class=&quot;caps&quot;&gt;SDK&lt;/span&gt;.&lt;/p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li class=&quot;&quot; id=&quot;comment-742&quot;&gt;
&lt;p&gt;&lt;cite&gt;&lt;a href=&quot;http://derailer.org/&quot;&gt;Wevah&lt;/a&gt;&lt;/cite&gt; Says:&lt;br /&gt;
		&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-742&quot; title=&quot;&quot;&gt;December 20th, 2008 at 02:05 AM&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
		&lt;p&gt;Nice job!&lt;/p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
      </content>
   </entry>
  
   <entry>
     <title>What's interesting about MagLev</title>
     <link href="/articles/whats-interesting-about-maglev.html"/>
     <updated>2008-06-02T21:15:00+02:00</updated>
     <id>tag:theexciter.com,2008-06-02:1212434100</id>
     <content type="html">
       &lt;p&gt;One of the more interesting presentations at this years RailsConf seems (I wasn&amp;#8217;t at the conference myself) to have been &lt;a href=&quot;http://www.avibryant.com/2008/06/maglev-recap.html&quot;&gt;the one about MagLev&lt;/a&gt;. &lt;a href=&quot;http://blog.amber.org/2008/06/01/sacred-cows/&quot;&gt;Lots&lt;/a&gt; of &lt;a href=&quot;http://blog.obiefernandez.com/content/2008/05/maglev-is-gemst.html&quot;&gt;excitement&lt;/a&gt;, a fair bit of koolaid-drinking and a good &lt;a href=&quot;http://fukamachi.org/wp/2008/06/02/maglev-and-the-naiivety-of-the-rails-community/&quot;&gt;round of healty&lt;/a&gt; &lt;a href=&quot;http://headius.blogspot.com/2008/06/maglev.html&quot;&gt;sceptism&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But the real interesting part of MagLev to me isn&amp;#8217;t the claimed performance numbers (though that in itself may be interesting, but not until we know how ruby-conformant the final product is going to be). What&amp;#8217;s really interesting is the Gemstone/S based persistance part of MagLev, along with the distributed VM part, and not at least Gemstone (the company) itself, with a long experience of creating dynamic language VMs.&lt;/p&gt;
&lt;p&gt;And yes, so far it might be mostly koolaid (since none of us outside Gemstone has used it), but I seem to remember a certain Ruby webframework being called samething similar in 2004, and continued to receive trash even after its release. History will probably repeat itself here with the arm-chair experts.&lt;/p&gt;
&lt;p&gt;I for one find traditional relational databases lacking in a lot of problem-domains, that&amp;#8217;s why I&amp;#8217;m interested in (admititly young) projects such as &lt;a href=&quot;http://incubator.apache.org/couchdb&quot;&gt;CouchDB&lt;/a&gt;, &lt;a href=&quot;http://strokedb.com/&quot;&gt;StrokeDB&lt;/a&gt; and &lt;a href=&quot;http://neo4j.org/&quot;&gt;Neo4j&lt;/a&gt;, and anything else that teases my mind with new posibilities. Yes, Facebook, Skype and others scales MySQL/Postgres indeed, but it&amp;#8217;s interesting to note that the reasons for that is probably more developer culture and experience related. While the Gemstone Smalltalk implementation seems to cater more towards wildly enterprising sectors, such as the financial and shipping markets, where the demand is quite different from sending virtual vampires to your friends. Then again, that argument works the other way as well.&lt;/p&gt;
&lt;p&gt;I don&amp;#8217;t think ActiveRecord is no-where near being the perfect way to persist objects, rather I think it&amp;#8217;s one of the better ways to map objects to a relational database. There&amp;#8217;s a small syntactic difference there, between an enhanced class (by inheriting from ActiveRecord::Base) and an object you interact with just as you would with any other object, which in turn is persisted across machines and sessions as those very same pure objects.&lt;/p&gt;
&lt;p&gt;People playing around with a shared global object store made by a company who has been doing it for 15+ years is a good thing. Who knows, &lt;del&gt;maybe&lt;/del&gt; hopefully someone will get inspired to write something new based on ideas we&amp;#8217;ll get from using MagLev. Maybe Seaside (or something similar in spirit) will find its way back to its Ruby roots. Maybe even other Ruby implementation hackers will start to implement something similar to Gemstone at the bottom.&lt;/p&gt;
&lt;p&gt;What&amp;#8217;s also interesting is that Gemstone the company even does this to begin with. I hope it&amp;#8217;s because they see Ruby as something they could help with their dynamic language VM experience. But moreso it might be because they see real business value in Ruby, and being able to provide a fast, scalable platform for enterprises who mgiht feel that Smalltalk is a bit old fashioned for todays developers, but dynamic enough to give them flexibility while being able lean against a &lt;em&gt;vendor&lt;/em&gt;. Of course, I know little of Gemstone the company or their customers, but that that seems to be the tune as of lately.&lt;/p&gt;
&lt;p&gt;From the sounds of it, MagLev is not going to be completely open source, but I&amp;#8217;m with &lt;a href=&quot;http://gilesbowkett.blogspot.com/2008/06/maglev-jruby-and-rubinius-who-will-win.html&quot;&gt;Giles Bowkett&lt;/a&gt; and &lt;a href=&quot;http://fukamachi.org/wp/2008/06/02/maglev-and-the-naiivety-of-the-rails-community/#comment-22087&quot;&gt;Wincent Colaiuta&lt;/a&gt; on this one, while the majority of the Rails community says things &lt;em&gt;should&lt;/em&gt; be F/OSS, that has never stopped them from using mostly closed-source things like Macs and Textmate. As long as it&amp;#8217;s shiny, works great and gets the job done, the majority of Rails developers will happily use it. And that in itself is fine.&lt;/p&gt;
&lt;p&gt;Maybe it&amp;#8217;ll MagLev really will be fast, maybe it&amp;#8217;ll inspire people and make their work enjoyable, maybe it&amp;#8217;ll allow certain applications to scale easier, maybe it&amp;#8217;s persistance model will be a joy to work with. And Maybe, just maybe, it&amp;#8217;ll be &lt;em&gt;friggin&amp;#8217; sweet&lt;/em&gt;. But I, for one, welcome our new Object overlords and can&amp;#8217;t wait to see what they&amp;#8217;re upto because anything doing interesting things to, or with, Ruby is a good thing.&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-733&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://drnicwilliams.com&quot;&gt;Dr Nic&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-733&quot; title=&quot;&quot;&gt;Jun 03 at 01:10&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Whilst macs + textmate are closed-source they are both very extensible + configurable and have thriving OSS communities built off them, fwiw.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-734&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;Johan S??rensen&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-734&quot; title=&quot;&quot;&gt;Jun 03 at 07:48&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Indeed there is, and from the sound of it Maglev will also be partially OSS, so there&amp;#8217;s no reason why the same kind of community couldn&amp;#8217;t be built around it.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-735&quot;&gt;
&lt;cite&gt;&lt;span&gt;David Whittington&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-735&quot; title=&quot;&quot;&gt;Jun 03 at 14:23&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
I actually do not use Textmate or a Mac in large part because they are closed source. That&amp;#8217;s not to say I mind other people using them, but it does make a difference to me.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-736&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://tammersaleh.com&quot;&gt;Tammer Saleh&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-736&quot; title=&quot;&quot;&gt;Jun 03 at 16:24&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Another difference is that we so far haven&amp;#8217;t staked our production applications on close source platforms.  There&amp;#8217;s an easy migration path from textmate to any other editor (and the same is true for all development tools we use), but once you start using the object persistence of maglev, you&amp;#8217;re locked in.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-737&quot;&gt;
&lt;cite&gt;&lt;span&gt;Adrian Madrid&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-737&quot; title=&quot;&quot;&gt;Jun 03 at 18:17&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
I wasn&amp;#8217;t at RailsConf so I didn&amp;#8217;t witness the presentation but I&amp;#8217;m interested mostly in the replicated OODB built into the environment. If the performance was upto par with MRI I would still use something like that. My problem is that it is still something I can&amp;#8217;t get my hands on and who knows when I would be able to. I don&amp;#8217;t mind the business model either as long they only target Fortune 500 companies and poor folks like me can use it. I guess I&amp;#8217;ll have to learn to be patient.&lt;/p&gt;
&lt;p&gt;AEM&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-738&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://sensei.coretech.net.au&quot;&gt;sensei&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-738&quot; title=&quot;&quot;&gt;Jun 04 at 06:32&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Maybe it&amp;#8217;ll sell more copies of GemStone/S ;-) GemStone &lt;strong&gt;is&lt;/strong&gt; bloody awesome.&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Gitorious, so far???</title>
     <link href="/articles/gitorious-so-far.html"/>
     <updated>2008-03-19T01:00:00+01:00</updated>
     <id>tag:theexciter.com,2008-03-19:1205884800</id>
     <content type="html">
       &lt;p&gt;It seems that Git is getting more and more mindshare by the day, which is great because I&amp;#8217;m loving working with it. It&amp;#8217;s a little over three months since I made &lt;a href=&quot;http://gitorious.org&quot;&gt;Gitorious.org&lt;/a&gt; public and I&amp;#8217;ve been having lots of fun with it since then.&lt;/p&gt;
&lt;p&gt;In particular I&amp;#8217;m happy about the wide range of projects there, ruby based projects are in the majority there including semi-official mirrors of both the &lt;a href=&quot;http://gitorious.org/projects/ruby-on-rails&quot;&gt;Rails&lt;/a&gt;, &lt;a href=&quot;http://gitorious.org/projects/rspec&quot;&gt;RSpec&lt;/a&gt; and &lt;a href=&quot;http://gitorious.org/projects/macruby&quot;&gt;MacRuby&lt;/a&gt; projects. But some python things, such as &lt;a href=&quot;http://gitorious.org/projects/gdb-python&quot;&gt;gdb-python&lt;/a&gt;, a bit of &lt;a href=&quot;http://gitorious.org/projects/cl-randist&quot;&gt;lisp&lt;/a&gt; along with some Erlang, C/C++ and two linux kernel mirrors. Good times.&lt;/p&gt;
&lt;p&gt;The last two are interesting because they are some of the biggest git repositories around, yet they only take up about 200 megs worth of diskspace. Heck, take  all the repositories combined on Gitorious, and the cache for the web frontend of gitorious.org is still bigger than those. However neither disk nor bandwidth charges are anywhere near hurting my wallet, and it&amp;#8217;ll stay that way for quite a while. I don&amp;#8217;t contribute as much as I should back to most of the open-source projects I use on a day to day basis at the $dayjob (or otherwise) so I consider Gitorious as my way of giving back. Long term I have some ideas that would allow gitorious to give back even more (&lt;a href=&quot;http://rubymendicant.wikidot.com/proposal&quot;&gt;things like this&lt;/a&gt; is awefully inspiring), without resorting to cheap tricks such as ads all over the place.&lt;/p&gt;
&lt;p&gt;So far, most of my focus on the Gitorious codebase has been on stability and speed (it&amp;#8217;s really quite snappy now I think), but also a few new features such as merge requests and searching. But soon it&amp;#8217;s time to add some of the bigger things on the list, that&amp;#8217;ll help dealing with managing an open source project hosted on Gitorious.&lt;/p&gt;
&lt;p&gt;But first I want to talk about &amp;#8220;the competition&amp;#8221;, namely &lt;a href=&quot;http://github.com&quot;&gt;github&lt;/a&gt;, &amp;#8220;competition&amp;#8221; is in quotes because I honestly don&amp;#8217;t see it like that (git &lt;em&gt;is&lt;/em&gt; distributed after all), however a lot of people seem to lay it out like that whenever the two are mentioned in the same sentence. It says a lot about the workflow that git presents, that we both had the same idea and ran with it, only to release each others thing publicly a week or so apart.&lt;/p&gt;
&lt;p&gt;But I find it slightly peculiar that a lot of open-source projects (ruby/rails projects in particular) has jumped on it, despite it being closed-source. What&amp;#8217;s the point of being &lt;a href=&quot;http://tomayko.com/writings/github-is-myspace-for-hackers&quot;&gt;myspace for hackers&lt;/a&gt; (not that that&amp;#8217;s a particular flattering comparison to begin with) if I can&amp;#8217;t hack on it? But that&amp;#8217;s me being seemlingly more idealistic about this stuff than most people. &lt;a href=&quot;https://launchpad.net/&quot;&gt;Launchpad&lt;/a&gt; is closed-source and seems to be doing well despite it being a total mess to use, and even the Apache Foundation offers their incubator projects an option to use JIRA and/or Confluence (&amp;#8220;The Enterprise Wiki&amp;#8221; &amp;#8212; that cracks me up everytime).&lt;br /&gt;
Anyway, not crying about this at night, just finding it interesting. What&amp;#8217;s really important is that more people discover the advantages of a distributed SCM such as Git, even for internal (&amp;#8220;dayjob&amp;#8221;) projects, regardless of whether they host their code on a third-party server or on their own using &lt;a href=&quot;http://eagain.net/gitweb/?p=gitosis.git&quot;&gt;Gitosis&lt;/a&gt; and gitweb, a custom Gitorious install (I hear there&amp;#8217;s a few already) or just a plain old git repository somewhere.&lt;/p&gt;
&lt;p&gt;I don&amp;#8217;t want Gitorious to end up like the mess that is Launchpad, but I do think there&amp;#8217;s a few good idaes floating around when it comes to dealing with the practicalities of running, or contributing to, an opensource project that&amp;#8217;s worth looking into. In particular the notion of a distributed bug tracking system is too cool to pass up, even if distributed just means that I can track bugs across projects and different repositories. Imagine Jane having cloned Bobs project publicly and fixing that damn bug #2353, all Bob has to do now to fix the bug is to pull in Janes changes into the mainline repository. &lt;a href=&quot;http://youtube.com/watch?v=r8L39UwOS-Y&quot;&gt;Boom&lt;/a&gt;, no need to mess around with patch files.&lt;/p&gt;
&lt;p&gt;Having the ticket system &lt;em&gt;truly&lt;/em&gt; distributed is of course &lt;a href=&quot;http://groups.google.com/group/gitorious/browse_thread/thread/40a541377060cb34&quot;&gt;something to strive for&lt;/a&gt;, but I think I&amp;#8217;ll start with a slightly less lofty target for Gitorious and use tracer bullets from there to hit the sweetspot of a ticketing system that fits git, and humans, well.&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-725&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://tomayko.com/&quot;&gt;Ryan Tomayko&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-725&quot; title=&quot;&quot;&gt;Mar 19 at 05:35&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Gitorious and GitHub (and repo.or.cz for that matter) definitely compete, IMO, but not in a who-can-make-the-same-exact-widget-most-efficiently type way. There&amp;#8217;s so much room for innovation here and no one service is going to be able to experiment with all the interesting possibilities by themselves.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;d actually like to see you guys collaborate a bit. The &amp;#8220;Associated repositories&amp;#8221; (Gitorious) and &amp;#8220;Network page&amp;#8221; (GitHub) could be so much better for everybody if they were capable of showing all clones everywhere. I realize that information isn&amp;#8217;t really recorded anywhere in a Git repo but it seems like something that could be done with a bit of cooperation.&lt;/p&gt;
&lt;p&gt;Or maybe not. But, ideally, the decision to put repos on GitHub vs. Gitorious should be made based on the service preferred by each individual. Right now, that decision is more likely to be made based on where the repo you&amp;#8217;re cloning resides or where a majority of your collaborators hang out. That seems very much against the spirit of Git.&lt;/p&gt;
&lt;p&gt;With regards to the phenomenon of F/OSS projects choosing GitHub over Gitorious, I&amp;#8217;m afraid I can&amp;#8217;t offer any single explanation, but I do have a few thoughts:&lt;/p&gt;
&lt;p&gt;First, from what I can tell, idealists who care about (or even know about) Freedom 0 are actually a pretty tiny minority of the overall community. And a lot of people who claim to value open development don&amp;#8217;t actually value it when compared to a bunch of other things. It&amp;#8217;s sad, really.&lt;/p&gt;
&lt;p&gt;Next, I&amp;#8217;m not sure we&amp;#8217;re at a place where the benefits of open development outweigh the benefits of focused, opinionated design. It&amp;#8217;s definitely possible to build coherent, usable systems in an open model but you need a BDFL type personality to keep the crack out and you take on some not-insignificant overhead simply maintaining an open project; where, in a closed project, you can go cowboy for a while and focus purely on making the app meet your vision. The benefits of the open model clearly outweigh the benefits of the closed model in the long run but these are early days. (That&amp;#8217;s a general observation &amp;#8211; I&amp;#8217;m not saying GitHub has a more coherent design or anything)&lt;/p&gt;
&lt;p&gt;And finally, even though GitHub and Gitorious were released within a few days of each other, it sure feels like GitHub sucked up a bunch of projects right out of the gate somehow. I have no idea why. I can tell you that when I personally first looked at Gitorious, I somehow missed a whole bunch of great features. I&amp;#8217;m more than willing to accept that it was me not being rigorous enough in my evaluation but if you get the same sort of feedback from others, I&amp;#8217;d say it would be well worth it to spend some time ensuring that the first visit shows off as many of Gitorious&amp;#8217;s features as possible. And, here, I really think its almost entirely about networking &amp;#8211; the project search and code browser and everything that&amp;#8217;s been a part of the forges is damned near irrelevant.&lt;/p&gt;
&lt;p&gt;Keep up the good work, Johan. I&amp;#8217;m absolutely blown away with what you&amp;#8217;ve accomplished so far and am anxious to see what&amp;#8217;s next.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-728&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;Johan S??rensen&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-728&quot; title=&quot;&quot;&gt;Mar 19 at 13:04&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Good comments&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I???d actually like to see you guys collaborate a bit. The ???Associated repositories??? (Gitorious) and ???Network page??? (GitHub) could be so much better for everybody if they were capable of showing all clones everywhere. I realize that information isn???t really recorded anywhere in a Git repo but it seems like something that could be done with a bit of cooperation [..]  That seems very much against the spirit of Git.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Me too, it&amp;#8217;s actually been on &lt;a href=&quot;http://gitorious.org/projects/gitorious/repos/mainline/blobs/master/TODO.txt#line32&quot;&gt;the list since the beginning&lt;/a&gt;, but I imagine it no way being specific for repo.or.cz/github/other, just a general remote repository hosted somewhere else. We could even peek at it periodically to fetch recent activity there. It&amp;#8217;s also possible to fetch which remotes that a repository often pull in and display that somehow.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;And a lot of people who claim to value open development don???t actually value it when compared to a bunch of other things. It???s sad, really.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;#8217;m as pragmatic as the next guy when it comes to getting work done, but I view open source and a completely different way, there it&amp;#8217;s a matter of principles for me, and especially so if you&amp;#8217;re marketing whatever you&amp;#8217;re doing as &amp;#8220;suitable for open source projects&amp;#8221;. Anything less is like Al Gore driving around in a Hummer as I see it. But in the end, I supposed all that really matter is that a (F/OSS) project being hosted somewhere produce useful code.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I can tell you that when I personally first looked at Gitorious, I somehow missed a whole bunch of great features. I???m more than willing to accept that it was me not being rigorous enough in my evaluation but if you get the same sort of feedback from others, I???d say it would be well worth it to spend some time ensuring that the first visit shows off as many of Gitorious???s features as possible. And, here, I really think its almost entirely about networking ??? the project search and code browser and everything that???s been a part of the forges is damned near irrelevant.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&amp;#8217;s some really good points, and something that&amp;#8217;s been nagging in the back of my head since the start, thanks for putting words to it. The overall UI (as in conveying what it does and general discoverability) needs some &lt;em&gt;uhmph&lt;/em&gt;, and I&amp;#8217;ve put the networking-type things a bit on the backburner in favor of individual per-project features, but maybe it&amp;#8217;s indeed time to change that approach.&lt;/p&gt;
&lt;p&gt;Again, thanks for the comments Ryan!&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-730&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://priit.mx.ee&quot;&gt;Priit Tamboom&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-730&quot; title=&quot;&quot;&gt;Mar 20 at 08:38&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
In the service world it gets really fuzzy about open source or not, i&amp;#8217;m still puzzled. However as a programmer I support open source way of doing as much as I can. However as Ryan told for most users it does not matter so much at all as we would like to think (especially in the sort term).&lt;/p&gt;
&lt;p&gt;In my view, there is no need for direct competition, solve only your (or core contributors) needs and visions. If you start competition feature-to-feature basis, you might need some revenue plan to keep things sustainable etc.&lt;/p&gt;
&lt;p&gt;By the way I don&amp;#8217;t mind ads if it is unobtrusive and correctly placed for users (similar to gmail), however aggressive ads would be deal-breakers for sure.&lt;/p&gt;
&lt;p&gt;Thanks for driving gitorious!&lt;br /&gt;
Priit@still.not.yet.so.diligent.committer&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-731&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;Johan S??rensen&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-731&quot; title=&quot;&quot;&gt;Mar 20 at 09:02&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
In the long term, there&amp;#8217;s definitely a revenue plan (that doesn&amp;#8217;t mean I&amp;#8217;ll have to charge directly for usage) it just doesn&amp;#8217;t make sense to implement it yet since, like I mentioned, the cost is abysmal and a) I need it myself to host my own personal projects, b) I need the app itself (internally) for my company and c) I don&amp;#8217;t mind &amp;#8220;giving back&amp;#8221; in the indirect way it currently does.&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Gitorious source pushed - and a freebie!</title>
     <link href="/articles/gitorious-source-pushed-and-a-freebie.html"/>
     <updated>2008-01-13T19:56:00+01:00</updated>
     <id>tag:theexciter.com,2008-01-13:1200250560</id>
     <content type="html">
       &lt;p&gt;I&amp;#8217;ve pushed the source to &lt;a href=&quot;http://gitorious.org/projects/gitorious&quot;&gt;Gitorious&lt;/a&gt; to&amp;#8230; Gitorious! &lt;a href=&quot;http://rashkovskii.com/&quot;&gt;Yurii&lt;/a&gt; has already made a &lt;a href=&quot;http://gitorious.org/projects/gitorious/repos/yrashk-gitorious-clone&quot;&gt;clone of it&lt;/a&gt; and I think he&amp;#8217;s hacking on some SVN mirroring he needed for one of his projects. Very cool.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve also added a &lt;a href=&quot;http://gitorious.org/projects/tumbline&quot;&gt;project called Tumbline&lt;/a&gt;, which is an 80% done tumblelogging application I wrote during the summer when I was really unhappy about &lt;a href=&quot;http://tumblr.com&quot;&gt;Tumblr&lt;/a&gt; (where I host &lt;a href=&quot;http://application-error.com&quot;&gt;Application Error&lt;/a&gt;), they have since shaped up a bit and I&amp;#8217;ll probably continue using them. But I&amp;#8217;ve open sourced the application I wrote in case someone wants to use it for something, rather than it collecting dust in my &lt;code&gt;~/Projects&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Gitorious - open source project hosting</title>
     <link href="/articles/gitorious-open-source-project-hosting.html"/>
     <updated>2008-01-08T10:08:00+01:00</updated>
     <id>tag:theexciter.com,2008-01-08:1199783280</id>
     <content type="html">
       &lt;p&gt;Since writing &lt;a href=&quot;http://theexciter.com/articles/distributed-scm-goodness&quot;&gt;this post&lt;/a&gt; I&amp;#8217;ve slowly been implementing some of the ideas of my take on a way to do open source collaboration on the repository level, based on git in particular.&lt;/p&gt;
&lt;p&gt;I love open source, from the things being created to the concept in itself. Project forges like SourceForge and Rubyforge are great ways to publish a project and handle the infrastructure around it, such as mailing lists, bugtrackers and tarball releases.&lt;/p&gt;
&lt;p&gt;But they&amp;#8217;re also filled with dead projects. Some of these projects have been forked, or are actively maintained elsewhere, but most of the time you aren&amp;#8217;t so lucky. They&amp;#8217;re also rather centralized in the sense that the project owner or maintainer, has to actively accept patches, or hand out commit bits, in order for the repository to stay up with developments. &lt;br /&gt;
As a project maintainer this can be hard in the long run, particular if you&amp;#8217;ve for some reason lost interest in the project, or are just too plain busy with other things. I know this far too well from my own opensource projects.&lt;/p&gt;
&lt;p&gt;Distributed source control provides one possible way around this, because every clone (or checkout in svn-speak) of a repository is a full-blown repository, you can just publish your updated repository instead and if people like your stuff better they can just pull from that instead of the &amp;#8220;mainline&amp;#8221; repository! Likewise, a project maintainer can just pull in these changes into the mainline repository to keep the project going forward and easily accept contributions.&lt;/p&gt;
&lt;p&gt;&lt;abbr title=&quot;Distributed Source Control&quot;&gt;DSCM&lt;/abbr&gt; tools like &lt;a href=&quot;http://git.or.cz/&quot;&gt;git&lt;/a&gt; are great at this, since every clone is a full repository it has to be extremely good at merging any commits you make when pushing upstream, hence pulling in other commits from clones works just as well. This also means that forking is not really such a big issue, because any forks can easily be pulled back in upstream (because of the shared commit history), in fact, forking (in the essence of the word) is the &lt;em&gt;only&lt;/em&gt; way to work with DSCM. Of course, the social aspects of forking, such as disagreements of project direction, is an entire different issue that has to dealt with on the social level.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://gitorious.org/&quot;&gt;Gitorious&lt;/a&gt; is a free git hosting solution I&amp;#8217;ve built, that allows anyone to create a project, and in turn, allowing anyone to create a clone of that project&amp;#8217;s mainline repository for their own contributions. The project owner, or anyone with write access to the mainline repository, can then pull in these changes into the mainline repository if they like what they se. Or they can provide feedback directly on commits if they&amp;#8217;re unsure about the approach taken, or just wanting to communicate something.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m hoping it will be useful for git users and I&amp;#8217;m very interested in seeing this being used and hear peoples ideas for improvements.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve got many more things I want to do with &lt;a href=&quot;http://gitorious.org/&quot;&gt;Gitorious&lt;/a&gt;, an improved repository browser and better ways to communicate with contributers are some of the next things on the list, but what&amp;#8217;s there today has everything to get you started.&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-602&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://blog.aslakhellesoy.com&quot;&gt;Aslak Helles??y&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-602&quot; title=&quot;&quot;&gt;Jan 08 at 11:45&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
This is great Johan!&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m just getting my feet wet with Git myself, but doesn&amp;#8217;t Git already have a built-in browser?&lt;br /&gt;
http://repo.or.cz/w/god.git (The graphiclog is particularly nice).&lt;/p&gt;
&lt;p&gt;Btw &amp;#8211; http://repo.or.cz/ is a public Git hosting site.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-603&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-603&quot; title=&quot;&quot;&gt;Jan 08 at 12:42&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
yeah, I&amp;#8217;m not happy with the current browser in gitorious at all, aspiring to be more gitk-like is an improvement thats on my TODO. I hope to add some more interesting things to gitorious, that repo.or.cz doesn&amp;#8217;t currently have.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-604&quot;&gt;
&lt;cite&gt;&lt;span&gt;Koz&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-604&quot; title=&quot;&quot;&gt;Jan 08 at 20:07&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
I&amp;#8217;m not sure aiming to be gitk like should be considered a good idea ;).&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-605&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://blog.codefront.net/&quot;&gt;Chu Yeow&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-605&quot; title=&quot;&quot;&gt;Jan 09 at 01:56&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
I really like the clean interface (and Georgia heh).&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m just getting into Git as well and may place a few toy apps up there and hopefully give you some feedback. Syntax highlighting for the diffs at least would be really nice to add, but I&amp;#8217;m sure that&amp;#8217;s already on your TODO list.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-606&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexiter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-606&quot; title=&quot;&quot;&gt;Jan 09 at 10:01&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
koz, I was mostly thinking about the way branch merging is displayed in gitk, not the rest ;)&lt;/p&gt;
&lt;p&gt;Highlighted diff display is definitely one of the next items on the list&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-608&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://novemberain.com&quot;&gt;Oleg Andreev&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-608&quot; title=&quot;&quot;&gt;Jan 17 at 18:09&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Git&amp;#8217;s great. Gitorious&amp;#8217; great.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve just done `git init`, added gitorious project url to .git/config and made `git pull`. Now, when my macbook suddenly dies i won&amp;#8217;t be so disappointed about unfinished opensource stuff.&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>A quick stroll through DTrace</title>
     <link href="/articles/a-quick-stroll-through-dtrace.html"/>
     <updated>2007-12-09T00:52:00+01:00</updated>
     <id>tag:theexciter.com,2007-12-09:1197157920</id>
     <content type="html">
       &lt;p&gt;DTrace has been getting a lot more press recently, since Apple has ported it to Leopard, it&amp;#8217;s also been getting a lot of mentions in the Ruby community since Apple has included the DTrace providers for it. Yet, surprisingly few seem to actually use DTrace much (yours truly included really). So here&amp;#8217;s a short intro to DTrace and D (not to be confused with &lt;a href=&quot;http://www.digitalmars.com/d/&quot;&gt;the other D&lt;/a&gt;).&lt;br /&gt;
	&lt;br /&gt;
The essence of DTrace is &lt;em&gt;probes&lt;/em&gt;, these are event handlers that fire whenever their particular event happens, you can then register interest in these probe events with a particular action, like printing it, aggregating usage counts and whatever other way you decide to use this information.&lt;/p&gt;
&lt;p&gt;Since there&amp;#8217;s over 450 000 probes in Leopard, there&amp;#8217;s a lot of information you can gather and the trick is to start at a high level and drill down &amp;#8212; &amp;#8220;hmm, why are there 800 syscalls? hmm, what function caused this? what is it writing? what did it do right before it made the call to write()?&amp;#8221; and so, one question leads to next with DTrace.&lt;br /&gt;
	&lt;br /&gt;
We can get a list of all the probes currently available on our system, by running &lt;code&gt;dtrace -l&lt;/code&gt;, or drilling down with the &lt;code&gt;-P&lt;/code&gt; flag&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo dtrace -l | wc -l
  454839
$ sudo dtrace -l -P syscall | head -5
   ID   PROVIDER            MODULE                          FUNCTION NAME
17590    syscall                                             syscall entry
17591    syscall                                             syscall return
17592    syscall                                                exit entry
17593    syscall                                                exit return
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So let&amp;#8217;s start with asking what syscalls are currently being made by all the applications currently running (unless otherwise told to, DTrace will listen forever so finish it with ctrl+c):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo dtrace -n 'syscall:::entry{trace(execname)}'
dtrace: description 'syscall:::entry' matched 427 probes
CPU     ID                    FUNCTION:NAME
  1  17698                      ioctl:entry   dtrace                           
  1  17698                      ioctl:entry   dtrace                                             
  1  17682                  sigaction:entry   dtrace                           
  1  17682                  sigaction:entry   dtrace                           
  1  17682                  sigaction:entry   dtrace                           
  1  18258           __semwait_signal:entry   Little Snitch U
  1  17686                sigprocmask:entry   WindowServer                     
  1  17696                sigaltstack:entry   WindowServer
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The probes are specified in a &lt;em&gt;provider:module:function:name&lt;/em&gt; format, with an empty entry being a wildcard, so asking for all syscall function entries would mean asking for &lt;code&gt;syscall:::entry&lt;/code&gt;, we could get all write syscall entries by asking for &lt;code&gt;syscall::write:entry&lt;/code&gt; and its (function) returns by asking for &lt;code&gt;syscall::write:return&lt;/code&gt; for the write() function.&lt;/p&gt;
&lt;p&gt;So the above output isn&amp;#8217;t all that useful since it&amp;#8217;s too much information for us puny humans to parse effectively. Luckily DTrace provides means of aggregating things with the &lt;code&gt;@[key(s)]&lt;/code&gt; notation, where &lt;em&gt;key(s)&lt;/em&gt; is an arbitary comma-seperated list of D expressions and the value is an aggregating function like &lt;code&gt;count()&lt;/code&gt; that simply counts the number of times something happens. So to aggregate the number of syscalls on the application name we can use &lt;code&gt;execname&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo dtrace -n 'syscall:::entry{ @[execname] = count() }'
dtrace: description 'syscall:::entry' matched 427 probes
^C

  DirectoryServic                  2
  Finder                           2
...
  WindowServer                    46
  launchd                         48
  natd                            81
  SystemUIServer                 113
  Adium                          131
  ruby                           356
  pmTool                         584
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can even expand this to see what probe function is being called using the &lt;code&gt;probefunc&lt;/code&gt; expression:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo dtrace -n 'syscall:::entry{ @[execname, probefunc] = count() }'
dtrace: description 'syscall:::entry' matched 427 probes
^C

 Finder                   kevent                    1
 Safari                   gettimeofday              1
 Terminal                 mmap                      1
...
 ruby                     select                   10
 dtrace                   ioctl                    14
 WindowServer             sigaltstack              15
 WindowServer             sigprocmask              15
 ruby                     __semwait_signal        141
 pmTool                   __sysctl                291
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ruby seems to be waiting in a semaphore/thread, lets take a look at its current stacktrace. We can do this by specifying a &lt;em&gt;predicate&lt;/em&gt; for our probing, think of it as a conditional. So, by only registering interest in a proble if the &lt;code&gt;execname == &quot;ruby&quot;&lt;/code&gt; predicate is met, we print the stack:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo dtrace -n 'syscall:::entry/execname == &quot;ruby&quot;/{ ustack() }'
dtrace: description 'syscall:::entry' matched 427 probes
CPU     ID                    FUNCTION:NAME
  0  18258           __semwait_signal:entry 
              libSystem.B.dylib`__semwait_signal+0xa
              libruby.1.dylib`rb_thread_group+0x29f
              libSystem.B.dylib`_pthread_start+0x141
              libSystem.B.dylib`thread_start+0x22

  0  18258           __semwait_signal:entry 
              libSystem.B.dylib`__semwait_signal+0xa
              libruby.1.dylib`rb_thread_group+0x29f
              libSystem.B.dylib`_pthread_start+0x141
              libSystem.B.dylib`thread_start+0x22
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yep, looks like an rb_thread allright. And that makes perfect sense since I had a mongrel running there in the background.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s take a look at what Ruby providers are available (you need a running ruby process to see this):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo dtrace -l -P &quot;ruby*&quot;          
   ID   PROVIDER            MODULE             FUNCTION NAME
19708  ruby48398   libruby.1.dylib             rb_call0 function-entry
19709  ruby48398   libruby.1.dylib             rb_call0 function-return
19710  ruby48398   libruby.1.dylib      garbage_collect gc-begin
19711  ruby48398   libruby.1.dylib      garbage_collect gc-end
19712  ruby48398   libruby.1.dylib              rb_eval line
19713  ruby48398   libruby.1.dylib         rb_obj_alloc object-create-done
19714  ruby48398   libruby.1.dylib         rb_obj_alloc object-create-start
19715  ruby48398   libruby.1.dylib      garbage_collect object-free
19716  ruby48398   libruby.1.dylib           rb_longjmp raise
19717  ruby48398   libruby.1.dylib              rb_eval rescue
19718  ruby48398   libruby.1.dylib    ruby_dtrace_probe ruby-probe
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We wildcard the ruby provider name since they&amp;#8217;re per app specific (the &lt;code&gt;48398&lt;/code&gt; part is the PID). Which is cool if you&amp;#8217;re running more than one ruby process, so you could poke around figureing out why one is eating cpu and the other isn&amp;#8217;t (Here&amp;#8217;s an &lt;a href=&quot;https://dtrace.joyent.com/projects/ruby-dtrace/wiki/Ruby+DTrace+probes+and+arguments&quot;&gt;explanation of the Ruby probes&lt;/a&gt;). Let&amp;#8217;s see what method calls are being used the most in a typical Rails request:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo dtrace -n 'ruby*:::function-entry{ @[copyinstr(arg0), copyinstr(arg1)] = count()  }'
dtrace: description 'ruby*:::function-entry' matched 1 probe
^C
...                                                                       
	Array            pop                                     24
  File::Stat       size                                    24
  Inflector        inflections                             24
  Inflector        inflections_without_route_reloading     24
...                                                    
	Hash             []                                     557
  Hash             []=                                    623
  String           to_s                                   723
  Hash             key?                                  4379
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we make the aggregation list keys out of the class and the method name, which is specified as &lt;code&gt;argN&lt;/code&gt;. &lt;code&gt;args[]&lt;/code&gt; is an array of arguments for the probe, &lt;code&gt;argN&lt;/code&gt; is a shortcut for that array, in this case the arguments are what the probe made them up to be (class and method name, arg 2 and 3 are sourcefile and line number), but it could also be the arguments for a function call. &lt;code&gt;copyinstr()&lt;/code&gt; simply means &amp;#8220;make a string out of this pointer reference&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Back to poking around, Hash lookups and String#to_s isn&amp;#8217;t all that interesting for us right now, but I&amp;#8217;m kinda curious about what it is stat()&amp;#8216;ing 24 times for a request? Let&amp;#8217;s try and find out:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
sudo dtrace -n 'ruby*:::function-entry/copyinstr(arg0) == &quot;File::Stat&quot; &amp;&amp; copyinstr(arg1) == &quot;size&quot;/{ ustack()  }'
dtrace: description 'ruby*:::function-entry' matched 1 probe
CPU     ID                    FUNCTION:NAME
  0  19708          rb_call0:function-entry 
              libruby.1.dylib`rb_eval_string_wrap+0x43f9
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
              libruby.1.dylib`rb_eval_string_wrap+0x34ba
              libruby.1.dylib`rb_eval_string_wrap+0x1f53
              libruby.1.dylib`rb_eval_string_wrap+0x2dbb
              libruby.1.dylib`rb_eval_string_wrap+0x4d65
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
              libruby.1.dylib`rb_eval_string_wrap+0x149d
              libruby.1.dylib`rb_eval_string_wrap+0x4d65
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
              libruby.1.dylib`rb_eval_string_wrap+0x4d65
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
              libruby.1.dylib`rb_thread_trap_eval+0x959
              libruby.1.dylib`rb_yield+0x21
              libruby.1.dylib`rb_ary_each+0x1e
              libruby.1.dylib`rb_eval_string_wrap+0x455f

  0  19708          rb_call0:function-entry 
              libruby.1.dylib`rb_eval_string_wrap+0x43f9
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
              libruby.1.dylib`rb_eval_string_wrap+0x1f53
              libruby.1.dylib`rb_eval_string_wrap+0x4d65
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
              libruby.1.dylib`rb_eval_string_wrap+0x149d
              libruby.1.dylib`rb_eval_string_wrap+0x4d65
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
              libruby.1.dylib`rb_eval_string_wrap+0x4d65
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
              libruby.1.dylib`rb_thread_trap_eval+0x959
              libruby.1.dylib`rb_yield+0x21
              libruby.1.dylib`rb_ary_each+0x1e
              libruby.1.dylib`rb_eval_string_wrap+0x455f
              libruby.1.dylib`rb_eval_string_wrap+0x5173
              libruby.1.dylib`rb_eval_string_wrap+0x23ee
                                                         24
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By adding the predicate of our target class and method we get only what we&amp;#8217;re interested in, and print the stack using &lt;code&gt;ustack&lt;/code&gt;. Unfortunately this being Ruby it&amp;#8217;s not all that useful to us, since it&amp;#8217;s pretty much all rb_eval-inner-ruby-runtime-here-be-dragons-stuff (I would love a ustack helper for ruby, &lt;a href=&quot;http://blogs.sun.com/levon/entry/python_and_dtrace_in_build&quot;&gt;like there is for python&lt;/a&gt;), that doesn&amp;#8217;t make much sense to us. I wonder which file it&amp;#8217;s doing this in though?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo dtrace -n 'ruby*:::function-entry/copyinstr(arg0) == &quot;File::Stat&quot; &amp;&amp; copyinstr(arg1) ==&quot;size&quot;/{ printf(&quot;%s in %s&quot;, copyinstr(arg0),copyinstr(arg2))}'
  dtrace: description 'ruby*:::function-entry' matched 1 probe
File::Stat in [...]gems/mongrel-1.0.1/lib/mongrel/handlers.rb
File::Stat in [...]gems/mongrel-1.0.1/lib/mongrel/handlers.rb
File::Stat in [...]gems/mongrel-1.0.1/lib/mongrel/handlers.rb
# (output slightly truncated)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;OK, so that tells us where this is happened, but not &lt;em&gt;what&lt;/em&gt; it&amp;#8217;s stat()&amp;#8216;ing, luckily File::Stat sounds like something that might be doing a syscall, and we have probes for that, here&amp;#8217;s a script that matches up the ruby function-entry with looking at syscalls at the same time:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#!/usr/sbin/dtrace -s

#pragma D option quiet

ruby*:::function-entry
/copyinstr(arg0) == &quot;File::Stat&quot; &amp;&amp; copyinstr(arg1) == &quot;size&quot;/
{
  self-&gt;interested = 1;
  self-&gt;rubymethod = copyinstr(arg1);
  self-&gt;rubyclass = copyinstr(arg0)
}

syscall::stat*:entry
/self-&gt;interested/
{
  printf(&quot;%s from %s#%s\n&quot;, copyinstr(arg0), self-&gt;rubyclass, self-&gt;rubymethod); 
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By defining the variable &lt;code&gt;interested&lt;/code&gt; whenever we&amp;#8217;re in the function-entry we&amp;#8217;re interested in, we can use that variable as a predicate for our &lt;code&gt;syscall::stat*:entry&lt;/code&gt; (stat* is wildcarded because there&amp;#8217;s things like stat64() as well), making it executable and running it we see:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ chmod +x who_be_stattin.d	
$ sudo ./who_be_stattin.d 
RAILS_ROOT/public/favicon.ico from File::Stat#size
RAILS_ROOT/public/favicon.ico from File::Stat#size
RAILS_ROOT/public/favicon.ico from File::Stat#size
RAILS_ROOT/public/favicon.ico from File::Stat#size
RAILS_ROOT/public/favicon.ico from File::Stat#size
RAILS_ROOT/public/favicon.ico from File::Stat#size
RAILS_ROOT/public/javascripts/application.js from File::Stat#size
RAILS_ROOT/public/javascripts/application.js from File::Stat#size
RAILS_ROOT/public/javascripts/application.js from File::Stat#size
RAILS_ROOT/public/javascripts/application.js from File::Stat#size
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Aha! It must be the Rails mongrel handler that checks the size of the asset files, before it sends them down the wire (it&amp;#8217;s from a local &lt;code&gt;./script/console&lt;/code&gt;). Not so interesting after all, but at least we learnt a bit along the way.&lt;/p&gt;
&lt;p&gt;Remember the ruby providers from up there? the &lt;code&gt;ruby-probe&lt;/code&gt; one? That one is basically a plugin that lets you fire your very own probes in your app, using the (Apple shipped) &lt;code&gt;DTracer&lt;/code&gt; class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&gt;&gt; def with_my_probe &amp;blk
&gt;&gt;   DTracer.fire(&quot;my-probe-entry&quot;)
&gt;&gt;   yield
&gt;&gt;   DTracer.fire(&quot;my-probe-return&quot;)
&gt;&gt;   end
=&gt; nil
&gt;&gt; with_my_probe{ puts &quot;moo&quot; }


$ cat probe_my_probe.d
#!/usr/sbin/dtrace -s

ruby*:::ruby-probe
/copyinstr(arg0) == &quot;my-probe-entry&quot;/
{
  self-&gt;interested = 1;
}

syscall:::
/self-&gt;interested/
{
  /* default action is to just print it */
}

ruby*:::function-entry
/self-&gt;interested/
{
  printf(&quot;%s in %s&quot;, copyinstr(arg1), copyinstr(arg0));
}

ruby*:::ruby-probe
/copyinstr(arg0) == &quot;my-probe-return&quot;/
{
  self-&gt;interested = 0;
}

$ sudo ./probe_my_probe.d
dtrace: script './ruby_probe_test.d' matched 860 probes
CPU     ID                    FUNCTION:NAME
  1 454800          rb_call0:function-entry puts in Object
  1 454800          rb_call0:function-entry write in IO
  1  17598                      write:entry 
  1  17599                     write:return 
  1 454800          rb_call0:function-entry write in IO
  1  17598                      write:entry 
  1  17599                     write:return 
  1 454800          rb_call0:function-entry fire in Module

# (While running 'with_my_probe{ puts &quot;foo&quot; }' in irb)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But more on that later. In the meantime do go off exploring your OS and applications with DTrace, you&amp;#8217;d be surprised how quickly you can loose an hour or two just by asking &amp;#8220;why is &lt;em&gt;that&lt;/em&gt; doing &lt;em&gt;this&lt;/em&gt; here?&amp;#8221;&amp;#8230;&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-600&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://poundbang.in&quot;&gt;Harish Mallipeddi&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-600&quot; title=&quot;&quot;&gt;Dec 10 at 04:37&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Is there any place where I can find out more about the Python probes? Apart from that one article on a Sun engineer&amp;#8217;s blog, I couldn&amp;#8217;t really find more info on the Python ustack support. Apple shipped DTrace alright, but never packaged any proper documentation with it :(&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>CouchDb views in Ruby instead of Javascript</title>
     <link href="/articles/couchdb-views-in-ruby-instead-of-javascript.html"/>
     <updated>2007-09-15T10:16:00+02:00</updated>
     <id>tag:theexciter.com,2007-09-15:1189844160</id>
     <content type="html">
       &lt;p&gt;I&amp;#8217;ve just pushed &lt;a href=&quot;http://couchobject.rubyforge.org/&quot;&gt;CouchObject&lt;/a&gt; 0.5 out to the rubyforge mirrors, here&amp;#8217;s the History.txt file:&lt;/p&gt;
&lt;pre&gt;
== 0.5.0 2007-09-15

* 2 major enhancements:
  * Database.filter{|doc| } for filtering the on doc, in Ruby!
  * couch_ruby_view_requestor, a JsServer client for CouchDb allowing you to query in Ruby

* 1 minor enhancement:
  * Added Database#store(document), the parallel of Document#save(database)
&lt;/pre&gt;
&lt;p&gt;Those two major enhancements are a result of my laziness as reported at the end of the &lt;a href=&quot;http://theexciter.com/articles/couchobject-released&quot;&gt;last post&lt;/a&gt;, because now you can query your CouchDb views in Ruby instead of Javascript:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ irb -rubygems
&gt;&gt; require 'couch_object'
=&gt; true
&gt;&gt; db = CouchObject::Database.open &quot;http://localhost:8888/foo&quot;
=&gt; #&lt;CouchObject::Database:0x142d4d8 ...&gt;
&gt;&gt; pp db.post(&quot;_temp_view&quot;, &quot;proc{ |doc| return doc if doc[\&quot;foo\&quot;] =~ /qux/  }&quot;)
#&lt;CouchObject::Response:0x13f8d50
 @parsed_body=
  {&quot;rows&quot;=&gt;
    [{&quot;_rev&quot;=&gt;189832163,
      &quot;_id&quot;=&gt;&quot;96193CD461168BD024B64EA367C1E0BF&quot;,
      &quot;value&quot;=&gt;
       {&quot;_id&quot;=&gt;&quot;96193CD461168BD024B64EA367C1E0BF&quot;,
        &quot;_rev&quot;=&gt;189832163,
        &quot;foo&quot;=&gt;&quot;qux&quot;}}],
   &quot;offset&quot;=&gt;0,
   &quot;total_rows&quot;=&gt;1,
   &quot;view&quot;=&gt;&quot;_temp_view:proc{ |doc| return doc if doc[\&quot;foo\&quot;] =~ /qux/  }&quot;},
 @response=#&lt;Net::HTTPOK 200 OK readbody=true&gt;&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Boom. The &lt;code&gt;rows&lt;/code&gt; key there is our matching document with an attribute of &lt;code&gt;foo&lt;/code&gt; that matches &lt;code&gt;/qux/&lt;/code&gt;. &lt;br /&gt;
You just pass in anything that responds to a &lt;code&gt;#call(the_couch_document)&lt;/code&gt; when you define your view request.&lt;/p&gt;
&lt;p&gt;But passing around strings will make your eyes sore, so, lets just do this in pure Ruby:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&gt;&gt; pp db.filter{ |doc| return doc if doc[&quot;foo&quot;] == &quot;qux&quot; }
[{&quot;_rev&quot;=&gt;189832163,
  &quot;_id&quot;=&gt;&quot;96193CD461168BD024B64EA367C1E0BF&quot;,
  &quot;value&quot;=&gt;
   {&quot;_id&quot;=&gt;&quot;96193CD461168BD024B64EA367C1E0BF&quot;,
    &quot;_rev&quot;=&gt;189832163,
    &quot;foo&quot;=&gt;&quot;qux&quot;}}]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Thanks to a bit of RubyToRuby we can send along the block to CouchDb just fine. &lt;br /&gt;
But, how is this all done on the CouchDb side of things? It&amp;#8217;s actually a whole lot easier than it looks; all CouchDb does when it receives a view query like the above is pass it on to whatever is defined as the &lt;code&gt;JsServer&lt;/code&gt; in $COUCH_INSTALL/couch.ini, this is normally SpiderMonkey, but with the CouchObject gem installed it can be Ruby!&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the relevant section from my couch.ini:&lt;br /&gt;
&lt;pre&gt;&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;&amp;#8230;&lt;/li&gt;
	&lt;li&gt;You need full, or relative to couch install dir, paths for now&lt;br /&gt;
JsServer=/opt/local/bin/couch_ruby_view_requestor&lt;/li&gt;
	&lt;li&gt;&amp;#8230;&lt;br /&gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So have a go at it:&lt;/p&gt;
&lt;pre&gt;
  $ sudo gem install couchobject
&lt;/pre&gt;
&lt;p&gt;Check out the Git source and have a play with it:&lt;/p&gt;
&lt;pre&gt;
  $ git clone git://gitorious.org/projects/couchobject/mainline.git
&lt;/pre&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-529&quot;&gt;
&lt;cite&gt;&lt;span&gt;james&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-529&quot; title=&quot;&quot;&gt;Oct 10 at 23:09&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Looks like the .ini file has changed format since you wrote this; it&amp;#8217;s now reading like this (on my system)&lt;/p&gt;
&lt;p&gt;[Couch Query Servers]&lt;/p&gt;
&lt;p&gt;text/javascript=/opt/local/share/couchdb/server/couchjs -f main.js&lt;/p&gt;
&lt;p&gt;Thanks for your work on this. Makes it easier to play around and kick the wheels. I&amp;#8217;m currently deciding whether to commit to couch for a big project and your plugin has helped get my head around things &amp;#8230;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-530&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-530&quot; title=&quot;&quot;&gt;Oct 11 at 06:23&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
yeah, I need to bring it up to date with the latest couch trunk, I&amp;#8217;ve been busy with other things for the past two weeks. The good news is that it&amp;#8217;s now possible to use several view servers at once, so you don&amp;#8217;t have to be &amp;#8220;locked in&amp;#8221; to just using ruby, or any other language, only.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-560&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://www.automatthew.com&quot;&gt;Matthew King&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-560&quot; title=&quot;&quot;&gt;Oct 29 at 22:32&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
The new syntax for adding a query server to couch.ini is this:&lt;/p&gt;
&lt;p&gt;text/ruby=/wherever/couchobject/bin/couch_ruby_view_requestor&lt;/p&gt;
&lt;p&gt;The ruby query server is, as Johan says, out of date.  I just submitted a patch on Rubyforge that fixes the one minor change in couch_ruby_view_requestor, updates the Ruby proc for the example view, and revises the specs and code where necessary to conform to the way CouchDB currently processes view.&lt;/p&gt;
&lt;p&gt;I just realized that I forgot to update the README.  Johan, I&amp;#8217;m using git, but I&amp;#8217;m not very familiar with shared development practices.  I can make my changes available in a public git repo, if that is helpful to you.&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>CouchObject released!</title>
     <link href="/articles/couchobject-released.html"/>
     <updated>2007-09-14T06:10:00+02:00</updated>
     <id>tag:theexciter.com,2007-09-14:1189743000</id>
     <content type="html">
       &lt;p&gt;&lt;a href=&quot;http://rubyforge.org/projects/couchobject/&quot;&gt;CouchObject&lt;/a&gt; 0.0.1 is out, fresh from the sofa. Sit down, relax and read the &lt;a href=&quot;http://couchobject.rubyforge.org/rdoc/&quot;&gt;RDoc&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ sudo gem install couchobject
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since the &lt;a href=&quot;http://theexciter.com/articles/couchdb-and-couchobjects&quot;&gt;last time&lt;/a&gt; I&amp;#8217;ve taken it in a slightly different direction, focusing more on getting the basics up and running. You see, I&amp;#8217;ve realised that CouchDb isn&amp;#8217;t really perfect as a general OODB store (though, nothing is stopping you from storing an objects attributes in CouchDb, the Persistable module still does that). I&amp;#8217;ll be waiting for &lt;a href=&quot;http://www.infoq.com/news/2007/08/gemstone-ruby&quot;&gt;GemStone and Rubinius&lt;/a&gt; for an awesome OODB. Instead CouchObject focus specifically on documents as it is right now:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&gt;&gt; CouchObject::Database.create!(&quot;http://localhost:8888&quot;, &quot;roflcopters&quot;)
=&gt; {&quot;ok&quot;=&gt;true}
&gt;&gt; db = CouchObject::Database.open(&quot;http://localhost:8888/roflcopters&quot;)
=&gt; #&lt;CouchObject::Database:0x65b184...&gt;
&gt;&gt; db.all_documents
=&gt; []
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Creating and saving a document&lt;br /&gt;
&lt;notextile&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;gt;&amp;gt; doc = CouchObject::Document.new
=&amp;gt; #&amp;lt;CouchObject::Document:0x62708c @id=nil, @attributes={}, @revision=nil&amp;gt;
&amp;gt;&amp;gt; doc.engine_noise = &quot;roflroflrofl&quot;
=&amp;gt; &quot;roflroflrofl&quot;
&amp;gt;&amp;gt; doc.url = &quot;http://www.thinkgeek.com/images/products/zoom/roflcopter.jpg&quot;
=&amp;gt; &quot;http://www.thinkgeek.com/images/products/zoom/roflcopter.jpg&quot;
&amp;gt;&amp;gt; pp doc.save(db)
#&amp;lt;CouchObject::Response:0x4cd934
 @parsed_body=
  {&quot;_rev&quot;=&amp;gt;-1022899809, &quot;_id&quot;=&amp;gt;&quot;4D91304BE683851F0E18871ADA6749D8&quot;, &quot;ok&quot;=&amp;gt;true},
 @response=#&amp;lt;Net::HTTPCreated 201 Created readbody=true&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/notextile&gt;&lt;/p&gt;
&lt;p&gt;Get the same document by its id, and convert the response to a document (Just to illustrate it)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&gt;&gt; doc_we_created = db.get(doc.id).to_document
=&gt; #&lt;CouchObject::Document:0x14e8c38 @id=&quot;4D91304BE683851F0E18871ADA6749D8&quot;, @attributes={&quot;url&quot;=&gt;&quot;http://www.thinkgeek.com/images/products/zoom/roflcopter.jpg&quot;, &quot;engine_noise&quot;=&gt;&quot;roflroflrofl&quot;}, @revision=-1022899809&gt;
&gt;&gt; doc_we_created.engine_noise
=&gt; &quot;roflroflrofl&quot;
&gt;&gt; doc_we_created.engine_noise = &quot;ROFLROFLROFL&quot;
=&gt; &quot;ROFLROFLROFL&quot;
&gt;&gt; doc_we_created.save(db)
&gt;&gt; db.all_documents
=&gt; [{&quot;_rev&quot;=&gt;1353035433, &quot;_id&quot;=&gt;&quot;4D91304BE683851F0E18871ADA6749D8&quot;}]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sending a raw request to the db&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&gt;&gt; response = db.post(&quot;_temp_view&quot;, &lt;&lt;EOJS)
function(doc){ 
  if (doc.engine_noise.match(/rofl/i)) { 
    return doc
  }  
}
EOJS
# Our temp view query returns a list of rows matched documents
&gt;&gt; pp response.to_document.rows.first
{&quot;_rev&quot;=&gt;1353035433,
 &quot;_id&quot;=&gt;&quot;4D91304BE683851F0E18871ADA6749D8&quot;,
 &quot;value&quot;=&gt;
  {&quot;url&quot;=&gt;&quot;http://www.thinkgeek.com/images/products/zoom/roflcopter.jpg&quot;,
   &quot;_rev&quot;=&gt;1353035433,
   &quot;_id&quot;=&gt;&quot;4D91304BE683851F0E18871ADA6749D8&quot;,
   &quot;engine_noise&quot;=&gt;&quot;ROFLROFLROFL&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the API still needs some ironing out by means of more real world usage. You&amp;#8217;ll notice that there&amp;#8217;s no nice way of doing view of doing view &amp;#8220;queries&amp;#8221;. I really really want to create a more familiar Ruby DSL for defining views and sending of temporary views (like the one above). In particular it boils down to one or more of these things:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;A Ruby to Javascript converter (Like &lt;a href=&quot;http://po-ru.com/diary/convert-ruby-to-javascript/&quot;&gt;this&lt;/a&gt; perhaps).&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://errtheblog.com/post/11998&quot;&gt;Ambition&lt;/a&gt; is awesome, CouchDb is awesome, sounds like a perfect match to me. LINQ me up.&lt;/li&gt;
	&lt;li&gt;Make CouchDb use ruby instead of Javascript for views.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since I&amp;#8217;m lazy, I&amp;#8217;m starting from the bottom, more on that later. Because I really want to say:&lt;br /&gt;
&lt;notextile&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
db.select{|doc| doc.title =~ /foo/ }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/notextile&gt;&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s a &lt;a href=&quot;http://repo.or.cz/w/couchobject.git&quot;&gt;Git repository too&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ git clone git://gitorious.org/projects/couchobject/mainline.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Patches? Yes please, release your inner couch potato.&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-524&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://ruy.ca&quot;&gt;rubyruy&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-524&quot; title=&quot;&quot;&gt;Sep 15 at 07:15&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Having played around with rb2js conversion myself, I can tell you that it is no simple task, and that although it&amp;#8217;s possible it requires some pretty hefty concessions.&lt;/p&gt;
&lt;p&gt;The difference between how the two languages handle objects and &amp;#8220;classes&amp;#8221; lead to either inaccurate translation or needing to side-step JS&amp;#8217;s native object handling entirely using a bulky run-time (as rb2js has done):&lt;/p&gt;
rbSendMessage(RbRootObject.getConstant(&amp;#8216;Kernel&amp;#8217;), &amp;#8216;puts&amp;#8217;, [s]);
&lt;p&gt;Eeek!&lt;/p&gt;
&lt;p&gt;Before even delving into the nuances of inheritance and meta-classes, consider the simple case of translating ruby &amp;#8220;foo.bar&amp;#8221;. Is it JS &amp;#8220;foo.bar()&amp;#8221; or &amp;#8220;foo.bar&amp;#8221; ? No way to know until run-time, hence the nasty mess above.&lt;/p&gt;
&lt;p&gt;And after you managed to implement ruby&amp;#8217;s C-code as JS (which is essentially what the above is doing), you get to do it all again for all your favorite standard library classes!&lt;/p&gt;
&lt;p&gt;Aaaand after you did all THAT, you will have managed to implement one of the (relatively speaking yada yada) slowest production languages (ruby) into THE slowest production language (js) running in one of the slowest interpreters (spidermonkey). Ok i&amp;#8217;m exagerating &amp;#8211; but it&amp;#8217;s going to be slow ;) Now I know,  this thing will run via map/reduce over 500 commodity PCs  that each cost less then a happy meal and you&amp;#8217;ll only run it every now and then on an insert/update &amp;#8211; but still that&amp;#8217;s a positively silly amount of extra work for no good reason.&lt;/p&gt;
&lt;p&gt;I think option 3 is by far the best, especially since couchdb just shells out to spidermonkey and could just as easily shell out to ruby.&lt;/p&gt;
&lt;p&gt;Ambition IS pretty cool, but their approach deals with problems couchdb just doesn&amp;#8217;t have ;) &lt;br /&gt;
I think option 3&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-526&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-526&quot; title=&quot;&quot;&gt;Sep 15 at 11:07&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Indeed! Option 3 it was/it :)&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>CouchDb and CouchObjects</title>
     <link href="/articles/couchdb-and-couchobjects.html"/>
     <updated>2007-09-07T09:09:00+02:00</updated>
     <id>tag:theexciter.com,2007-09-07:1189148940</id>
     <content type="html">
       &lt;p&gt;I&amp;#8217;ve been watching &lt;a href=&quot;http://www.couchdbwiki.com/&quot;&gt;CouchDb&lt;/a&gt; for a while, but it wasn&amp;#8217;t until recently when it changed it transport format from XML to JSON that I got real interest in doing something with it, something I apparently &lt;a href=&quot;http://damienkatz.net/2007/09/couchdb_strikes.html&quot;&gt;wasn&amp;#8217;t alone about&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the things I&amp;#8217;m doing with it is a library called CouchObject, and one of the things it does is allowing you to serialize arbitrary ruby objects to and from CouchDb JSON documents by including a module and defining a few methods on your class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
class Bike
  include CouchObject::Persistable

  def initialize(wheels)
    @wheels = wheels
  end
  attr_accessor :wheels

  def to_couch
    {:wheels =&gt; @wheels}
  end

  def self.from_couch(attributes)
    new(attributes[&quot;wheels&quot;])
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;#to_couch&lt;/code&gt; method is the one that describes the format we want the class instances&amp;#8217; attributes serialized as a document in the CouchDb database:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
{ 
  &quot;_id&quot;: &quot;6FA2AFB684A93ECE77DEAAF52BB02565&quot;, 
  &quot;_rev&quot;: 1745167971, 
  &quot;attributes&quot;: {
    &quot;wheels&quot;: 4
  }, 
  &quot;class&quot;: &quot;Bike&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our #to_couch return result is stored in the &lt;code&gt;attributes&lt;/code&gt; key, and the class of the object is the &lt;code&gt;class&lt;/code&gt; key, for querying purposes (&lt;code&gt;_id&lt;/code&gt; and &lt;code&gt;_rev&lt;/code&gt; are CouchDb document attributes).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;from_couch&lt;/code&gt; class method is what describes how we should set up our new Bike object that we load from the database, the &lt;code&gt;attributes&lt;/code&gt; parameter is the &lt;code&gt;attributes&lt;/code&gt; key from the CouchDb document. In this case we just instantiate a new Bike with a number of wheels:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&gt;&gt; bike_4wd = Bike.new(4)
=&gt; #&amp;lt;Bike:0x6a0a68 @wheels=4&amp;gt;
&gt;&gt; bike_4wd.save(&quot;couchobject&quot;)
=&gt; {&quot;_rev&quot;=&gt;1745167971, &quot;_id&quot;=&gt;&quot;6FA2AFB623A93E0E77DEAAF59BB02565&quot;, &quot;ok&quot;=&gt;true}
&gt;&gt; bike = Bike.get_by_id(&quot;couchobject&quot;, bike_4wd.id)
=&gt; #&amp;lt;Bike:0x64846c @wheels=4&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I started on this last night there&amp;#8217;s still lots of little things to add, like better server and database semantics (in the above #save call, the argument is the database name and the host is hardcoded for now; not pretty).&lt;/p&gt;
&lt;p&gt;Another thing I&amp;#8217;ve been thinking about doing is a more formal way to describe &amp;#8220;models&amp;#8221;, something along the DataMapper pattern perhaps, but we&amp;#8217;ll see if I actually need it once I get the Persistable module some more features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I&amp;#8217;ve uploaded the &lt;a href=&quot;http://repo.or.cz/w/couchobject.git&quot;&gt;Git repository here&lt;/a&gt;, I want to add a few things before I do a release.&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-509&quot;&gt;
&lt;cite&gt;&lt;span&gt;Jon Wood&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-509&quot; title=&quot;&quot;&gt;Sep 07 at 13:56&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Excellent &amp;#8211; I&amp;#8217;ve been hoping for something a bit more robust for a while, but not got round to writing anything.&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;br /&gt;
Is the source available anywhere? I&amp;#8217;d love to have a play around with it &amp;#8211; I&amp;#8217;d especially like to see if the to_couch and from_couch methods could be dropped in standard use cases.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-510&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-510&quot; title=&quot;&quot;&gt;Sep 07 at 14:38&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
it&amp;#8217;ll be &lt;a href=&quot;http://rubyforge.org/projects/couchobject/&quot; rel=&quot;nofollow&quot; &gt;up on rubyforge&lt;/a&gt; soon, hopefully something after the weekend.&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;br /&gt;
My first approach was actually to just copy the instance variables in and out.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-511&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://marcora.caltech.edu&quot;&gt;Dado&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-511&quot; title=&quot;&quot;&gt;Sep 07 at 18:44&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Could the wheel attribute be put at the same level as the class, _id, and _rev attributes instead of in a &amp;#8220;attributes&amp;#8221; field? Is this a convention, something forced by CouchDb, or what? I believe it only add unnecessary clutter to the data structure.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-514&quot;&gt;
&lt;cite&gt;&lt;span&gt;Dan&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-514&quot; title=&quot;&quot;&gt;Sep 09 at 02:27&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Does it make sense to have an  ActiveRecord adapter for CouchDB?&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-515&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://anarchogeek.com&quot;&gt;rabble&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-515&quot; title=&quot;&quot;&gt;Sep 09 at 20:22&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
I&amp;#8217;ve been thinking about ActiveRecord and CouchDB, maybe ActiveCouch. :)&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s perhaps not the best fit. A rails like storage model is a great idea, but because couchdb doesn&amp;#8217;t have set scheams, we need to define that in our model. Perhaps an AR style definition of the fields would be a good addition to this lib.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-516&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;Johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-516&quot; title=&quot;&quot;&gt;Sep 10 at 06:01&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
@Dado: not enforced by CouchDb at all, I just think its nice to separate the metadata from the actual object data.&lt;/p&gt;
&lt;p&gt;@Dan/rabble: Yeah, after working a bit with the approach from the post here, I find that I need, or want rather, a more formal and descriptive model of my data, since my current wish isn&amp;#8217;t really to store arbitrary Ruby objects in CouchDb, but rather a domain-specific set of objects.&lt;br /&gt;
I don&amp;#8217;t think the ActiveRecord pattern at it&amp;#8217;s core maps too well to CouchDb&amp;#8217;s loose (schemaless) structure. But I&amp;#8217;d certainly want to do something along these lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
class Post &lt; CouchObject::Model
  couch_attribute :title, :body
  # could be typecasted to JSON types too
  couch_attribute :created_on, :Date 
end
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li id=&quot;comment-517&quot;&gt;
&lt;cite&gt;&lt;span&gt;Crabbers&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-517&quot; title=&quot;&quot;&gt;Sep 10 at 09:24&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
I&amp;#8217;m not fully up to speed with couch but ive been interested in the query side of it using javascript constructs to declare the map functions. could it be modelled in ruby in the same way with a block then the block serialised to a javascript construct? does that make sense?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
def find
   Couch.query do |doc|
      return doc unless doc.type == 'something'
   end 
end
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li id=&quot;comment-518&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-518&quot; title=&quot;&quot;&gt;Sep 10 at 09:32&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
there&amp;#8217;s &lt;a href=&quot;http://rb2js.rubyforge.org/&quot;&gt;ruby2js&lt;/a&gt;, never used it though. But more interesting is the fact that it looks fairly easy to change the query engine in CouchDb (it&amp;#8217;s essentially shelling out to spidermonkey right now).&lt;/p&gt;
&lt;p&gt;Next on my list is obviously to try and make the query engine use Ruby instad of Javascript :)&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-519&quot;&gt;
&lt;cite&gt;&lt;span&gt;Dan&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-519&quot; title=&quot;&quot;&gt;Sep 10 at 15:43&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
@rabble: Could the schema be derived from db/schema.rb in the rails app rather than the models?&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-520&quot;&gt;
&lt;cite&gt;&lt;span&gt;Maraby&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-520&quot; title=&quot;&quot;&gt;Sep 13 at 16:57&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Or perhaps a CouchDB document for CouchDB documents including the schema description, using CouchDB to describe itself (sort of).&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-522&quot;&gt;
&lt;cite&gt;&lt;span&gt;Kevin Teague&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-522&quot; title=&quot;&quot;&gt;Sep 14 at 10:50&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
If you are interested in existing implementations of formal schema definitions and doing data modelling uses pure dynamic objects, there has been a lot of different projects within the Python community.&lt;/p&gt;
&lt;p&gt;In Django the models contain the schema definition directly &amp;#8211; they&amp;#8217;ve experimented with schema inheritance but I think that&amp;#8217;s on-hold since they have an ORM to deal with:&lt;/p&gt;
&lt;p&gt;http://code.djangoproject.com/wiki/ModelInheritance&lt;/p&gt;
&lt;p&gt;In the Zope and Plone world we have been publishing persistent dynamic objects to the web for a long time using the Zope Object Database (ZODB), this is very similar to the method used by Gemstone &amp;#8211; implementation details are of course quite different, but the core concept is the same. Plone developed Archetypes which uses multiple inheritance to do schema inheritance, so mix-ins style schemas are possible. Archetypes does a good job, but like Django, Archetypes tightly couples Widget objets by embedding them within the schema, making code reuse hard. It has it&amp;#8217;s other warts too:&lt;/p&gt;
&lt;p&gt;http://plone.org/documentation/tutorial/borg/to-archetype-or-not-to-archetype&lt;/p&gt;
&lt;p&gt;When the core Zope developers did the whole let&amp;#8217;s-start-over-from-scratch thing after they had been working on Zope 2 for a long time, it took them a lot more years to produce Zope 3. The zope.interface and zope.schema packages in Zope 3 provides a very formal way of specifiying boths APIs and Schemas respectively. These are very well written packages. Schemas are considered an aspect of your API, since in the world of objects the two are tightly linked. Interfaces are just objects thought, and your model declares that it implements specific schemas. This is a much more pleasant way of doing it, IMO.&lt;/p&gt;
&lt;p&gt;http://pypi.python.org/pypi/zope.schema/&lt;/p&gt;
&lt;p&gt;Except of course Zope 3 requires a great deal of explicit configuration in the form of XML. Which isn&amp;#8217;t always the most fun stuff to write. Recently there has been a movement to create a way of working with Zope 3 that uses a lot of the same ideas where Ruby on Rails did a lot of innovation, such as convention over configuration. This project is called Grok and it makes Zope 3 a heck off a lot more fun to play with. It can also give you a glimpse of what Ruby on Rails might be like if it used an OODB:&lt;/p&gt;
&lt;p&gt;http://grok.zope.org&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-523&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://ruy.ca&quot;&gt;rubyruy&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-523&quot; title=&quot;&quot;&gt;Sep 15 at 06:52&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
@Maraby: That strikes me as throwing away the benefit of having schema-free storage. It makes much more sense (to me anyway) to define fields in one&amp;#8217;s model &amp;#8211; you know, close to the validation rules and other smartness that go with the object.&lt;/p&gt;
&lt;p&gt;In fact, it seems to me that CouchDb makes it far easier to embrace ruby&amp;#8217;s dynamism, since neither ruby nor couch really cares what you store in your attributes. The default behavior should be to just store your data and get on with it. If you want specific behavior, ruby already has many excellent ways of doing that, like actually defining the setter/getter methods with specific code, validation macros, type-casting macros etc etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-527&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://mega.blaix.com&quot;&gt;Justin&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-527&quot; title=&quot;&quot;&gt;Sep 21 at 15:21&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Looks great, but why not use yaml instead of manually creating the to/from methods?&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-528&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexiter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-528&quot; title=&quot;&quot;&gt;Sep 21 at 16:16&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Well, the idea was that there&amp;#8217;s no general way of knowing exactly how any particular object should map it attributes (could be into accessors, methods, class/instance/local variables etc etc), hence the mapping methods&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Distributed SCM == Goodness</title>
     <link href="/articles/distributed-scm-goodness.html"/>
     <updated>2007-08-10T01:44:00+02:00</updated>
     <id>tag:theexciter.com,2007-08-10:1186703040</id>
     <content type="html">
       &lt;p&gt;Ever since leaving &lt;a href=&quot;http://joyent.com/&quot;&gt;Joyent&lt;/a&gt; in favour of &lt;a href=&quot;http://bengler.no/&quot;&gt;Bengler&lt;/a&gt; back in february or so, I&amp;#8217;ve pretty much switched all my development over to using a distributed SCM (a topic I&amp;#8217;ve &lt;a href=&quot;http://application-error.com/&quot;&gt;tumbled&lt;/a&gt; about a lot lately too).&lt;/p&gt;
&lt;p&gt;While I&amp;#8217;ve looked at distributed SCMs before, they never really stuck until I was forced to using them full-time; at first it was Darcs since that&amp;#8217;s what they used when I switched jobs, coming from Subversion discovering distributed source control is a three step process;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Denial &amp;#8212; &lt;em&gt;I don&amp;#8217;t get it, why!?&lt;/em&gt;&lt;/li&gt;
	&lt;li&gt;Acknowledgment &amp;#8212; &lt;em&gt;Ok, so doing bigger features in different branches that&amp;#8217;s easily mergable into the mainline is really nice&lt;/em&gt;&lt;/li&gt;
	&lt;li&gt;Acceptance &amp;#8212; &lt;em&gt;it&amp;#8217;s the only way for me to work&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Later on I discovered Git and now most of my own local stuff is in Git repositories.&lt;/p&gt;
&lt;p&gt;The real dealbreaker when it comes to distributed SCMs and the open source world is pretty much summed up by Linus Thorvalds in his &lt;a href=&quot;http://youtube.com/watch?v=4XpnKHJAok8&quot;&gt;Git talk&lt;/a&gt;, where he says something along the lines of:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;if you want to implement something just clone the main repository and start hacking, and if people like your stuff better they can just pull from there instead&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, put this into the context of GForge installations such as sourceforge and rubyforge, where there are a huge number of inactive projects (for various reasons). If I wanted to hack a bit on a dead project I should be able to just register my own repository/branch (depending on SCM terminology) with the project and anyone interested in my new awesome updates could just pull from my repository instead. Or the project owner could just take a look at it and merge it back into trunk if he was happy with it.&lt;br /&gt;
Or more commonly, just when &amp;#8220;Bob&amp;#8221; and I work on a big feature and we&amp;#8217;ll just push and pull from each other without disturbing the mainline.&lt;/p&gt;
&lt;p&gt;Most projects are made up of several smaller internal and/or external projects (frameworks, libraries etc), and most often its different (from yours) real world usage that reveals bugs in someone elses code (or maybe even your own in case of &amp;#8220;internal&amp;#8221; projects). Wouldn&amp;#8217;t it be nice to be able to report these, but still have them in the context of your own project somehow so you knew when it was fixed (and other people knew it was already reported, just in a different project), add to that the multiple repositories in a project from above and you&amp;#8217;d most definitely need a way to report, watch and fix a bug in several different places.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://launchpad.net&quot;&gt;Launchpad&lt;/a&gt; seems to get a lot of this right right, but it&amp;#8217;s proprietary and fairly tightly coupled to &lt;a href=&quot;http://bazaar-vcs.org/&quot;&gt;Bazaar&lt;/a&gt;, which I for various reasons dislike. &lt;br /&gt;
But, I still want to use a issuetracker and source browser (and so on) that &lt;strong&gt;gets&lt;/strong&gt; all of this distributed stuff, both for my open things and for internal corporate stuff and for anyone else who may be needing it in those settings. Distributed means giving away some control (which you never had to begin with anyway aka the &lt;a href=&quot;http://hgbook.red-bean.com/hgbookch1.html#x5-150001.4.1&quot;&gt;forking non-issue&lt;/a&gt;), but it also means you loose that one-stop place to get an overview of what&amp;#8217;s going on.&lt;/p&gt;
&lt;p&gt;So that&amp;#8217;s what I&amp;#8217;m hacking on right now. It won&amp;#8217;t be &lt;a href=&quot;http://collaboa.org&quot;&gt;Collaboa&lt;/a&gt; that gets this functionality, mainly because I want to experiment a bit with this freely, but also because I don&amp;#8217;t think my new requirements will fit well with Subversion at all. Which is mainly why Collaboa now &lt;a href=&quot;http://lists.collaboa.org/pipermail/collaboa-talk/2007-August/000535.html&quot;&gt;has a new maintainer&lt;/a&gt; (can&amp;#8217;t wait to see what he does with it).&lt;br /&gt;
 &lt;br /&gt;
I&amp;#8217;m not really interested in cloning launchpad as such, but I do think they do get a lot of things right (and a good amount of things I don&amp;#8217;t like/need). So there&amp;#8217;s some similarities of concepts in my &amp;#8220;thing&amp;#8221;, but it&amp;#8217;s also a lot more geared towards my needs and workflow, things learnt from building Collaboa and using other issue trackers. And most importantly; being shown how development should work by using distributed SCMs.&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-506&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://joyeur.com&quot;&gt;Jason Hoffman&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-506&quot; title=&quot;&quot;&gt;Aug 11 at 08:33&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
We&amp;#8217;ve gone down the subversion to git (I&amp;#8217;ve used git-svn to hook into svn) and we&amp;#8217;ve actually arrived to bazaar.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-507&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-507&quot; title=&quot;&quot;&gt;Aug 11 at 12:52&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
I don&amp;#8217;t fancy bzr much, it feels even slower than (networked) svn to me. Slowness in the scm is something I&amp;#8217;ve grown slightly allergic too, esp. when darcs has one of its &amp;#8220;moments&amp;#8221; where it just decides to sit there and think.&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;br /&gt;
Git still wins the speed-faceoff compared to almost anything it seems, and the cmd-line ui is getting better (and friendlier) with each release. I still miss the interactive commit mode from darcs (gits interactive mode is nowhere close)&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Dynamic page caching with Nginx &amp; SSI</title>
     <link href="/articles/dynamic-page-caching-with-nginx-ssi.html"/>
     <updated>2007-05-21T22:49:00+02:00</updated>
     <id>tag:theexciter.com,2007-05-21:1179780540</id>
     <content type="html">
       &lt;p&gt;Zed Shaw mentioned this on the Rails podcast, but you see, &lt;a href=&quot;http://nginx.net/&quot;&gt;Nginx&lt;/a&gt; has this ability to do virtual SSI includes from another url. The documentation on it &lt;a href=&quot;http://wiki.codemongers.com/NginxHttpSsiModule&quot;&gt;is right here&lt;/a&gt; but half of it is in russian to confuse the spies. It&amp;#8217;s pretty straightforward stuff though, so lets play around with it for a bit. Like Zed says on the podcast, this kinda stuff would really be useful in situations where you can page cache pretty much the entire page, &lt;em&gt;except&lt;/em&gt; this little part that needs to be dynamic (like a &amp;#8220;Welcome &amp;lt;\%= current_user.name -\%&amp;gt;&amp;#8221;)&lt;/p&gt;
&lt;p&gt;Server Side Includes where these things we all used in the nineties for sprinkling random dynamic(-ish) stuff over our &lt;em&gt;homepages&lt;/em&gt;. Nginx has support for virtual includes that looks something like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&lt;!--# include virtual=&quot;/foo&quot; --&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;which will include whatever the url &lt;code&gt;/foo&lt;/code&gt; returns straight into the document where the include is defined (you can also throw it into another SSI block if you like, as the docs say).&lt;br /&gt;
Our little Rails testapp for this does about 205 req/s without any caching and using &lt;code&gt;render(:partial =&amp;gt; &quot;foo&quot;)&lt;/code&gt; for the &amp;#8220;welcome&amp;#8221; bit (I feel really bad mentioning Zed Shaw and stupid/naive statistics like the above in the same place, but the precise performance gains aren&amp;#8217;t that important. Think big picture stuff for now).&lt;br /&gt;
So here&amp;#8217;s a little helper for outputting the SSI in our template:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  def ssi_include(options={})
    #(options hash so we can pass in a SSI block target or whatever, YAGNI really).
    options.assert_valid_keys(:url)
    %Q{&lt;!--# include virtual=&quot;#{url_for(options[:url])}&quot; --&gt;}
  end
  
  # and in our view we'd use it like this:
  &lt;%= ssi_include :url =&gt; {:action =&gt; &quot;greet&quot;, :name =&gt; current_user.name} -%&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Not exactly rocket science. With that and page caching turned on, it&amp;#8217;s slightly faster (about 250 req/s), but not that much. Chances are we can cache those fragments in memcache to gain just a bit more.&lt;/p&gt;
&lt;p&gt;By now you&amp;#8217;ve hopefully realized that that the actual greet don&amp;#8217;t even need to come from rails to begin with; with a shared session storage and because Nginx forwards us the cookies to the virtual included url, we can just as well hook up a small Merb or Rack (or whatever) app to fetch the session_id and/or objects needed from the database (or cache storage) and display the correct text, all without the luggage from rails which we really don&amp;#8217;t need just to render some tiny text fragments. &lt;br /&gt;
Doing that in our stupid little test scenario here gives us just over 1000 req/s. That&amp;#8217;s a bit closer to the 5K req/s that nginx does for straight up html from disk (on my local machine) than the 205req/s we started off with. Yet another thing to pull out of the olde bag of tricks when you really do need it. I&amp;#8217;d be interested in hearing if others have experimented in practice with this kinda approach?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;update:&lt;/strong&gt; passing in the &amp;#8220;name&amp;#8221; querystring like the example code is about the worst example I could think of, since its static once it&amp;#8217;s written to the cached template, but cookies and such are still go&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Application Error: The Tumblelog</title>
     <link href="/articles/application-error-the-tumblelog.html"/>
     <updated>2007-03-07T00:21:02+01:00</updated>
     <id>tag:theexciter.com,2007-03-07:1173223262</id>
     <content type="html">
       &lt;p&gt;Since The Exciter updates are far between, I&amp;#8217;ve been running a tumblelog for the past two weeks called &lt;a href=&quot;http://www.application-error.com/&quot;&gt;Application Error&lt;/a&gt;. It&amp;#8217;s powered by the fabulous &lt;a href=&quot;http://www.tumblr.com&quot;&gt;tumblr&lt;/a&gt; (which I think is going to get huge). Expect mostly ruby-related links and other random stuff there.&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Ruby has a nice new Rack</title>
     <link href="/articles/ruby-has-a-nice-new-rack.html"/>
     <updated>2007-02-26T23:33:00+01:00</updated>
     <id>tag:theexciter.com,2007-02-26:1172529180</id>
     <content type="html">
       &lt;p&gt;As someone who&amp;#8217;ve used Rails and other ruby web frameworks for quite some time, plus my own dabbling in that domain, I&amp;#8217;ve seen how we all go and redo our own webserver interface, while those cheeky python kids keep nagging about WSGI.&lt;/p&gt;
&lt;p&gt;So, I&amp;#8217;ve been playing with &lt;a href=&quot;http://rack.rubyforge.org&quot;&gt;Rack&lt;/a&gt; recently, and it&amp;#8217;s quite inspired by WSGI. At its core, all a Rack application has to do is answer to a message for &lt;code&gt;call&lt;/code&gt; with the environment hash as the arguments and return a tuple looking like &lt;code&gt;[status_code, headers, body_array]&lt;/code&gt;, like this&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
require &quot;rack&quot;
class Foo
  def call(env)
    [200, {&quot;Content-Type&quot;=&amp;gt;&quot;text/plain&quot;}, [&quot;Hello world!&quot;]]
  end
end
HOST_AND_PORT = {:Host =&amp;gt; &quot;127.0.0.1&quot;, :Port =&amp;gt; 8080}
Rack::Handler::Mongrel.run(Foo.new, HOST_AND_PORT)
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;in fact, we could even replace that whole class with a &lt;code&gt;lambda&lt;/code&gt; that just returns the array:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
app = lambda { [200, {&quot;Content-Type&quot; =&amp;gt; &quot;text/plain&quot;}, [&quot;Hello lambda world!&quot;]] }
Rack::Handler::Mongrel.run(app, {:Host =&amp;gt; &quot;127.0.0.1&quot;, :Port =&amp;gt; 8080})
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;And we can run our marvelous application under mongrel. Now, a Rack application is basically anything that responds to #call, the nice thing about this is that we can chain Rack applications together, forming some middleware between our main app and the request being served by the browser. So if we call Rack::ShowExceptions#call before calling Foo#call, like this &lt;code&gt;Rack::ShowExceptions.new(Foo.new)&lt;/code&gt; we get some nice views from your nasty little exceptions.&lt;/p&gt;
&lt;p&gt;Why is this good? Because as a framework author you&amp;#8217;d be able to reuse middleware (Rack applications) from other applications, or as &lt;a href=&quot;http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html&quot;&gt;Chris puts it&lt;/a&gt;:&lt;/p&gt;
bq. Compare ???That upload handler you wrote for IOWA is really great, too bad I use Camping.??? with ???That upload handler you wrote for Rack works great for me too!???
&lt;p&gt;Rack is still a bit rough around the edges, and the API is stupidly simple (&amp;#8220;just #call it&amp;#8221;), however it does provide a very easy to use API.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://theexciter.com/files/cabinet.rb.txt&quot;&gt;Cabinet&lt;/a&gt; is a tiny little pseudo-framework I wrote while playing around with Rack last night. Knock yourself out. I think the slogan should be &amp;#8220;10x less productive&amp;#8221; or &amp;#8220;typing boring stuff over convention&amp;#8221;. Features Django inspired url dispatching, that&amp;#8217;ll make you type lots of regexens for every single thing. &amp;#8220;Ruby push-ups&amp;#8221; or something like that.&lt;/p&gt;
&lt;p&gt;Now, don&amp;#8217;t go write your own framework just yet, unless its merely for the sake of fooling around (like &amp;#8220;Cabinet&amp;#8221; was), ruby already has a bunch; Rails, Nitro, Camping, Merb, Ramaze and the oldskoolers like IOWA and Cerise.&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-502&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://drnicwilliams.com&quot;&gt;Dr Nic&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-502&quot; title=&quot;&quot;&gt;Mar 07 at 10:31&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Thanks for the intro to Rack; I just haven&amp;#8217;t found time to fiddle around with it yet.&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Oslo RUG: Distributed Ruby Slides</title>
     <link href="/articles/oslo-rug-distributed-ruby-slides.html"/>
     <updated>2007-01-04T11:15:00+01:00</updated>
     <id>tag:theexciter.com,2007-01-04:1167905700</id>
     <content type="html">
       &lt;p&gt;Last night I gave a presentation at our small (but awesome) &lt;a href=&quot;http://rubyonrails.citycita.org/22498/&quot;&gt;Oslo Ruby group&lt;/a&gt; about distributed programming with Ruby. &lt;br /&gt;
Topics of the talk included DRb, Rinda::TupleSpace, Rinda::RingServer, tin-can telephones, Joyent&amp;#8217;s &lt;a href=&quot;http://www.bingodisk.com&quot;&gt;Bingo!&lt;/a&gt;, &lt;code&gt;set_trace_func&lt;/code&gt; and a brief &lt;a href=&quot;http://www.sun.com/servers/x64/x4500/&quot;&gt;thumper&lt;/a&gt; server-porn interlude.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://theexciter.com/files/oslo_rug_drb.pdf&quot;&gt;Here are the slides&lt;/a&gt; (900kb). Beware though, they&amp;#8217;re in an abomination of norwegian, swedish and english&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-499&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://www.eribium.org&quot;&gt;Alex MacCaw&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-499&quot; title=&quot;&quot;&gt;Jan 04 at 12:21&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Another option is a mysql job server (I wrote this because I found that, for windows at least, Backgroundrb was a bit buggy). Job&amp;#8217;s get queued, and then every so often, a cron job checks to see if there are any new jobs. Optimistic Locking seems to prevent duplication. If you&amp;#8217;re interested in seeing the code, please contact me.&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-500&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://theexciter.com&quot;&gt;johan&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-500&quot; title=&quot;&quot;&gt;Jan 05 at 00:12&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
yup, that&amp;#8217;s another of doing it if the jobs are of that kind. A DRb setup gives the advantage of being able to talk back and forth.&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;br /&gt;
We don&amp;#8217;t actually use backgroundrb, since it was Thread based when we started, but our lib works mostly the same way conceptually.&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Flashing the Nokia 770</title>
     <link href="/articles/flashing-the-nokia-770.html"/>
     <updated>2006-10-23T01:43:00+02:00</updated>
     <id>tag:theexciter.com,2006-10-23:1161560580</id>
     <content type="html">
       &lt;p&gt;I recently bought a &lt;a href=&quot;http://www.nokia.com/770&quot;&gt;Nokia 770&lt;/a&gt; &amp;#8220;Internet Tablet&amp;#8221; as they call it. No GSM/3G, just WLAN, Bluetooth and USB. And it runs a Debian linux offspring out of the box so it&amp;#8217;s very hackable.&lt;/p&gt;
&lt;p&gt;And that was kind of the problem for me; I hacked around too much and in a moment of clear stupidity I changed the setuid bit on `sudo` in a freak typing accident of a chmod gone wrong. &lt;em&gt;Oops&lt;/em&gt;. &lt;br /&gt;
So I locked myself out of a lot of fun times, including the package manager not working, and without any ssh server installed (yet) and no clear way of making it boot in single-user mode I decided to reflash the whole thing; returning it to its factory settings. Here&amp;#8217;s a readers digests of my approach using OSX. It&amp;#8217;s based on things found mostly &lt;a href=&quot;http://maemo.org/maemowiki/Flasher_tool_usage&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://www.math.ucla.edu/~jimc/nokia770/hacking.html#flasher&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Flashing your device&lt;/h2&gt;
&lt;p&gt;I downloaded the &lt;em&gt;flasher.macosx&lt;/em&gt; version if &lt;a href=&quot;http://www.maemo.org/downloads/d3.php&quot;&gt;the flasher&lt;/a&gt; along with the &lt;a href=&quot;http://www.maemo.org/downloads/nokia_770&quot;&gt;it2006 OS image/Maemo 2.0&lt;/a&gt; image&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt; you may want to backup your settings, bookmarks and whatnots using the builtin backup software. Then we do a complete fresh install by flashing the device with the image:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
$ ./flasher-2.0.macosx -F SU-18_2006SE_1.2006.26-8_PR_F5_MR0_ARM.bin -f -R
flasher v0.8.1 (Jun 22 2006)

SW version in image: SU-18_2006SE_1.2006.26-8_PR_MR0
Image '2nd', size 8704 bytes
Image 'secondary', size 87040 bytes
Image 'xloader', size 13824 bytes
Image 'initfs', size 1890304 bytes
Image 'kernel', size 1266560 bytes
Image 'rootfs', size 60030976 bytes
USB device found found at bus 003, device address 002-0421-0105-00-00
Found device SU-18, hardware revision 1802
[..lots of fun stuff..]
100% (58624 of 58624 kB, avg. 814 kB/s)
Finishing flashing... done
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;And then when the thing boots back up we have to go through those fun initial settings again (language, datetime etc).&lt;/p&gt;
&lt;h3&gt;Making a smaller initfs&lt;/h3&gt;
&lt;p&gt;OK, so that was easy, except now our initfs partition is completely full, so we can&amp;#8217;t install packages such as &amp;#8220;becomeroot&amp;#8221; and other fun things, so we have to find a stripped down version if that image. Luckily people smarter than me have &lt;a href=&quot;http://maemo.org/maemowiki/BootMenu&quot;&gt;already figured that out&lt;/a&gt;.&lt;br /&gt;
(There&amp;#8217;s also a smaller image &lt;a href=&quot;http://www.math.ucla.edu/~jimc/nokia770/hacking.html&quot;&gt;here&lt;/a&gt; but I couldn&amp;#8217;t get it to work as expected).&lt;/p&gt;
&lt;p&gt;The smaller initfs comes in the form of a binary xdelta diff, but I had some issues with the `xdelta` dependencies, so in the end I had to install fink, just for the sake of getting xdelta in a quick way. So:&lt;/p&gt;
&lt;p&gt;First &amp;#8220;unpack&amp;#8221; the image from the device:&lt;br /&gt;
  &lt;pre&gt;&lt;br /&gt;
  &lt;code&gt;
  $ mkdir it2006-unpacked
  $ cd it2006-unpacked
  $ ../flasher-2.0.macosx -F ../SU-18_2006SE_1.2006.26-8_PR_F5_MR0_ARM.bin -u
  [...]
  Image 'initfs', size 1890304 bytes
  [...]
  Unpacking initfs image to file 'initfs.jffs2'...
  [...]
  &lt;/code&gt;&lt;br /&gt;
  &lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Then we get the smaller initfs image and apply the xdelta&lt;br /&gt;
  &lt;pre&gt;&lt;br /&gt;
  &lt;code&gt;
  $ curl -O http://fanoush.webpark.cz/maemo/initfs.bootmenu.it2006.tgz
  $ tar xzf initfs.bootmenu.it2006.tgz&lt;/p&gt;
# Apply the xdelta
$ /sw/bin/xdelta patch initfs.bootmenu.xdelta initfs.jffs2 initfs.bootmenu.jffs2
# flash it onto the 770:
$ ../flasher-2.0.macosx &amp;#8212;initfs initfs.bootmenu.jffs2 -f -R
[&amp;#8230;]
Sending initfs image (1537 kB)&amp;#8230;
100% (1537 of 1537 kB, avg. 859 kB/s)
Flashing initfs&amp;#8230; done.
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;And we&amp;#8217;re laughing. But we&amp;#8217;re going to laugh even more once we install Ruby 1.8.4 from &lt;a href=&quot;http://maemo.mmapps.net/&quot;&gt;here&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.flickr.com/photos/johan-s/275198370/&quot;&gt; &lt;img src=&quot;http://static.flickr.com/91/275198370_bcfe3fc126.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-496&quot;&gt;
&lt;cite&gt;&lt;span&gt;bbb&lt;/span&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-496&quot; title=&quot;&quot;&gt;Nov 04 at 03:28&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
bollywood&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;comment-497&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://www.powertrip.co.za&quot;&gt;Jacques Marneweck&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-497&quot; title=&quot;&quot;&gt;Nov 28 at 22:49&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
Is there any way to prevent ruby from trying to load a whole huge file into memory for the duration of the upload?&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;br /&gt;
29985 root         1   4    0   479M   478M sbwait   0:02  0.00% ruby&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>Bingo!</title>
     <link href="/articles/bingo.html"/>
     <updated>2006-09-15T13:09:00+02:00</updated>
     <id>tag:theexciter.com,2006-09-15:1158318540</id>
     <content type="html">
       &lt;p&gt;A few days ago &lt;a href=&quot;http://www.joyent.com&quot;&gt;we&lt;/a&gt; launched &lt;a href=&quot;http://www.bingodisk.com/&quot;&gt;Bingodisk&lt;/a&gt;, which is a 100GB WebDAV powered disk in the sky, with a public folder. Useful for storing just about anything and serving it up (or not) to the public.&lt;/p&gt;
&lt;div class=&quot;image-right&quot;&gt;
&lt;p&gt;&lt;img src=&quot;http://johan.bingodisk.com/public/bingo_logo_trans.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Building Bingo! is a lot of fun, the bingodisk you&amp;#8217;ll get is sitting on a &lt;a href=&quot;http://www.sun.com/servers/x64/x4500/&quot;&gt;&amp;#8216;Thumper&amp;#8217;&lt;/a&gt; which is just a lovely piece of monster storage hardware and the actual frontend application is a Rails application that talks to the Thumpers via a distributed interface I wrote.&lt;/p&gt;
&lt;p&gt;The really nice thing is that it uses a &lt;a href=&quot;http://webdav.org/&quot;&gt;WebDAV&lt;/a&gt; interface which means that it&amp;#8217;s possibly to mount it as a disk in almost every modern operating system (as usual, we had to jump through hoops to get it working properly in windows, but it does). WebDAV also supports things such as resource locking, and easily moving and/or copying resources.&lt;br /&gt;
Now, WebDAV is a set of HTTP extensions, which means that it&amp;#8217;s easy to talk to from an application. Let&amp;#8217;s see how we could do that from a Ruby script using &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/index.html&quot;&gt;Net::HTTP&lt;/a&gt;. Unfortunately Net::HTTP doesn&amp;#8217;t support Digest authentication out of the box (Basic auth won&amp;#8217;t work with Bingo), but we can fairly easily add that, based on a snippet I found by Eric Hodel.&lt;/p&gt;
&lt;p&gt;First you&amp;#8217;ll need to get &lt;a href=&quot;http://johan.bingodisk.com/public/code/net_digest_auth.rb&quot;&gt;this file&lt;/a&gt;. It adds a &lt;code&gt;digest_auth&lt;/code&gt; method to Net::HTTP.&lt;/p&gt;
&lt;h2&gt;Finding properties&lt;/h2&gt;
&lt;p&gt;To get a list of the resources available we&amp;#8217;ll use the PROPFIND HTTP method, which will return a (&lt;a href=&quot;http://johan.bingodisk.com/public/code/dav-profind.txt&quot;&gt;rather large&lt;/a&gt;) chunk of XML, containing locking info, resource name and meta such as size and mtime. Here&amp;#8217;s a script that lists the files at a given path:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
  # list.rb
  require 'net_digest_auth'
  require 'rexml/document'
  include REXML

  abort(&quot;Usage #{$0} &amp;lt;username&amp;gt; &amp;lt;password&amp;gt;&quot;) unless ARGV.size==2

  ALLPROPS = &amp;lt;&amp;lt;EOS
  &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;
  &amp;lt;D:propfind xmlns:D=&quot;DAV:&quot;&amp;gt;
    &amp;lt;D:allprop/&amp;gt;
  &amp;lt;/D:propfind&amp;gt;
  EOS

  url = URI.parse(&quot;http://johan.bingodisk.com/bingo/&quot;)
  Net::HTTP.start(url.host) do |http|
    res = http.head(url.request_uri)
    req = Net::HTTP::Propfind.new('/bingo/tmp/', {'Depth' =&amp;gt; '1'})
    req.digest_auth(ARGV[0], ARGV[1], res)
    response = http.request(req, ALLPROPS)
  
    puts &quot;#{response.code} #{response.message}\n&quot;
    puts
  
    Document.new(response.body).elements.each(&quot;//D:response&quot;) do |r| 
      puts r.elements[&quot;D:href&quot;].text
    end
  end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Apologies for the textile parsing error with the ARGV index&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And the output:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
  $ ruby list.rb johan@bingodisk.com secret
  207 Multi-Status

  /bingo/tmp/
  /bingo/tmp/mch.jpg
  /bingo/tmp/TextMateBook-beta.pdf
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;So what this does is that it first requests HEAD to get the things needed for the digest auth, then we create a new &lt;code&gt;Net::HTTP::Propfind&lt;/code&gt; request instance and use the &lt;code&gt;digest_auth_&lt;/code&gt; method to set the user and password from the arguments given. &lt;br /&gt;
Then fire off the request with a snippet (the &lt;code&gt;ALLPROPS&lt;/code&gt; constant) of XML telling the DAV server we want to get all the props.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;ll get back the &amp;#8220;207 Multi-Status&amp;#8221; HTTP request code and the XML describing the properties of the resources, on which it does a XPath query using REXML to get the filenames (the &lt;code&gt;D:href&lt;/code&gt; element).&lt;/p&gt;
&lt;h2&gt;PUTting resources on the disk&lt;/h2&gt;
&lt;p&gt;Now let&amp;#8217;s upload something, as expected we&amp;#8217;ll want to use the PUT method, here&amp;#8217;s a script that takes the username, password and a path for a file to upload into &lt;code&gt;/bingo/public/code/&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
  # upload.rb
  require 'net_digest_auth'

  abort(&quot;Usage: #{$0} &amp;lt;username&amp;gt; &amp;lt;password&amp;gt; &amp;lt;path/to/file/to/upload&amp;gt;&quot;) unless ARGV.size==3

  if File.exists?(ARGV[2])
    url = URI.parse(&quot;http://johan.bingodisk.com/bingo/&quot;)
    Net::HTTP.start(url.host) do |http|
      res = http.head(url.request_uri)
      req = Net::HTTP::Put.new(&quot;/bingo/public/code/#{File.basename(ARGV[2])}&quot;)
      req.digest_auth(ARGV[0], ARGV[1], res)
      response = http.request(req, File.read(ARGV[2]))
      puts response.code + &quot; &quot; + response.message
    end
  else
    puts &quot;No such file #{ARGV[2].inspect}&quot;
  end
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;By running the script we&amp;#8217;ll upload the net_digest_auth.rb file you:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;/code&amp;gt;
  $ ruby upload.rb johan@bingodisk.com secret net_digest_auth.rb 
  201 Created
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Nice and easy.&lt;/p&gt;
&lt;p&gt;WebDAV might feel a bit more &amp;#8220;bulky&amp;#8221; than a straight up RESTful interface, but it&amp;#8217;s really not that bad and the fact that it&amp;#8217;s so well-supported in existing client programs is just friggin&amp;#8217; sweet.&lt;/p&gt;
&lt;hr class=&quot;comment-divider&quot; /&gt;
&lt;div class=&quot;commentsblock&quot;&gt;
&lt;h3 id=&quot;comments&quot;&gt;Comments:&lt;/h3&gt; 
&lt;ol class=&quot;commentlist&quot;&gt;
&lt;li id=&quot;comment-489&quot;&gt;
&lt;cite&gt;&lt;a href=&quot;http://www.fngtps.com&quot;&gt;Thijs van der Vossen&lt;/a&gt;&lt;/cite&gt; Says:
&lt;p&gt;&lt;small class=&quot;commentmetadata&quot;&gt;&lt;a href=&quot;#comment-489&quot; title=&quot;&quot;&gt;Sep 15 at 22:53&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
You could also use http://rubyforge.org/projects/httpauth/ for easy HTTP Digest Authentication.&lt;/p&gt;
&lt;/li&gt;
&lt;p&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>On Redmond developer happiness</title>
     <link href="/articles/on-redmond-developer-happiness.html"/>
     <updated>2006-03-27T09:26:00+02:00</updated>
     <id>tag:theexciter.com,2006-03-27:1143444360</id>
     <content type="html">
       &lt;p&gt;&lt;a href=&quot;http://minimsft.blogspot.com/2006/03/vista-2007-fire-leadership-now.html&quot;&gt;This little thing&lt;/a&gt; has been popping up in my feed reader again and again during the past week.&lt;/p&gt;
&lt;p&gt;The post has over 400 comments, many of them from Microsoft employees. Let&amp;#8217;s just say about 150 of those are from actual MSFT employees, and let&amp;#8217;s say, for the sake of argument, that 100 of those are actually working on code inside Redmond.&lt;/p&gt;
&lt;p&gt;I don&amp;#8217;t care how small a percentage 100 employees is out of your total number, but when you have 100+ employees not happy about either their job, the way their company is run or their boss/manager; then you have a problem that will turn into an infectious disease, if it hasn&amp;#8217;t already.&lt;/p&gt;
&lt;p&gt;Just think about how many &lt;abbr title=&quot;lines of code&quot;&gt;LOC&lt;/abbr&gt; an employee potentially pushes out over a year, or how many &lt;em&gt;small&lt;/em&gt; or big features (end-user facing or not) they implement over that year. Now, I don&amp;#8217;t know about you, but I produce a heck of a lot better code when I&amp;#8217;m happy and passionate about my job than when I&amp;#8217;m not. So in my eyes the comments on that post say they got 100+ coders turning out bad code, all because of their management.&lt;/p&gt;
&lt;p&gt;I am glad neither my professional or personal computing experience depends on Microsoft in any way whatsoever.&lt;/p&gt;
      </content>
   </entry>
  
   <entry>
     <title>On ActionMailer fixtures</title>
     <link href="/articles/on-actionmailer-fixtures.html"/>
     <updated>2006-03-15T15:44:00+01:00</upda