<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>cfis</title>
    <link>http://cfis.savagexi.com</link>
    <atom:link rel="self" type="application/rss+xml" href="http://cfis.savagexi.com/articles.rss"/>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Charlie Savage's Blog</description>
    <item>
      <title>Building Ruby 1.9.1 on Windows</title>
      <description>&lt;p&gt;As &lt;a href="http://www.infoq.com/news/2009/03/ruby-19-adoption"&gt; 			noted&lt;/a&gt; 			&lt;a href="http://antoniocangiano.com/2009/03/23/rubys-biggest-challenge-for-2009/"&gt; 			else&lt;/a&gt; 			&lt;a href="http://houseofpostmodern.wordpress.com/2009/02/06/the-push-to-ruby-19/"&gt; 			where&lt;/a&gt;, ruby 1.9.1 hasn't exactly bounded out of the gate.  			That's not particularly surprising, considering 1.9.1 has been  			available for only a couple of months and requires changes to  			existing code. In addition, there are a number of incompatible  			gems, giving rise to the &lt;a href="http://isitruby19.com/"&gt;isitrub19y&lt;/a&gt;  			website as a clearing house of information. So despite the  			great efforts from the Rails team, the rest of the community is  			still lagging behind.&lt;/p&gt;
&lt;p&gt;That's particularly true on Windows, where a new one-click installer isn't    yet available. According to the latest market share   &lt;a href="http://marketshare.hitslink.com/operating-system-market-share.aspx?qprid=8"&gt;   stats&lt;/a&gt; from Net Applications, Windows controls 88% of the desktop market. I    have no idea how many Ruby installations exist, and how they are divided    by operating system. But looking at &lt;a href="http://rubyforge.org/"&gt;   RubyForge&lt;/a&gt;, by far and away the most popular download of all times is the    Windows one-click installer with over 3 million downloads.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blog.mmediasys.com/"&gt;Luis Lavena&lt;/a&gt; has taken over    stewardship of the one-click installer, and clearly needs a bit of help.    So although I have very little free time, I offered to pitch in as I could.    While Luis is concentrating on putting together a new version of the one-click    installer using Mingw and msys, I thought I could help out by putting 1.9.1    through its paces on Windows.&lt;/p&gt;
&lt;p&gt;My basic approach was to simply start with the basics:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Build ruby with Visual Studio 2008&lt;/li&gt;
    &lt;li&gt;Build the default extensions and libraries Ruby uses (zlib, iconv,  	openssl, etc)&lt;/li&gt;
    &lt;li&gt;Run Ruby's unit tests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That was almost a month ago. Thirty-nine patches later (I have no    doubt Nobu is getting sick of me), I just about have Ruby 1.9.1's test suite    running on Windows. There a still a few remaining issues, in particular    a couple of imap tests that hang.&lt;/p&gt;
&lt;p&gt;As for Visual Studio, I'm using it for two reasons. First, it has a    lights-out debugger that makes it much easier to track down and fix problems.    Second, its lets you compile instrumented executable and libraries that can    detect incorrect API usage, heap corruption, stack corruption and mismatched    calling conventions.&lt;/p&gt;
&lt;p&gt;It quickly became obvious that no one had ever done that with Ruby,    because it turned up a whole host of issues. For example, the dl    extension used the cdecl calling convention to call the Windows API instead of    stdcall. Or that there were a set of memory leaks in printf/sprintf.&lt;/p&gt;
&lt;p&gt;The other thing that was bothersome was the huge number of compiler    warnings generated by building Ruby.   &lt;a href="http://redmine.ruby-lang.org/issues/show/1254"&gt;See&lt;/a&gt; for your self    - and then realize the original list doesn't include any of the warnings    generated by building Ruby's extensions. Cleaning up the warnings took a    number of patches, but at this point most of them have been fixed. And    all credit to Nobu for working through my patches, fixing them and applying    them since my knowledge of the Ruby runtime is fairly limited, thereby causing    most of my patches to not be quite right.&lt;/p&gt;
&lt;p&gt;Anyway, since its not all that obvious how to build Ruby on Windows (with    Visual Studio or Mingw), I'll see if I can put together a few posts that    describe how to do it for anyone who wants to roll their own.&lt;/p&gt;

</description>
      <pubDate>Sat, 28 Mar 2009 22:29:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:5fc93acd-6756-4c01-a448-773e8f5f7908</guid>
      <comments>http://cfis.savagexi.com/2009/03/28/building-ruby-1-9-1-on-windows#comments</comments>
      <category>Ruby</category>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=508</trackback:ping>
      <link>http://cfis.savagexi.com/2009/03/28/building-ruby-1-9-1-on-windows</link>
    </item>
    <item>
      <title>libxml-ruby 1.1.3 - Boosting Performance</title>
      <description>&lt;p&gt;I'm happy to announce the release of 			&lt;a href="http://libxml.rubyforge.org/rdoc/index.html"&gt;libxml-ruby&lt;/a&gt;  			1.1.3. Besides including the usual assortment of new features  			and bug &lt;a href="http://libxml.rubyforge.org/rdoc/files/CHANGES.html"&gt;fixes&lt;/a&gt;,  			this release also includes a speed boost of roughly 10% to 20%.&lt;/p&gt;
&lt;p&gt;This resulted from 			&lt;a href="http://www.rubyinside.com/ruby-xml-performance-benchmarks-1641.html"&gt; 			RubyInside's&lt;/a&gt; recent post summarizing the performance of Ruby  			parsers. As expected, &lt;a href="http://libxml.rubyforge.org/"&gt; 			libxml-ruby&lt;/a&gt; blew away 			&lt;a href="http://wiki.github.com/why/hpricot"&gt;Hpricot&lt;/a&gt; and 			&lt;a href="http://www.germane-software.com/software/rexml/"&gt;REXML&lt;/a&gt;  			in pure parsing speed (which of course is a simplistic view of what  			is important in an xml processor, but nevertheless still important).  			But it consistently finished a bit behind 			&lt;a href="http://github.com/tenderlove/nokogiri/tree/master"&gt;Nokogiri&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was a bit surprised by that since libxml-ruby and Nokogiri use the &lt;a href="http://xmlsoft.org/"&gt; 			libxml2&lt;/a&gt; library as their parsing engine. Since the  			specific test cases almost exclusively tested parsing, the two  			extensions should have identical run times.&lt;/p&gt;
&lt;p style="width: 515px;"&gt;Since the times were different, then the  			obvious conclusion was that the two extensions were  			using different libxml2 APIs or using different settings. I  			suspected the second, but when  			investigating performance you never know beforehand.&lt;/p&gt;
&lt;p&gt;Not to bore everyone with the nitty-gritty details of using  			libxml2, but when looking into the first test, parsing an in-memory  			string, it didn't look there was much difference in API calls.&lt;/p&gt;
&lt;p&gt;For libxml-ruby:&lt;/p&gt;
&lt;p&gt;&lt;font size="2"&gt;
&lt;pre&gt;&lt;tt&gt;xmlCreateMemoryParserCtxt&lt;br /&gt;xmlParseDocument&lt;/tt&gt;&lt;/pre&gt;
&lt;p&gt;For Nokogiri:&lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;xmlReadMemory&lt;br /&gt;  -&amp;gt; xmlCreateMemoryParserCtxt&lt;br /&gt;  -&amp;gt; xmlDoRead&lt;br /&gt;     -&amp;gt; xmlParseDocument&lt;/tt&gt;&lt;/pre&gt;
&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;So that didn't solve the mystery.&lt;/p&gt;
&lt;p&gt;The next possibility was xmlDoRead was modifying the libxml2  			parser context. Now a libxml2 parser context is a beast of a thing -  			for those brave souls who want to take a peek, its defined in  			libxml2's online 			&lt;a href="http://xmlsoft.org/html/libxml-tree.html#xmlParserCtxt"&gt; 			documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Working through the options one-by-one, I finally found the  			culprit, an obscure field in the structure:&lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;int	dictNames	: Use dictionary names for the tree&lt;/tt&gt;&lt;/pre&gt;
&lt;p&gt;What this setting controls is whether libxml2 uses a dictionary  			to cache strings it has previously parsed. Caching strings  			makes a big difference, so by default it should be enabled.  			That is now the case with libxml-ruby 1.1.3 and higher.&lt;/p&gt;
&lt;p&gt;Rerunning the published benchmarks now shows libxml-ruby and Nokogiri to    have equivalent performance. If you run the tests yourself, beware though. The    order in which the extensions are tested changes the results. Whichever    extension is tested first will always be faster, at least on my Fedora 10 box.    I assume that's because the first parser has more memory available to it when    the test begins and therefore invokes Ruby's garbage collector a few times    less.&lt;/p&gt;

</description>
      <pubDate>Sat, 21 Mar 2009 23:19:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:03029801-9d84-486c-9260-81803237b7d1</guid>
      <comments>http://cfis.savagexi.com/2009/03/21/libxml-ruby-1-1-3-boosting-performance#comments</comments>
      <category>Ruby</category>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=507</trackback:ping>
      <link>http://cfis.savagexi.com/2009/03/21/libxml-ruby-1-1-3-boosting-performance</link>
    </item>
    <item>
      <title>libxml-ruby reaches 1.0!</title>
      <description>			&lt;p&gt;A mere seven years after its inception, libxml-ruby has finally reached version 1.0.
			  libxml-ruby provides ruby, via the
			&lt;a href="http://www.xmlsoft.org/"&gt;libxml2&lt;/a&gt; libary, the super fast, 
			  feature rich xml parser that is has sorely lacked.&lt;/p&gt;
			&lt;p&gt;Last year I
			&lt;a href="http://cfis.savagexi.com/2008/07/16/resurrecting-libxml-ruby"&gt;
			posted&lt;/a&gt; about the resurrection of the project, and since then 
			we&amp;#39;ve made enormous progress. The 1.0 release marks the culmination 
			of this work, and comes with tons of goodies:&lt;/p&gt;
			&lt;ul&gt;
			  &lt;li&gt;Ruby 1.9.1 support&lt;/li&gt;
			  &lt;li&gt;Out of the box support for OS X 10.5 and MacPorts&lt;/li&gt;
			  &lt;li&gt;Greatly expanded 
			  &lt;a href="http://libxml.rubyforge.org/rdoc/index.html"&gt;documentation&lt;/a&gt;&lt;/li&gt;
			  &lt;li&gt;Much better test coverage&lt;/li&gt;
			  &lt;li&gt;A nice, clean API that makes it easy to do simple things, but 
			  provides all the power of libxml2 if you need it &lt;/li&gt;
			&lt;/ul&gt;
			&lt;p&gt; Not to mention that libxml-ruby is blindingly fast and 
			incredibly feature rich (see my
			&lt;a href="http://cfis.savagexi.com/2008/07/16/resurrecting-libxml-ruby"&gt;
			post&lt;/a&gt; from last year for all the details), making it the choice 
			for a number of high-traffic websites.&lt;/p&gt;
			&lt;p&gt;So give them a try - its as easy to install as: &lt;/p&gt;
			&lt;pre&gt;&lt;tt&gt;gem install libxml-ruby&lt;/tt&gt;&lt;/pre&gt;
			&lt;p&gt;And if you feel like polishing your ruby, xml, or C skills, come 
			join the community! &lt;/p&gt;


</description>
      <pubDate>Tue, 10 Mar 2009 23:52:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:69e9097b-f9f5-4b4a-b231-4904b71dcee2</guid>
      <comments>http://cfis.savagexi.com/2009/03/10/libxml-ruby-reaches-1-0#comments</comments>
      <category>Ruby</category>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=506</trackback:ping>
      <link>http://cfis.savagexi.com/2009/03/10/libxml-ruby-reaches-1-0</link>
    </item>
    <item>
      <title>Window Functions and the Connected Workspace</title>
      <description>&lt;p&gt;One of the great new features of the upcoming postgresql 8.4 release is  		the addition of window functions. Previously limited to enterprise  		databases such as Oracle and DB2, they open up a whole new world of  		functionality to sql queries.&lt;/p&gt;
&lt;p&gt;Window functions are one of the more obscure parts of the 		&lt;a href="http://en.wikipedia.org/wiki/SQL"&gt;sql&lt;/a&gt; standard, so you may  		never have heard of them. In a nutshell, they let you perform calculations based  		on the current record and its set of related records. This turns out to be quite useful. A good place to find out more information is the postgresql documentation, which does a good job of 		&lt;a href="http://developer.postgresql.org/pgdocs/postgres/tutorial-window.html"&gt; 		explaining&lt;/a&gt; some of the more common use cases.&lt;/p&gt;
&lt;h3&gt;Workplace of the Future&lt;/h3&gt;
&lt;p&gt;My introduction to window functions was almost five years ago while  		doing a project for &lt;a href="http://www.ubisense.net/"&gt;Ubisense&lt;/a&gt;. Ubisense sells indoor tracking  		systems, based on ultra-wideband, that can locate tags within 6  		inches.&lt;/p&gt;
&lt;p&gt;For one of projects, we worked on Cisco's Connected Workspace. The  		Connected Workspace was designed to see if office space could be laid  		out in a way to increase worker happiness and productivity. To do  		this, Cisco took all the cubicles out of the main floor of one of its  		buildings and replaced it with a fairly radical design. Roughly half of the floor  		was made into a a large open open space with  		individual and group desks. The remainder of the floor was split  		between a large kitchen with a really nice eating room and offices that  		ranged in size from 1 to 12 people. Here is a picture of the main floor area (courtesy Cisco Systems):&lt;/p&gt;
&lt;p&gt;&lt;img src="http://cfis.savagexi.com/files/cisco_connected_workspace.png" alt="Cisco Connected Workspace" /&gt;&lt;/p&gt;
&lt;p&gt;For a few more pictures, check out Cisco's 		&lt;a href="http://www.cisco.com/web/about/ciscoitatwork/downloads/ciscoitatwork/pdf/Cisco_IT_Case_Study_Connected_Workplace_POC_print.pdf"&gt; 		presentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The idea was that employees could sit wherever they wanted, there were  		no assigned seats. If employees needed to collaborate they could  		work in the open areas, if they needed privacy they could grab one of  		the smaller offices and if they needed to do a conference call they  		could grab one of the larger offices.&lt;/p&gt;
&lt;p&gt;The other impetus behind the experiment was financial. Cisco has a  		huge campus in Santa Clara hundreds of buildings, each costing  		millions of dollars to maintain. Was it possible to pack more  		people into each building and maintain, or improve, their hapiness and productivity?&lt;/p&gt;
&lt;h3&gt;The Experiment&lt;/h3&gt;
&lt;p&gt;Ubisense was hired to figure out how well the different parts of the  		connect workspace were utilized. By giving each employee a tag, the system  		anonymously keep track of each time someone entered or left a room.  		This aggregate data could then be used to gain  		insight into the effectiveness of the new floor plan:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Did employees spend time in the open area?&lt;/li&gt;
    &lt;li&gt;If so, in which parts of the open area (it was divided into 5  		  subdivisons)?&lt;/li&gt;
    &lt;li&gt;How much were the individual offices being used? Were there  		  too many or too few?&lt;/li&gt;
    &lt;li&gt;What about the larger conference room?&lt;/li&gt;
    &lt;li&gt;How much was the kitchen and eating area utilized?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To do this, I hooked into Ubisense's platform API to monitor each time a tag  		entered or left a room. That information was then entered into a  		Oracle database (without any user information, so the data was totally  		anonymous). Thus the Oracle table consisted of millions of rows  		of data - with each row representing an tag entering a room or leaving a  		room. For example, here is a simplified view of the data:&lt;/p&gt;
&lt;table style="width: 100%;"&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;th&gt;tag_id&lt;/th&gt;
            &lt;th&gt;room_id&lt;/th&gt;
            &lt;th&gt;event&lt;/th&gt;
            &lt;th&gt;time&lt;/th&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;1&lt;/td&gt;
            &lt;td&gt;Conference #1&lt;/td&gt;
            &lt;td&gt;Enter&lt;/td&gt;
            &lt;td&gt;10:00am&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;2&lt;/td&gt;
            &lt;td&gt;Office #2&lt;/td&gt;
            &lt;td&gt;Enter&lt;/td&gt;
            &lt;td&gt;11:15am&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;2&lt;/td&gt;
            &lt;td&gt;Office #2&lt;/td&gt;
            &lt;td&gt;Leave&lt;/td&gt;
            &lt;td&gt;11:20am&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;1&lt;/td&gt;
            &lt;td&gt;Conference #1&lt;/td&gt;
            &lt;td&gt;Leave&lt;/td&gt;
            &lt;td&gt;11:30am&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Window Functions to the Rescue&lt;/h3&gt;
&lt;p&gt;The next trick was to analyze the data to answer the questions I  		posed above. To do that required figuring out how much time each  		tag spent in each room. So something like this:&lt;/p&gt;
&lt;table style="width: 100%;"&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;th&gt;room_id&lt;/th&gt;
            &lt;th&gt;enter&lt;/th&gt;
            &lt;th&gt;leave&lt;/th&gt;
            &lt;th&gt;duration&lt;/th&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Conference #1&lt;/td&gt;
            &lt;td&gt;10:00am&lt;/td&gt;
            &lt;td&gt;11:30am&lt;/td&gt;
            &lt;td&gt;1 hour 30 min&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Office #2&lt;/td&gt;
            &lt;td&gt;11:15am&lt;/td&gt;
            &lt;td&gt;11:20am&lt;/td&gt;
            &lt;td&gt;5 min&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;OObviously you could write a script in the language of your choice to  		process the raw data and populate this new table. But that adds  		another level of complexity to the system and makes it hard to do  		add-hoc queries.&lt;/p&gt;
&lt;p&gt;And this is where window functions are so useful. Using window  		functions, you can implement the basic algorithm fully in &amp;gt;&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Sort the data by tag_id, room_id and id so that room enter records  		  for a tag are directly followed by room exit records&lt;/li&gt;
    &lt;li&gt;Select the room exit records&lt;/li&gt;
    &lt;li&gt;Use theUse the lag window function to pull the previous record, which is  		  the room enter record, and then subtract the two times to get the  		  duration&lt;/li&gt;
    &lt;li&gt;Wrap this query up in a view, let's call it room_usage, that  		  can serve as the basis for add-hoc queries or reports.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Without window functions, item #3 is impossible with sql because there  		is no way to relate a record to its surrounding records (ie., a window).&lt;/p&gt;
&lt;p&gt;And thus window functions provide a great new data analysis tools which  		postgresql will make available to everyone at no-cost.&lt;/p&gt;

</description>
      <pubDate>Sat, 07 Mar 2009 18:20:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:faf73f9c-53c1-4481-8708-ca4dbccb7c99</guid>
      <comments>http://cfis.savagexi.com/2009/03/07/window-functions-and-the-connected-workspace#comments</comments>
      <category>Technology</category>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=505</trackback:ping>
      <link>http://cfis.savagexi.com/2009/03/07/window-functions-and-the-connected-workspace</link>
    </item>
    <item>
      <title>Profiling Your Rails Application - Take Two</title>
      <description>&lt;p&gt;Last year I wrote about how to &lt;a href="http://cfis.savagexi.com/2007/07/10/how-to-profile-your-rails-application"&gt;profile&lt;/a&gt; your Rails application, which is a lot harder then it seems. Its not so much the profiling itself - its easy enough to create one-off results. Instead, its coming up with a reproducible process that lets you measure performance changes over time.&lt;/p&gt;
&lt;p&gt;Some things that don't work over the long term:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Insert profiling code into your application code&lt;/li&gt;
    &lt;li&gt;Use unit tests for profiling&lt;/li&gt;
    &lt;li&gt;Use functional tests for profiling&lt;/li&gt;
    &lt;li&gt;Use integration tests for profiling&lt;/li&gt;
    &lt;li&gt;Modify standard rails environments (test, development, production for profiling)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the latest version of &lt;a href="http://cfis.savagexi.com/2008/11/12/ruby-prof-0-7-0"&gt;ruby-prof&lt;/a&gt; introduces a new approach to profiling  		your Ruby or Rails code that is heavily based on the excellent work &lt;a href="http://bitsweat.net/"&gt;Jeremy&lt;/a&gt;  		has done on the request profiler included in newer versions of Rails.&lt;/p&gt;
&lt;p&gt;The basic idea is to extend Ruby's TestUnit library so that individual test  		cases are profiled by including a new RubyProf::Test module. When you include this module, ruby-prof will run each test once as a warm up  		and then ten more times to gather profiling data (using another new feature  		of the 0.7.0 release, the ability to pause and resume a profiling run).  Profile data is then output for each test.&lt;/p&gt;
&lt;p&gt;Let's look at an example:&lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;class&lt;/span&gt;&lt;/span&gt; ExampleTest &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;lt;&lt;/span&gt; Test&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;Unit&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;TestCase&lt;br /&gt;  &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;include&lt;/span&gt;&lt;/span&gt; RubyProf&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;Test&lt;br /&gt;  &lt;br /&gt;  &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;def&lt;/span&gt;&lt;/span&gt; test_stuff&lt;br /&gt;    puts &lt;span style="color: rgb(255, 0, 0);"&gt;&amp;quot;Test method&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/tt&gt;&lt;/pre&gt;
&lt;p&gt;The line &lt;tt&gt;&lt;span style="font-weight: bold;"&gt; 		&lt;span style="color: rgb(0, 0, 255);"&gt;include&lt;/span&gt;&lt;/span&gt; RubyProf&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;Test&lt;/tt&gt;  		turns the test case into a profiling test case. The same approach  		could be used for hooking into other testing frameworks - all patches are  		of course welcome!&lt;/p&gt;
&lt;h3&gt;Using a Profile Environment for Rails&lt;/h3&gt;
&lt;p&gt;Now lets talk about profiling Rails. There are two main issues that make it harder then it seems.&lt;/p&gt;
&lt;p&gt;First, to get any useful data you need to profile a Rails app using the production environment settings in conjunction with a test database.  		Using the development environment doesn't work because the time it takes  		Rails to reload classes on each request drowns out any useful information.&lt;/p&gt;
&lt;p&gt;Second, how should profile tests be written and where should they go?&lt;/p&gt;
&lt;p&gt;The solution I've adopted is to use functional like-tests that use a  		PROFILE environment, and place them in a directory called test/profile.&lt;/p&gt;
&lt;p&gt;Let's look at another example:&lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 128);"&gt;require&lt;/span&gt;&lt;/span&gt; File&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;dirname&lt;span style="color: rgb(153, 0, 0);"&gt;(&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;__FILE__&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;+&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;'/../profile_test_helper'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;class&lt;/span&gt;&lt;/span&gt; MyControllerTest &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;lt;&lt;/span&gt; Test&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;Unit&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;TestCase&lt;br /&gt;  &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;include&lt;/span&gt;&lt;/span&gt; RubyProf&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;Test&lt;br /&gt;&lt;br /&gt;  fixtures&lt;span style="color: rgb(153, 0, 0);"&gt; :my_fixture&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;def&lt;/span&gt;&lt;/span&gt; setup&lt;br /&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;@controller&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt; MyController&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;new&lt;br /&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;@request&lt;/span&gt;    &lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt; ActionController&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;TestRequest&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;new&lt;br /&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;@response&lt;/span&gt;   &lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt; ActionController&lt;span style="color: rgb(153, 0, 0);"&gt;::&lt;/span&gt;TestResponse&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;new&lt;br /&gt;  &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;def&lt;/span&gt;&lt;/span&gt; test_get&lt;br /&gt;    get&lt;span style="color: rgb(153, 0, 0);"&gt;(&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;:index&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/tt&gt;&lt;/pre&gt;
&lt;p&gt;The only difference between a functional test and a profile test are  		the inclusion of the RubyProf::Test module and loading profile_test_helper.rb.  		profile_test_helper is unfortunately needed because the standard test_helper.rb  		file Rails uses loads the TEST environment. Hopefully future versions  		of Rails will fix this by allowing greater flexibility in specifying a test  		environment.&lt;/p&gt;
&lt;p&gt;So to get started with profiling your Rails application:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Copy profile_test_helper.rb from the ruby-prof distribution to your  		  rails test directory&lt;/li&gt;
    &lt;li&gt;Modify profile_test_helper.rb as needed to set ruby-prof's output  		  directory&lt;/li&gt;
    &lt;li&gt;Create a profile.rb file in the environments directory&lt;/li&gt;
    &lt;li&gt;Update your databases.yml file to include a profile database (just map it to your test database)&lt;/li&gt;
    &lt;li&gt;Create a new directory test/profile&lt;/li&gt;
    &lt;li&gt;Start writing profiling tests that look similar to the above example&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And now you'll have reproducible profiling tests cases.&lt;/p&gt;
&lt;p&gt;So what's missing? A way of keeping track of how your  		applications performance changes over time. A quick hack is to use  		source control to keep profile tests results around. A more sophisticated  		solution would be to use ruby-prof's API to dump profile results into a  		database and then put a nice web front end onto it. Any takers?&lt;/p&gt;

</description>
      <pubDate>Thu, 13 Nov 2008 11:11:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:e7a00427-bd00-4c50-8066-fdf41ef25854</guid>
      <comments>http://cfis.savagexi.com/2008/11/13/profiling-your-rails-application-take-two#comments</comments>
      <category>ruby-prof</category>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=504</trackback:ping>
      <link>http://cfis.savagexi.com/2008/11/13/profiling-your-rails-application-take-two</link>
    </item>
    <item>
      <title>ruby-prof 0.7.0</title>
      <description>&lt;p&gt;I'm happy to announce the release of 		&lt;a href="http://rubyforge.org/projects/ruby-prof"&gt;ruby-prof&lt;/a&gt; 0.7.0, the  		superfast, open-source, Ruby profiler that helps you find bottlenecks in  		your Ruby code. This release was a joint effort, with major contributions  		from &lt;a href="http://bitsweat.net/"&gt;Jeremy Kemper&lt;/a&gt; (aka bitsweat) of  		Rails fame and &lt;a href="http://www.linkedin.com/pub/0/567/a2"&gt;Hin Boen&lt;/a&gt;  		from CodeGear. There are two major new features in this release, as well  		as a number of smaller enhancements and bug fixes. For a full list of changes,  		take a look at the 		&lt;a href="http://rubyforge.org/forum/forum.php?forum_id=28366"&gt;release notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The first major new feature is improved Rails profiling, which I'll talk  		about in a separate &lt;a href="http://cfis.savagexi.com/2008/11/13/profiling-your-rails-application-take-two"&gt;post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The second major feature is significant internal changes that make it  		easier to integrate ruby-prof with IDEs. ruby-prof is already being used  		by Aptana's &lt;a href="http://www.aptana.com/rails/"&gt;RadRails&lt;/a&gt; and has  		been integrated into the next version of Code Gear's 		&lt;a href="http://www.codegear.com/products/3rdrail"&gt;3rd Rail&lt;/a&gt;. As part  		of this work, Hin has built a user interface for ruby-prof that lets a user  		inspect individual methods to see how much time they took as well as how  		they were called.&lt;/p&gt;
&lt;p&gt;One big problem though, previous versions of ruby-prof only kept track  		of aggregate data. This made it impossible for Hin to create the user interface  		he wanted. For example, look at this call sequence:&lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;     A&lt;br /&gt;    / \&lt;br /&gt;   B   K&lt;br /&gt;  / \   \&lt;br /&gt; C   D   B&lt;br /&gt;        / \&lt;br /&gt;       C   D&lt;/tt&gt;&lt;/pre&gt;
&lt;p&gt;With earlier versions of ruby-prof, there was no way to tell what percent  		of the time spent in method C was a result of the A -&amp;gt; B -&amp;gt; C call sequence  		versus the A -&amp;gt; K -&amp;gt; B -&amp;gt; C call sequence. &lt;br /&gt;
&lt;br /&gt;
Or take another example:&lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;  A    K&lt;br /&gt;  |    |&lt;br /&gt;  B    B&lt;br /&gt;  |    |&lt;br /&gt;  C    D	&lt;/tt&gt;&lt;/pre&gt;
&lt;p&gt;In this case, if you tried to reconstruct the call sequence from ruby-prof  		you would end up with this incorrect result:&lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;     A    K&lt;br /&gt;     |  /&lt;br /&gt;     B   &lt;br /&gt;    / \ &lt;br /&gt;  C    D	&lt;/tt&gt;&lt;/pre&gt;
&lt;p&gt;So working with Hin, I rearchitected ruby-prof to keep track of full call  		sequences. Most likely you won't notice any difference - the changes will  		only affect you if you use ruby-prof's api to present results in a custom  		way. In that case, you'll have to update your code, which should only take  		a few minutes (to see the api in use, take a look at the various printer  		classes that ship with ruby-prof).&lt;/p&gt;
&lt;p&gt;Enjoy, and all feedback is welcome.&lt;/p&gt;

</description>
      <pubDate>Wed, 12 Nov 2008 09:41:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:8201cb27-ae0d-4362-9e4c-f229565d12fb</guid>
      <comments>http://cfis.savagexi.com/2008/11/12/ruby-prof-0-7-0#comments</comments>
      <category>ruby-prof</category>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=503</trackback:ping>
      <link>http://cfis.savagexi.com/2008/11/12/ruby-prof-0-7-0</link>
    </item>
    <item>
      <title>So This is What 100k People Look Like </title>
      <description>&lt;p&gt;No doubt this post is two weeks past its prime, but better late than  		never, right? On Sunday, October 26th, Yue and I headed downtown to check  		out Barak Obama's campaign rally at Civic Center park in the heart of Denver.&lt;/p&gt;
&lt;p&gt;By the time we arrived the place was packed. In fact, it turned out that  		over &lt;a href="http://www.denverpost.com/breakingnews/ci_10821877"&gt;100,000&lt;/a&gt;  		of our closest friends were there, making it Obama's largest crowd in the  		United States up to that point. And I think the second largest crowed I've  		ever been in, surpassed only by watching fireworks on the 4th of July from  		the National Mall in Washington, DC).&lt;/p&gt;
&lt;p&gt;Wanting to at least be able to see Obama, we managed to be the last two  		people allowed into the center of the park (versus around the periphery).  		Once inside, we squirreled our way about half way to the stage. As you can  		see in the pictures, that wasn't all that close, but close enough to catch  		a glimpse of Obama. The first picture is looking west towards Denver's City  		hall. If you're interested, click the picture to get a bigger version, then  		find the tree in the center of the stage under the Colorado flag, and look  		right 3.5 columns to see Obama working the crowd:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Obama_Denver.jpg"&gt; 		&lt;img height="600" width="450" src="http://cfis.savagexi.com/files/Obama_Denver_Small.jpg" alt="Obama Denver Rally 2008" /&gt;&lt;/a&gt;]&lt;/p&gt;
&lt;p&gt;And here's the view looking back east, back towards the Colorado State  		Capital (supposedly Denver is the only city where the city hall faces the  		state Capital building, but I've never verified if that's true).&lt;/p&gt;
&lt;p&gt;&lt;img src="http://cfis.savagexi.com/files/Crowd_Capital_Small.jpg" alt="Obama Denver Rally Capital" /&gt;&lt;/p&gt;
&lt;p&gt;For the most part Obama stuck to his standard stump speech, but what  		struck me was its optimism and focus on working together.  		Nice words no doubt, but it was a nice change of pace from typical campaign  		bashing.&lt;/p&gt;
&lt;p&gt;On the way out, there was a table for all the poor souls who lost their  		keys and cell phones that day:&lt;/p&gt;
&lt;p&gt;&lt;img height="600" width="450" src="http://cfis.savagexi.com/files/Lost_Keys.jpg" alt="Lost Keys" /&gt;&lt;/p&gt;
&lt;p&gt;And a couple pictures of Yue for good measure, that show what a beautiful day it was:&lt;/p&gt;
&lt;p&gt;&lt;img height="600" width="450" src="http://cfis.savagexi.com/files/Yue_City_Hall.jpg" alt="Yue City Hall" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img height="600" width="450" src="http://cfis.savagexi.com/files/Yue_Capital.jpg" alt="Yue Capital" /&gt;&lt;/p&gt;
&lt;p&gt;Update: If you can't see Obama, check out Paul's &lt;a href="http://blog.cleverelephant.ca/2008/11/on-guard.html"&gt;post&lt;/a&gt;.  Paul also points out the snipers that are visible on top of City Hall.  And they weren't the only ones, there were plenty more to the north where Denver's taller buildings are located.&lt;/p&gt;

</description>
      <pubDate>Tue, 11 Nov 2008 11:15:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:4dfef351-738c-407f-b1ac-5857f979c47d</guid>
      <comments>http://cfis.savagexi.com/2008/11/11/so-this-is-what-100k-people-look-like#comments</comments>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=502</trackback:ping>
      <link>http://cfis.savagexi.com/2008/11/11/so-this-is-what-100k-people-look-like</link>
    </item>
    <item>
      <title>Rafting Down the Grand Canyon</title>
      <description>&lt;p&gt;Rafting down the Grand Canyon has been on my todo list for a long time.  		Over the years, I've rafted or canoed the James, Potomac, Rappahannock,  		Shenandoah, Arkansas, Taylor (well that's mostly a creek), Green and Colorado  		rivers. But never the Grand Canyon.&lt;/p&gt;
&lt;p&gt;So this summer it was time. Seven of us - Haydon, Dave, Natasha, Brian,  		Lauren, Yue and myself - took the plunge and paddled down the 		&lt;a href="http://www.outdoorsunlimited.com/grand-canyon/upper.html"&gt;upper  		part&lt;/a&gt; of the Grand Canyon from Marble Canyon to the Bright Angel Trail.  		We went with 		&lt;a href="http://www.outdoorsunlimited.com/grand-canyon/upper.html"&gt;Outdoors  		Unlimited&lt;/a&gt;, which I highly recommend. They not only provided equipment,  		but also knowledgeable guides and great food.&lt;/p&gt;
&lt;p&gt;Here's all of us by a grotto a few hundred feet above the Colorado River  		(from left to right is Dave, Haydon, Yue, myself, Brian, Natasha and Lauren  		- click the picture for a bigger version):&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Grotto.jpg"&gt; 		&lt;img height="431" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Grotto_Small.jpg" alt="Group" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are several different types of trips you can take down the canyon  		- we opted for a paddle trip. A paddle trip is just like it sounds - you  		get to paddle your way down the canyon using yellow, rubber boats, with  		six people per boat plus a guide. Depending on how much you like thrills,  		the best seats in the boat are the front two, where you get really wet from  		waves breaking over the bow when you hit a big rapid.&lt;/p&gt;
&lt;p&gt;Yue, who isn't much of a camper, was a good sport about the whole thing  		once she discovered she could sleep in a tent versus sleeping under the  		stars. Here is our campground ffrom the third night, with yours truly sleeping  		outside:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Tent.jpg"&gt; 		&lt;img height="337" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Tent_Small.jpg" alt="Tent" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It took five days and ninety five miles to get to our drop-off point  		- the deepest part of the canyon at the bottom of the 		&lt;a href="http://en.wikipedia.org/wiki/Bright_Angel_Trail"&gt;Bright Angel Trail&lt;/a&gt;..  		From there its an eight mile hike, and 4,380 feet up, to get to the visitor  		center on the South Rim. I had hiked the very top bit of the trail twenty  		years ago, but hadn't been back since.&lt;/p&gt;
&lt;p&gt;Its an absolutely beautiful trail, surprisingly cool on the bottom half  		(well, we did start at 7am) as it climbs up along a small creek. Here is  		what it looks like once you've hiked about two miles and reached the top  		of the inner gorge - the picture is looking south with the&lt;a href="http://en.wikipedia.org/wiki/Great_Unconformity"&gt;  		Great Unconformity&lt;/a&gt; in the foreground and the towering south wall in  		the background:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Bright_Angel_Trail.jpg"&gt; 		&lt;img height="600" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Bright_Angel_Trail_Small.jpg" alt="Bright Angel Trail" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dave took the prize, hiking up in an amazing time of 2:45, followed by  		Lauren and Natasha at 3:15, and the rest of us at 3:45 (which by the way  		I was quite proud of).&lt;/p&gt;
&lt;p&gt;Here the obligatory picture from the top:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Rim_View.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Rim_View_Small.jpg" alt="Rim View" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And here's Lauren, Natasha, Haydon and Brian a enjoying some well-deserved  		ice cream.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_at_the_Top.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_at_the_Top_Small.jpg" alt="At the Top" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And some of our better pictures:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Group_Picture_1.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Group_Picture_Small.jpg" alt="Group" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Brian_and_Yue.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Brian_and_Yue_Small.jpg" alt="Yue and Brian" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Charlie_and_Dave.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Charlie_and_Dave_Small.jpg" alt="Dave and Charlie" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Brian_Camp.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Brian_Camp_Small.jpg" alt="Brian's Camp" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Haydon_Boat.jpg"&gt; 		&lt;img height="430" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Haydon_Boat_Small.jpg" alt="Haydon on Boat" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;amp;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Yue_Natasha.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Yue_Natasha_Small.jpg" alt="Yue and Natasha" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Lauren_Brian_Yue_Charlie.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Lauren_Brian_Yue_Charlie_Small.jpg" alt="Lauren Brian Yue Charlie" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://cfis.savagexi.com/files/Grand_Canyon_Haydon_Brian_Lauren.jpg"&gt; 		&lt;img height="338" width="450" src="http://cfis.savagexi.com/files/Grand_Canyon_Haydon_Brian_Lauren_Small.jpg" alt="Haydon Brian Lauren" /&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <pubDate>Mon, 10 Nov 2008 20:24:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:a391fc79-e57d-4047-8cf8-0a1f32d52bdf</guid>
      <comments>http://cfis.savagexi.com/2008/11/10/rafting-down-the-grand-canyon#comments</comments>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=501</trackback:ping>
      <link>http://cfis.savagexi.com/2008/11/10/rafting-down-the-grand-canyon</link>
    </item>
    <item>
      <title>DNC Event Map</title>
      <description>&lt;p&gt;One of the projects we've been working on for &lt;a href="http://www.mapbuzz.com"&gt;MapBuzz &lt;/a&gt;the last few weeks is building an interactive &lt;a href="http://www.politicswest.com/mapbuzz"&gt;map &lt;/a&gt;that shows all the events going on in Denver during the Democratic National Convention. Users can pick the event type and date they are interested in, and the map refreshes with icons for relevant events. By clicking on a given event, the user can see exactly where and when the event is taking place. I think the map turned out pretty well - its a good example of mashup pulling data from different sources.  In this case, base maps from Google, event information from Zvents, and all rendering/styling/page from MapBuzz.&lt;/p&gt;
&lt;p&gt;It did clarify my thinking on a few points. First, Rails built-in page caching is really limited - it ignores query parameters and only works for html. So we had to hack around that, more info coming in a later post. Second, for building mashups xml really is superior to JSON simply because it supports namespaces (for all their pain points, namespaces really do facilitate merging of data from multiple sources). Third, when you need it, xslt is invaluable. Zvents serves its data using RSS, but our client only supports Atom. The simple solution was a quick xsl transformation to convert Zvent's rss feed over to Atom using libxslt (and thus MapBuzz's contribution back to the Ruby community to get the libxml and libxslt bindings back into good shape).&lt;/p&gt;

</description>
      <pubDate>Wed, 27 Aug 2008 12:55:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:927b124d-efdb-45cb-beff-2abd5c4e5e69</guid>
      <comments>http://cfis.savagexi.com/2008/08/27/dnc-event-map#comments</comments>
      <category>Design</category>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=500</trackback:ping>
      <link>http://cfis.savagexi.com/2008/08/27/dnc-event-map</link>
    </item>
    <item>
      <title>Resurrecting libxml-ruby</title>
      <description>          &lt;p&gt;There is general discontent with the state of XML processing in Ruby - see for example  &lt;a href="http://www.tbray.org/ongoing/When/200x/2006/08/22/REXML"&gt;here&lt;/a&gt; or &lt;a href="http://enfranchisedmind.com/blog/2008/03/24/rexml-dynamic-typing-lose/"&gt;here&lt;/a&gt;. An obvious solution is to use &lt;a href="http://xmlsoft.org/"&gt;libxml&lt;/a&gt;. However that has been a non-starter since the  &lt;a href="http://libxml.rubyforge.org/"&gt;libxml&lt;/a&gt; Ruby bindings have historically caused numerous segementation faults, don't run on  Windows and recently lost their current maintainer, Dan Janowski. Making it even more frustrating is that Dan had spent  the last year rearchitecting the bindings, successfully fixing the segmentation faults.&lt;/p&gt;
          &lt;p&gt;Since &lt;a href="http://www.mapbuzz.com"&gt;MapBuzz&lt;/a&gt; heavily depends on libxml, it seemed time to step in and contribute.  Over the last two weeks  I've added support for Windows, cleaned out the bug database and patch list, resolved the few remaining segmentation issues, greatly improved the RDocs and  refactored large portions of the code base to conform with modern Ruby extension standards.&lt;/p&gt;
          &lt;p&gt;After iterating through a couple of releases over the last two weeks, the Ruby libxml community is happy to announce the availability of  version 0.8.0,  which we believe is ready for prime time. It offers a great combination of speed, functionality and conformance (libxml  &lt;a href="http://xmlsoft.org/"&gt;passes&lt;/a&gt; all 1800+  tests in the &lt;a href="http://www.oasis-open.org/committees/xml-conformance/"&gt;OASIS XML Tests  Suite&lt;/a&gt;).&lt;/p&gt;
          &lt;p&gt;So  give it a try - its as easy to install as: &lt;/p&gt;
          &lt;pre&gt;&lt;tt&gt;&lt;/tt&gt;gem install libxml-ruby&lt;/pre&gt;
&lt;p&gt;If you're on Windows there may be an extra step if you haven't already installed libxml2.  If not, then the libxml-ruby distribution includes a prebuilt libxml2 dll in the libxml-ruby/mingw directory.  Copy the dll to libxml-ruby/lib, your Ruby bin directory, or somewhere on your path (basically put it someplace where Windows can find it). &lt;/p&gt;
&lt;p&gt;Undoubtedly there are still some bugs left, so please  &lt;a href="http://rubyforge.org/tracker/?atid=1971&amp;amp;group_id=494&amp;amp;func=browse"&gt;report&lt;/a&gt; anything you find, so we can fix them in future releases. &lt;/p&gt;
&lt;h3&gt;Blindingly Fast&lt;/h3&gt;
&lt;p&gt;The major reason people consider using libxml-ruby is performance. Here are the results from running (on my laptop) a few simple benchmarks that have recently been blogged about on the Web (you can find them in the benchmark directory of  the libxml distribution).&lt;/p&gt;
&lt;p&gt;From &lt;a href="http://depixelate.com/2008/4/23/ruby-xml-parsing-benchmarks"&gt;Zack Chandler&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;              user     system      total        real
libxml    0.032000   0.000000   0.032000 (  0.031000)
Hpricot   0.640000   0.031000   0.671000 (  0.890000)
REXML     1.813000   0.047000   1.860000 (  2.031000)&lt;/tt&gt;&lt;/pre&gt;
From &lt;a href="https://svn.concord.org/svn/projects/trunk/common/ruby/xml_benchmarks/"&gt;Stephen Bannasch:&lt;/a&gt;
&lt;pre&gt;
              user     system      total        real
libxml    0.641000   0.031000   0.672000 (  0.672000)
hpricot   5.359000   0.062000   5.421000 (  5.516000)
rexml    22.859000   0.047000  22.906000 ( 23.203000)
&lt;/pre&gt;
&lt;p&gt;From &lt;a href="http://yomi.at/archives/19"&gt;Andreas Meingast&lt;/a&gt;: &lt;/p&gt;
&lt;pre&gt;&lt;tt&gt;LIBXML THROUGHPUT:
	10.2570516817665 MB/s
	10.2570830340359 MB/s
	12.6992253283934 MB/s
  10.2570516817665 MB/s
	8.51116888387252 MB/s
	10.2570830340359 MB/s

HPRICOT THROUGHPUT:
	0.211597647822036 MB/s
	0.202390771964726 MB/s
	0.180272812529665 MB/s
	0.198474511420818 MB/s
	0.198474499681793 MB/s
  0.180925089981179 MB/s

REXML THROUGHPUT:
	0.130301425548982 MB/s
	0.131630590068325 MB/s
	0.128316078417727 MB/s
	0.125203555921636 MB/s
	0.120181872867636 MB/s
	0.115330940074107 MB/s&lt;/tt&gt;&lt;/pre&gt;
&lt;p&gt;I can't vouch for the appropriateness of the tests, but they show libxml clocking in at 10x hpricot and 30x to 60x REXML. I'd be happy to accept additional tests or more appropriate tests if you have any.&lt;/p&gt;

&lt;h3&gt;An Embarrassment of Riches&lt;/h3&gt;
          &lt;p&gt;In addition to performance, the libxml-ruby  bindings provide impressive  coverage of libxml's functionality. Goodies include:          &lt;/p&gt;
          &lt;ul&gt;
            &lt;li&gt;SAX&lt;/li&gt;
            &lt;li&gt;DOM&lt;/li&gt;
            &lt;li&gt;XMLReader (streaming interface) &lt;/li&gt;
            &lt;li&gt; XPath&lt;/li&gt;
            &lt;li&gt;XPointer&lt;/li&gt;
            &lt;li&gt;XML Schema&lt;/li&gt;
            &lt;li&gt;DTDs &lt;/li&gt;
            &lt;li&gt;XSLT (split into the &lt;a href="http://rubyforge.org/projects/libxsl/"&gt;libxslt-ruby&lt;/a&gt; bindings) &lt;/li&gt;
        &lt;/ul&gt;
          &lt;p&gt;Now, your first reaction might be that SAX, DOM and XPath are all you need, but validating parsers make it a whole lot easier to &lt;a href="http://cfis.savagexi.com/articles/2007/02/08/a-sane-way-of-sanitizing-html"&gt;sanitize&lt;/a&gt; user contributed content on web sites. And the XMLReader  offers a clever way of combining the DOM's ease of use (well, ok, compared to SAX at least) with SAX's memory and speed advantages. &lt;/p&gt;
          &lt;p&gt;Better yet, most of this functionality is exposed via an easy-to-use, Ruby like API. There are still of course  some warts lurking in the code, where libxml's C api leaks through to Ruby, but they are being removed one by one. And for those of you who aren't C hackers, much of this work can be done in good old Ruby. &lt;/p&gt;
          &lt;h3&gt;A Long History &lt;/h3&gt;
          &lt;p&gt;For such a useful, and full-featured library, the libxml-ruby bindings have a star-crossed history. Out of curiosity, I went back and traced their  lineage. &lt;a href="http://sean.chittenden.org/"&gt;Sean Chittenden&lt;/a&gt; originally &lt;a href="http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/7b946bfb42564929/0f92dfeb951ed7d0?lnk=gst&amp;amp;amp;q=libxml#0f92dfeb951ed7d0"&gt;wrote&lt;/a&gt; them back in 2002. At the start of 2005,  Trans Onoma &lt;a href="http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/3bf6b57ec81198c/746badd2919c859b?lnk=gst&amp;amp;q=trans+libxml#746badd2919c859b"&gt;adopted&lt;/a&gt; the project after Sean had moved on, and at the end of 2005 the bindings found their current &lt;a href="http://libxml.rubyforge.org/"&gt;home&lt;/a&gt; on Ruby Forge. At that point Ross Bamford took &lt;a href="http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/50c380881c5faff8/21e98aa900a96ef0?lnk=gst&amp;amp;amp;q=libxml#"&gt;over&lt;/a&gt; maintenance and  worked on the bindings for roughly a year, until early 2007, when then the bindings  again became unmaintained. Dan Janowski &lt;a href="http://rubyforge.org/pipermail/libxml-devel/2007-August/000430.html"&gt;picked&lt;/a&gt; up the ball in 2007 and completely overhauled the binding's memory model. Sadly,  Dan had to &lt;a href="http://rubyforge.org/pipermail/libxml-devel/2008-March/000744.html"&gt;give&lt;/a&gt; up active support this spring. &lt;/p&gt;
          &lt;p&gt;But on the bright side, Trans, Dan and Sean are all once again active on the mailing list, providing valuable experience and insight. From my point of view, with the renewed push towards a production quality release, and bringing in new users, the libxml-ruby community is as healthy as it has been in a long while.&lt;/p&gt;


</description>
      <pubDate>Wed, 16 Jul 2008 10:38:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:b0cd4b59-6bdc-464c-8211-17cf59c98973</guid>
      <comments>http://cfis.savagexi.com/2008/07/16/resurrecting-libxml-ruby#comments</comments>
      <category>Ruby</category>
      <trackback:ping>http://cfis.savagexi.com/trackbacks?article_id=499</trackback:ping>
      <link>http://cfis.savagexi.com/2008/07/16/resurrecting-libxml-ruby</link>
    </item>
  </channel>
</rss>
