<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ramblings &#187; SQL</title>
	<atom:link href="http://ramblings.gibberishcode.net/archives/category/programming/sql/feed" rel="self" type="application/rss+xml" />
	<link>http://ramblings.gibberishcode.net</link>
	<description>about fetching, interpreting, and executing.</description>
	<lastBuildDate>Sat, 01 May 2010 19:19:27 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Getting Ruby to talk to MSDE</title>
		<link>http://ramblings.gibberishcode.net/archives/getting-ruby-to-talk-to-msde/22</link>
		<comments>http://ramblings.gibberishcode.net/archives/getting-ruby-to-talk-to-msde/22#comments</comments>
		<pubDate>Fri, 09 Apr 2010 01:07:45 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Configuration]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Ruby Language]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Setups]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=22</guid>
		<description><![CDATA[Getting Ruby to talk to Microsoft SQL Server 2005 is one thing.  Getting Ruby to talk to Microsoft SQL Server Developer Edition 2000, has one twist that threw me off for hours.  Here, I&#8217;ll show you how to do and hopefully save you lots of frustration and brain damage from banging your head [...]]]></description>
			<content:encoded><![CDATA[<p>Getting Ruby to talk to Microsoft SQL Server 2005 is one thing.  Getting Ruby to talk to Microsoft SQL Server Developer Edition 2000, has one twist that threw me off for hours.  Here, I&#8217;ll show you how to do and hopefully save you lots of frustration and brain damage from banging your head against the wall.
</p>
<p>I won&#8217;t cover a complete Ruby and Rails/Ramaze/Sinatra installation.  This post assumes you have that much down pat, so we&#8217;ll pick up the ball with installing FreeTDS, unixODBC, Ruby ODBC
</p>
<p>If you&#8217;re using Ubuntu 8.10, you can get away with installing everything from packages with the following:
</p>
<pre class="twilight">
apt-get install  unixodbc freetds-common tdsodbc \
             libodbc-ruby1.8 \
             libdbi-ruby1.8 libdbd-odbc-ruby \
             autoconf make
</pre>
<p><b>NOTE:  I wrote this article and right before publishing, attempted to replicate these results on both mac OS X and CentOS 5.  Both failed to work with the technique I thought I had all nice and worked out.  I spent several hours over many months trying to find out exactly what&#8217;s different between the packages, but never got very far with my investigations.  For a long time, it looked to me like both the ODBC packages and the FreeTDS packages need to be tweaked to properly handle [SERVER]\[DB_INSTANCE] construct that MSDE uses vs. just [SERVER] that the full-fledged SQL Server uses.</b>  This, after all, is the convention of the Windows ODBC driver and DSN definitions.  However, there&#8217;s a really simple solution that I hit upon on Actual Technologies&#8217; website that led me to a true universal solution and that&#8217;s to learn the port the INSTANCE runs on and set up the DSN specifically to that port!
</p>
<p>But beware, the above doesn&#8217;t get you all of the freetds tools, most importantly the tsql command which can help you with debugging issues before getting into the ODBC game (that is, direct TDS connections to your SQL Server).  So, to cover the most distros possible, I&#8217;ll document the &#8220;from source&#8221; approach for *nix systems and macports for Macs so you&#8217;ll find two sets of installation instructions for every step.</p>
<h2>Preparing your Environment</h2>
<p>I prefer to pull down all source packages into my local user directory under a &#8220;~/src&#8221; folder.  The instructions that follow assume you&#8217;re in this folder.  If you&#8217;re not, then please make the appropriate substitutions as you follow along.  Additionally, I configure and compile most things as a regular user then use sudo to install system-wide as a &#8220;super user.&#8221;
</p>
<h2>Install unixODBC</h2>
<p>unixODBC provides the ODBC connectivity for the TDS driver.</p>
<pre class="twilight">
wget http://www.unixodbc.org/unixODBC-2.2.14.tar.gz
tar zxfv unixODBC-2.2.14.tar.gz
cd unixODBC-2.2.14
./configure  --enable-gui=no
make
sudo make install
</pre>
<h3>On Macs</h3>
<pre class="twilight">
sudo port install unixODBC
</pre>
<h3>On CentOS</h3>
<pre class="twilight">
sudo yum install unixODBC unixODBC-devel
</pre>
<h2>Install FreeTDS</h2>
<p>FreeTDS provides the open source implementation of the protocol implemented by both Sybase and Microsoft SQL Server.
</p>
<pre class="twilight">
wget ftp://ftp.ibiblio.org/pub/Linux/ALPHA/freetds/stable/freetds-stable.tgz
tar zvxf freetds-stable.tgz
cd freetds-0.82/
sudo ./configure  --with-unixodbc=/usr/local
sudo make
sudo make install
</pre>
<h3>On Macs</h3>
<pre class="twilight">
sudo port install freetds
</pre>
<h3>On CentOS</h3>
<pre class="twilight">
sudo yum install freetds
</pre>
<h2>Install ruby-odbc</h2>
<p>The ruby-odbc package provides the ruby bindings for ODBC connectivity.
</p>
<pre class="twilight">
wget http://www.ch-werner.de/rubyodbc/ruby-odbc-0.9995.tar.gz
tar zvxf ruby-odbc-0.9995.tar.gz
cd ruby-odbc-0.9995/
ruby extconf.rb
make
sudo make install
</pre>
<h3>On Macs</h3>
<pre class="twilight">
sudo port install rb-odbc
</pre>
<h2>Install ruby DBI</h2>
<pre class="twilight">
wget http://rubyforge.org/frs/download.php/655/ruby-dbi-all-0.0.23.tar.gz
tar zvxf ruby-dbi-all-0.0.23.tar.gz
cd ruby-dbi-all
ruby setup.rb config --with=dbi,dbd_odbc
ruby setup.rb setup
sudo ruby setup.rb install
</pre>
<h2>Configure FreeTDS and ODBC</h2>
<p>If you&#8217;re connecting to either SQL Server 2000 or SQL Server 2005 Standard, Enterprise, etc., then the typical approach is to add an entry for the server to /etc/freetds/freetds.conf and then reference that entry in the /etc/odbc.ini.  Sometimes, these files get installed in /usr/local/etc.  If so, then I will usually symlink the files into /etc for convenience, so that&#8217;s what&#8217;s referenced below.
</p>
<p>My server is called &#8220;apvdbs01&#8243; (the WINS name) with an IP Address of 10.0.2.2 and the database I&#8217;m connecting to is called &#8220;test_development&#8221; while &#8220;username&#8221; and &#8220;passwd&#8221; should be substituted with an valid user account and credentials for successfully connection to the database server.
</p>
<p>
Add a server entry for your server (which I typically give same name as Windows Server) to /etc/freetds.conf, so that host points to the SQL Server address:
</p>
<pre class="twilight">
[apvdbs01]
host = 10.0.2.2
port = 1433
tds version = 7.0
</pre>
<p>TDS version 7.0 is for SQL Server 2000.  If you have SQL Server 2005, then tds version is 8.0.</p>
<pre class="twilight">
tsql –S apvdbs01 –U username –P passwd
</pre>
<p>Once you can connect directly with the TDS drivers, move on to adding the ODBC driver entries.</p>
<p>Add the FreeTDS ODBC driver by editing “/etc/odbcinst.ini” (make sure “/usr/local/lib/libtdsodbc.so” exists first):
</p>
<pre class="twilight">
[FreeTDS]
Description     = TDS driver (Sybase/MS SQL)
Driver          = /usr/lib/odbc/libtdsodbc.so
Setup           = /usr/lib/odbc/libtdsS.so
</pre>
<p>Add a new DSN to “/etc/odbc.ini”:</p>
<pre class="twilight">
[test_development]
Driver          = FreeTDS
Description     = ODBC connection via FreeTDS
Trace           = No
ServerName      = apvdbs01
Database        = test_development
</pre>
<p>So, &#8220;FreeTDS&#8221; is the driver that we gave for the lib TDS ODBC connector in the odbcinst.ini file (this allows you to install and maintain multiple versions of the libtdsodbc.so file).  We reference this driver in the odbc.ini file with the &#8220;Driver&#8221; entry and we reference the actual server data in the freetds.conf file via the &#8220;ServerName&#8221; entry.
</p>
<p>Now that you have ODBC configured for FreeTDS, test it with:</p>
<pre class="twilight">
	isql test_development username passwd
</pre>
<h2>But that doesn&#8217;t work for MSDE!</h2>
<p>The above will get you connected to a standard or enterprise SQL Server 2000/2005 installation, but does not work for an Embedded SQL Server 2000/2005 installation.  After trying many permutations, I finally came up with a configuration that actually works.  One of the key things to know about MSDE is that the MSDE server instance is in the form of &#8220;servername\instance&#8221; rather than straight up &#8220;servername&#8221; as with standard/enterprise installations.  This is known as a &#8220;named instance.&#8221;  The trick to connecting?  Find out the port the named instance is listening on and connect to that port rather than the 1433 port!</p>
<p>You can determine the port used by the instance by using the Server Network Utility in the Microsoft SQL Server program group on the Windows server where your database resides. Select the instance name from the drop down list and select TCP/IP from the list of enabled protocols. This will show you the port number for that instance.</p>
<pre class="twilight">
[test_msde2000]
Driver         = FreeTDS
Description    = ODBC connection via FreeTDS
Server         = 10.0.2.2
Database       = my_test_db
Port           = 1348
TDS_Version    = 7.0
</pre>
<p>I could then connect to the MSDE instance like so:</p>
<pre class="twilight">
isql test_msde2000 username passwd
</pre>
<h2>Test ODBC connectivity from Ruby</h2>
<p>If both tsql and isql commands work, then you&#8217;re well on your way to connecting to SQL Server via Ruby.  To test Ruby with the above ODBC configurations:</p>
<pre class="twilight">
irb
require "dbi"
dbh = DBI.connect("dbi:ODBC:test_development", "username", "passwd")
</pre>
<h2>Connecting with Sequel</h2>
<p>One of my favorite tools for connecting to SQL DBMS&#8217; is Sequel, which is maintained by Jeremy Evans.  To connect using Sequel, install the gem:
</p>
<pre class="twilight">
sudo gem install sequel
</pre>
<p>And then give this a shot:</p>
<pre class="twilight">
irb
require 'rubygems'
require 'sequel'

DB = Sequel.odbc('test_development', :db_type => 'mssql', :user => 'username', :password => 'passwd')
</pre>
<h2>References</h2>
<p>http://www.freetds.org/userguide/confirminstall.htm</p>
<p>http://www.unixodbc.org/odbcinst.html</p>
<p>http://www.actualtech.com</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/getting-ruby-to-talk-to-msde/22/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Rails vs. Ramaze Performance Comparison</title>
		<link>http://ramblings.gibberishcode.net/archives/rails-vs-ramaze-performance-comparison/31</link>
		<comments>http://ramblings.gibberishcode.net/archives/rails-vs-ramaze-performance-comparison/31#comments</comments>
		<pubDate>Thu, 19 Feb 2009 21:07:24 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Servers]]></category>
		<category><![CDATA[Systems]]></category>
		<category><![CDATA[mri ruby]]></category>
		<category><![CDATA[passenger]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ramaze]]></category>
		<category><![CDATA[ree ruby]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=31</guid>
		<description><![CDATA[One of my biggest concerns of late is that my &#8220;more than just a little trivial&#8221; Rails projects seem to find their way straight into the heavyweight category in no time at all.  While I am quite hopeful that Merb being the 3.0 version of Rails will resolve many of the issues I face [...]]]></description>
			<content:encoded><![CDATA[<p>One of my biggest concerns of late is that my &#8220;more than just a little trivial&#8221; Rails projects seem to find their way straight into the heavyweight category in no time at all.  While I am quite hopeful that Merb being the 3.0 version of Rails will resolve many of the issues I face today, I have no idea when I can truly count on Rails 3.0&#8217;s arrival at the party.  Thus, I have begun looking at other frameworks that are available today.</p>
<h2>Introducing Ramaze</h2>
<p>One I found is <a href="http://www.ramaze.net">Ramaze</a>.  I have to say that Ramaze is a very minimalist approach to a framework that is actually rather enlightening.  For one coming from a Rails&#8217; world, it can feel a bit sparse at first.  But lately, I&#8217;m attributing my lack of comfort to being put on my toes to do some actual real Ruby coding.  Its both a little scary and liberating at the same time.  I have found that I am enjoying Ruby a lot more as a result of my exposure to Ramaze and my thought process is actually radically changing, almost like a Visual Basic programmer going to Borland Delphi might feel.</p>
<p>So I decided to take a hard look at performance metrics between Rails and Ramaze&#8217;s.  At the same time, I wanted to see how MRI 1.8.7 stacked up against Ruby Enterprise Ed. 1.8.6, so I&#8217;m satisfying a dual curiosity herein.  Being quite happy with <a href="http://www.mod_rails.com">Phusion Passenger</a>, I decided to set things up with Apache 2.2.9 + Passenger + Rails/Ramaze.  Since I&#8217;m quite comfortable with ERB and ActiveRecord, I tweaked Ramaze to be a bit more like the familiar Rails world I am familiar with.</p>
<h2>Un-DRYing the Results</h2>
<p>There&#8217;s no point in generating a lot of data and reporting on it if others cannot repeat yourself (CRY!) in replicating the results, so with that in mind, I attempt to refrain from further bad turn of words by busying myself in pushing everything I utilized to <a href="http://github.com/mwlang/benchmarks/tree/master">github.com</a> and documenting the basic <a href="http://ramblings.gibberishcode.net/archives/bootstrap-ubuntu-server-810-intrepid-64-bit/29">bootstrapping of Ubuntu Intrepid</a>.  For these tests in particular, I used two scripts, <a href="http://github.com/mwlang/bootstrap-scripts/blob/af44b371a24a79820c98fcf9cfe014b7a523a24a/ubuntu/intrepid/bootstrap-passenger-std.sh">bootstrap-passenger-std.sh</a> and <a href="http://github.com/mwlang/bootstrap-scripts/blob/af44b371a24a79820c98fcf9cfe014b7a523a24a/ubuntu/intrepid/bootstrap-passenger-ent.sh">bootstrap-passenter-ent.sh</a> to set up four distinct configurations under the Ruby1.8 interpreter.  Ruby 1.9.1. was a bit difficult to set up and there is a <a href="http://github.com/mwlang/bootstrap-scripts/blob/af44b371a24a79820c98fcf9cfe014b7a523a24a/ubuntu/intrepid/bootstrap-passenger-191.sh">bootstrap-passenger-191.sh</a> script that I assembled as I installed things, but I haven&#8217;t run it to ensure it works end-to-end.   </p>
<table style="width: 70%;" border="1">
<thead>
<tr>
<td style="text-align: center;" colspan="5">Apache 2.2.2</td>
</tr>
<tr>
<td style="text-align: center;" colspan="4">Passenger 2.0.6<br/>(gem)</td>
<td style="text-align: center;">Passenger 2.1.0<br/>(source)</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2">Rails 2.2.2</td>
<td style="text-align: center;" colspan="2">Ramaze 2009.01</td>
<td style="text-align: center;" colspan="1">Ramaze 2009.01</td>
</tr>
<tr>
<td style="text-align: center;">MRI Ruby 1.8.7<br/>package</td>
<td style="text-align: center;">Ent. Ruby 1.8.6<br/>source</td>
<td style="text-align: center;">MRI Ruby 1.8.7<br/>package</td>
<td style="text-align: center;">Ent. Ruby 1.8.6<br/>source</td>
<td style="text-align: center;">MRI Ruby 1.9.1<br/>source</td>
</tr>
</thead>
</table>
<p>I utilized this simple call to Apache Bench to run through the four test scenarios:<br />
All data collected was based on 10,000 requests at 100 concurrent users after an initial &#8220;warmup&#8221; of the passenger threads with start of 100 requests at 10 concurrent users.  The following command shows the basic set of parameters issued to Apache Bench:</p>
<pre class="twilight">
ab -n 10000 -c 100 http://demo.u64rails01.local/posts
</pre>
<p><br/></p>
<h2>What was tested?</h2>
<p>In order to compare, as close as possible, apples to apples, I opted to use Active Record and ERubis in Ramaze to set up the same MVC classes in both the Rails and Ramaze project.  ActiveRecord is probably the heaviest weighted ORM that can be utilized with Ramaze.  I tried briefly to get Erubis working with Rails so that the two frameworks&#8217; projects would be more closely aligned than the out-of-box ERB rendering Rails ships with would have afforded.  I did attempt Erubis with Rails, however, I was either doing something very wrong or Erubis under Rails takes a sizable performance hit and I stuck with out-of-box ERB for these tests given its better performance.  As such, Rails uses out of box ERB rendering in these tests.</p>
<p>The <a href="http://github.com/mwlang/benchmarks/tree/0baffeb9cf3eb514912c6de34b64dc2ce25c2e31/2009/02">application itself</a> is ridiculously simple, offering only one model and performing only a read against the database, but hopefully, an effective start towards building this benchmark framework towards more substantive benchmark tests to come.  For now, lets take a look at how Rails and Ramaze stack up to each other and what affect the underlying interpreter can have on performance.  Standard packaged Main Ruby Interpreter (MRI) 1.8.7 consisted of simple package install provided by the Ubuntu Intrepid repositories whle <a href="http://www.rubyenterpriseedition.com/">Ruby Enterprise Edition</a> (REE) 1.8.6, which hails from the same folks bringing you Phusion Passenger, is compiled from source.</p>
<p>As a bonus, I also compiled and clocked metrics for Ramaze on 1.9.1.  This was a tricky environment to get set up and working.  And my initial results made me think I did something horribly wrong with compiling and configuring Ruby 1.9.1.  However, I fully replicated Antonio Cangiano&#8217;s benchmarks in my test environment (see <a href="http://antoniocangiano.com/2008/12/09/the-great-ruby-shootout-december-2008/">The Great Ruby Shootout (December 2008)</a>) and MRI 1.9.1. did indeed crush the other Ruby interpreters in Fibonacci number crunching (amongst others).  The theory tossed around in the Ramaze IRC channel was that 1.9.1. change in implementation of the String class (and it being unicode native now) slows things down a good bit with string processing.</p>
<h2>The Results</h2>
<p>The following table tabulates the results gathered from Apache Bench as well as passenger-memory-stat.  I ran a short run to warm things up (100 requests, 10 concurrently).  And then ran the big bench at 10,000 requests, 100 concurrently, and then ran again and recorded the results.  At around 5,000 requests, I ran passenger-memory-stat to collect the data during mid-processing and then again at the end.  These tests were run on a Ubuntu 64-bit Intrepid 8.10 virtual server that was allocated 1 CPU core and 512MB of RAM.  Swap space was never triggered (0k utilization), and CPU utilization was roughly 85% within the Virtual Machine during the test runs.  The host machine, which is four core CPU, showed one core busy at near 100% with the other three cores pretty much idle.</p>
<table border="0" cellspacing="0" frame="void" rules="none">
<tbody>
<tr>
<td width="138" height="17" align="left"><strong></strong></td>
<td width="86" align="right"><strong>mri_rails</strong></td>
<td width="86" align="right"><strong>ree_rails</strong></td>
<td width="86" align="right"><strong>mri_ramaze</strong></td>
<td width="86" align="right"><strong>ree_ramaze</strong></td>
<td width="86" align="right"><strong>191_ramaze</strong></td>
<td width="86" align="left"><strong>units</strong></td>
</tr>
<tr>
<td height="16" align="right"><strong>Time taken for tests</strong></td>
<td align="right">489.24</td>
<td align="right">379.76</td>
<td align="right">397.43</td>
<td align="right">287.5</td>
<td align="right">351.95</td>
<td align="left"><em>seconds</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Complete requests</strong></td>
<td align="right">10000</td>
<td align="right">10000</td>
<td align="right">10000</td>
<td align="right">10000</td>
<td align="right">10000</td>
<td align="left"><em></em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Failed requests</strong></td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="left"><em></em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Write errors</strong></td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="left"><em></em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Total transferred</strong></td>
<td align="right">36670146</td>
<td align="right">36668229</td>
<td align="right">18130000</td>
<td align="right">18130000</td>
<td align="right">18460000</td>
<td align="left"><em>bytes</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>HTML transferred</strong></td>
<td align="right">30500000</td>
<td align="right">30500000</td>
<td align="right">14480000</td>
<td align="right">14480000</td>
<td align="right">14680000</td>
<td align="left"><em>bytes</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Requests per second</strong></td>
<td align="right">20.44</td>
<td align="right">26.33</td>
<td align="right">25.19</td>
<td align="right">34.78</td>
<td align="right">28.41</td>
<td align="left"><em>[#/sec] (mean)</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Time per request</strong></td>
<td align="right">4892.42</td>
<td align="right">3797.56</td>
<td align="right">3970.43</td>
<td align="right">2875.03</td>
<td align="right">3519.49</td>
<td align="left"><em>[ms] (mean)</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Time per request</strong></td>
<td align="right">48.92</td>
<td align="right">37.98</td>
<td align="right">39.7</td>
<td align="right">28.75</td>
<td align="right">35.2</td>
<td align="left"><em>[ms] (mean, across all concurrent requests)</em></td>
</tr>
<tr>
<td height="17" align="right"><strong>Transfer rate</strong></td>
<td align="right">73.19</td>
<td align="right">94.29</td>
<td align="right">44.59</td>
<td align="right">61.58</td>
<td align="right">51.22</td>
<td align="left"><em>[Kbytes/sec] received</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Total private dirty RSS</strong></td>
<td align="right">323.73</td>
<td align="right">180.89</td>
<td align="right">221.11</td>
<td align="right">221.51</td>
<td align="right">210.73</td>
<td align="left"><em>MB</em></td>
</tr>
</tbody>
</table>
<h2>Throughput</h2>
<p>Everybody likes to <a href="http://www.therailsway.com/2009/1/6/requests-per-second">talk about throughput</a>, so lets start there.  As you can see, MRI and Rails was the worst performer while REE and Ramaze clocked in with the highest throughput in terms of requests per second.  Be forewarned that throughput doesn&#8217;t necessarily equate to user response times.  This metric simply tells you the load at which your system is capable of delivering content at.</p>
<p><img src='http://github.com/mwlang/benchmarks/raw/0baffeb9cf3eb514912c6de34b64dc2ce25c2e31/2009/02/throughput.png' alt='throughput' class='alignnone' /></p>
<h2>Time per Request</h2>
<p>Time per request helps you see about how long an average user request takes for a given load.  As you can see here, with times in the milliseconds, we&#8217;re talking about numbers that will easily be swallowed in the noise of network latency.  Even so, its not hard to be impressed by what the REE folks managed to pull out of their hat with regards to performance on the main ruby interpreter.</p>
<p><img src='http://github.com/mwlang/benchmarks/raw/0baffeb9cf3eb514912c6de34b64dc2ce25c2e31/2009/02/time_per_request.png' alt='Time per Request' class='alignnone' /></p>
<h2>Memory Consumption</h2>
<p>This final graph shows the sizeable dent REE makes in memory consumption for running a Rails application.  Ramaze is already pretty efficient and lean and REE made practically no dent in memory consumption here.  However, REE&#8217;s use of the faster memory allocation routines does have a good effect on boosting Ramaze&#8217;s performance (as seen in above graphs), so there&#8217;s still lots to be gained by running Ramaze with REE.</p>
<p><img src='http://github.com/mwlang/benchmarks/raw/0baffeb9cf3eb514912c6de34b64dc2ce25c2e31/2009/02/memory_usage.png' alt='Memory Usage' class='alignnone' /></p>
<h2>Conclusion</h2>
<p>For me, I have two very, very viable solutions before me.  Rails on Enterprise Ruby or Ramaze on Ruby 1.8.7.  The final choice comes down to how much I can bet on my own Ruby prowess.   Ramaze is a very lean framework that gets you going, but doesn&#8217;t get you a nine inch pillowed mattress topping for your bed like Rails does.  Because Ramaze is so lean, I realize I will have to really take my Ruby skills to the next level and truly understand the ruby code that other developers produce.  Unlike Rails, where there&#8217;s gobs and gobs of plugins and blogs on the topic that make pretty much any need simple to satisfy and get working, I have to know my Ruby to get a solution implemented on Ramaze.  Not a bad thing, but certainly a more challenging and potentially rewarding path to take.</p>
<p>Aside from my personal concerns about making the jump, looking at these results, it is good to see that I have options and how easy it is to get a good deployment up and working compared to 3 or 4 years ago when I was banging my head against the wall for days on end to keep lighttpd + fcgi processes up and running around the clock.  In these tests, it was very, very satisfying that not one single crash or dropped request was recorded in all the benchmarking I performed.  Mongrel came along and made a big impact, but you still needed something like monit to watch Mongrel and restart it on ocvasion.  Now Passenger has come along and made yet another big impact in the ease of deploying Rails, Ramaze, or any of the other ruby-based frameworks.  We&#8217;ve come a long ways, indeed.  I, for one, certainly look forward to what the Ruby sphere will bring us over the next couple of years.</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/rails-vs-ramaze-performance-comparison/31/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Ruby, Mysql, CentOS 5, and 64-bit</title>
		<link>http://ramblings.gibberishcode.net/archives/ruby-mysql-centos-5-and-64-bit/23</link>
		<comments>http://ramblings.gibberishcode.net/archives/ruby-mysql-centos-5-and-64-bit/23#comments</comments>
		<pubDate>Mon, 19 Jan 2009 20:36:35 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[SQL]]></category>
		<category><![CDATA[Systems]]></category>
		<category><![CDATA[64-bit]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[Ruby Language]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=23</guid>
		<description><![CDATA[I have lately switched away from Ubuntu as my desktop and started using CentOS 5.2 because I wanted better support for a lot of RAM and virtualization (where I can quickly commission Linux servers to build out my testbed of target environments).  With the change, I also make the leap into pure 64-bit libraries with [...]]]></description>
			<content:encoded><![CDATA[<p>I have lately switched away from Ubuntu as my desktop and started using CentOS 5.2 because I wanted better support for a lot of RAM and virtualization (where I can quickly commission Linux servers to build out my testbed of target environments).  With the change, I also make the leap into pure 64-bit libraries with little to no 32-bit libraries installed.  There&#8217;s been a few sticky points to push through and one of them is getting Mysql and the appropriate mysql gems installed.  This quick post shows the way.</p>
<p><H2>My System</H2><br />
I am running CentOS 5.2 64-bit.  Or more succinctly:</p>
<pre  name="code" class="c:nocontrols">
2.6.18-92.1.22.el5 #1 SMP Tue Dec 16 11:57:43 EST 2008 x86_64 x86_64 x86_64 GNU/Linux
</pre>
<h2>The Problem</h2>
<p>If you simply execute:</p>
<pre  name="code" class="c:nocontrols">
sudo gem install mysql
</pre>
<p>You&#8217;ll get the following errors on a 64-bit system:</p>
<pre  name="code" class="c:nocontrols">
$ sudo gem install mysql
Building native extensions.  This could take a while...
ERROR:  Error installing mysql:
        ERROR: Failed to build gem native extension.

/usr/local/bin/ruby extconf.rb install mysql
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... yes
checking for mysql_query() in -lmysqlclient... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/usr/local/bin/ruby
        --with-mysql-config
        --without-mysql-config
        --with-mysql-dir
        --without-mysql-dir
        --with-mysql-include
        --without-mysql-include=${mysql-dir}/include
        --with-mysql-lib
        --without-mysql-lib=${mysql-dir}/lib
        --with-mysqlclientlib
        --without-mysqlclientlib
        --with-mlib
        --without-mlib
        --with-mysqlclientlib
        --without-mysqlclientlib
        --with-zlib
        --without-zlib
        --with-mysqlclientlib
        --without-mysqlclientlib
        --with-socketlib
        --without-socketlib
        --with-mysqlclientlib
        --without-mysqlclientlib
        --with-nsllib
        --without-nsllib
        --with-mysqlclientlib
        --without-mysqlclientlib

Gem files will remain installed in /usr/local/lib/ruby/gems/1.8/gems/mysql-2.7 for inspection.
Results logged to /usr/local/lib/ruby/gems/1.8/gems/mysql-2.7/gem_make.out
</pre>
<p>After digging into the gem options and logs a bit and looking at where mysql actually got installed, I discovered that it was hitting all the locations the 32-bit version would normally install to, but not the 64-bit version, which ends up in /usr/lib64/mysql. </p>
<p><H2>Installing the Ruby gems</h2>
<p>To properly install the Ruby gems for mysql, you&#8217;ll have to explicitly tell the gem where the mysql config is and it will then handle all the rest.  </p>
<pre  name="code" class="c:nocontrols">
gem install mysql -- --with-mysql-config=/usr/lib64/mysql/mysql_config
</pre>
<p>With that, you should see the following:</p>
<pre  name="code" class="c:nocontrols">
Building native extensions.  This could take a while...
Successfully installed mysql-2.7
1 gem installed
</pre>
<p>Check it out with:</p>
<pre  name="code" class="c:nocontrols">
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'mysql'
=> true
irb(main):003:0> quit
</pre>
<p>Life is good.  </p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/ruby-mysql-centos-5-and-64-bit/23/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Converting Oddmuse Wiki to Edgewall Trac</title>
		<link>http://ramblings.gibberishcode.net/archives/converting-oddmuse-wiki-to-edgewall-trac/13</link>
		<comments>http://ramblings.gibberishcode.net/archives/converting-oddmuse-wiki-to-edgewall-trac/13#comments</comments>
		<pubDate>Thu, 02 Oct 2008 16:30:38 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Macs]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python Language]]></category>
		<category><![CDATA[Ruby Language]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[oddmuse]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[trac]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=13</guid>
		<description><![CDATA[Our company began long ago with wiki&#8217;s, but we chose the Oddmuse wiki way back when.  These days, we&#8217;re heavy users of Trac wiki because of its integrated ticket support system.  So what to do with all those old wiki&#8217;s that folks have stopped using and reading.  The Oddmuse wikis still hold [...]]]></description>
			<content:encoded><![CDATA[<p>Our company began long ago with wiki&#8217;s, but we chose the Oddmuse wiki way back when.  These days, we&#8217;re heavy users of Trac wiki because of its integrated ticket support system.  So what to do with all those old wiki&#8217;s that folks have stopped using and reading.  The Oddmuse wikis still hold valuable data, but they have since become an administrative overhead to keep around, so I decided to convert them all to Edgewall&#8217;s Trac. </p>
<p>To get started, I needed to know how to get the data into Trac.  The following links gave me an API I could utilize to create trac pages and content:</p>
<p><a href="http://trac.edgewall.org/wiki/TracDev/DataModels">Trac Data Models</a><br />
<a href="http://trac.edgewall.org/browser/trunk/trac/wiki/model.py">The Data model Python code</a></p>
<p>Great!  Except there&#8217;s one small problem.  I don&#8217;t really know python all that well, even though I did do a bit of Zope development, oh, five years ago.  These days, my language of choice is Ruby, so I needed to figure out how to parse the Oddmuse syntax and turn it into Trac syntax.  The strategy:  Parse with Ruby, load cleaned page files with Python.  </p>
<p>This script will take a filename at command line and convert it and save back to file, but different extension (I chose *.om for &#8220;oddmuse&#8221;).  In the script below, I made the command-line optional as I discovered I didn&#8217;t handle every case as I worked on developing this script, so I simply changed the line that pulls the command-line argument to point to a specific file and output such to terminal and this allowed me to debug and fix as I went:</p>
<pre  name="code" class="ruby:nocontrols">
#!/opt/local/bin/ruby
# $FS  = "\xb3";      # The FS character is the RECORD SEPARATOR control char in ASCII
# $FS0 = "\xb3";      # The old FS character is a superscript "3" in Latin-1
# $FS1 = $FS . '1';   # The FS values are used to separate fields
# $FS2 = $FS . '2';   # in stored hashtables and other data structures.
# $FS3 = $FS . '3';   # The FS character is not allowed in user data.

FS = 179.chr

SITE_NAME = "corporate_wiki"
ATTACHMENT_URL_BASE = "https://intranet.example.com/#{SITE_NAME}/attachment/wiki/ConvertedAttachments/"

require 'find'
require 'ftools'

def safe_page_name(pagename)
  result = pagename.gsub("'","")
  result = result.slice(/(\w+)/).first
end

def get_section(page, section)
  page.each_with_index {|e,i| return page[i+1] if e == section}
  return ''
end

def asterick_prefixed(text)
  t = text.match(/(\*+)([^\b]*)/)
  return text if t.nil?

  tokens = t.to_a
  return ('  ' * tokens[1].size) + '* ' + tokens[2]
end

def colon_prefixed(text)
  return "  #{colon_prefixed(text.slice(1,text.size))}" if text[0] == ":"[0]
  return text
end

def pound_prefixed(text)
  return "  1. #{text.slice(1,text.size)}" if text[0] == "#"[0]
  return text
end

def fix_headers(text)
  t = text.match(/(=+)([^=]+)(=+)/)
  return text if t.nil?

  tokens = t.to_a
  return "#{tokens[1]} #{tokens[2].strip} #{tokens[1]}"
end

def replace_a_url(url, text)
  text.gsub!("http://#{url}", ATTACHMENT_URL_BASE)
  text.gsub!("https://#{url}", ATTACHMENT_URL_BASE)
  text.gsub!("http:/#{url}", ATTACHMENT_URL_BASE)
  text.gsub!("https:/#{url}", ATTACHMENT_URL_BASE)
  return text
end

def replace_wiki_urls(text)
  matches = text.match(/\[http(.)*\]/)
  result = text
  if matches
    result = replace_a_url("wiki.example.com/#{SITE_NAME}/wikifiles/", result)
    result = replace_a_url("pdfreviewfiles/", result)
    result = replace_a_url("cgi-bin/", result)
  end
  result
end

def convert_formats(text)
  result = text.gsub(/\[\[(\w+)\]\]/,"[wiki:" + '\1' + "]")
  result = asterick_prefixed(result)
  result = colon_prefixed(result)
  result = pound_prefixed(result)
  result = replace_wiki_urls(result)
  result = fix_headers(result)
  result
end

def oddmuse_to_trac(text)
  result = ''
  in_pre = false
  text.split("\n").each do |line|

    # Detect indented lines as these are rendered as PRE html blocks
    trimmed = line.lstrip
    is_prefixed = ((trimmed.size < line.size) and (trimmed[0] != '*'[0]))
    result << "{{{\n" if (is_prefixed and !in_pre)

    result << "}}}\n" if (!is_prefixed and in_pre)
    in_pre = is_prefixed

    # Don't process special characters when in PRE block
    if in_pre
      result << line << "\n"
    else
      result << convert_formats(line) << "\n"
    end
  end
  result += "}}}\n" if in_pre
  result
end

path = ARGV[0] || "page/U/UsingThePhones.db"
raw_page = File.new(path).read
page2 = raw_page.split(FS + '2')
page3 = raw_page.split(FS + '3')

text = get_section(page3, 'text')

pagename = File.basename(path).split('.').first
author = get_section(page2, 'username')
address = get_section(page2, 'ip')
body = oddmuse_to_trac(text)

puts "writing: #{pagename}.om"
outfile = File.new(path.gsub('.db','.om'),'w')
outfile.puts 'pagename:' + pagename
outfile.puts 'author:' + author
outfile.puts 'address:' + address
outfile.puts body
outfile.close

puts body if ARGV[0].nil?
</pre>
<p>Below, comes the Python side of the equation.  Once I did the heavy lifting in Ruby, I needed to generate all the pages.  As you can see, there's some parsing happening, but its very simple parsing and I was able to apply what simple Python knowledge I still retained pretty effectively:</p>
<pre  name="code" class="python:nocontrols">
import sys
from trac.env import Environment
from trac.wiki.model import WikiPage

print sys.argv[1]
file = open(sys.argv[1], 'r')
pagename = file.readline().split(':')[1].split('\n')[0]
author_name = file.readline().split(':')[1].split('\n')[0]
address = file.readline().split(':')[1].split('\n')[0]

text = file.read()

env = Environment('/srv/www/trac/corporate_wiki')

# Read an existing or new WikiPage:
page = WikiPage(env, pagename)
if page.text != text:
        page.text = text
        page.save(author=author_name, comment='imported from oddmuse wiki', remote_addr=address)
</pre>
<p>As you can see, this file simply takes the command line argument, open the file for reading, pull the various variables out with the remainder of the file going to the page content.</p>
<p>During the conversion, I noticed some pages were failing to convert at all, like "Michael'sTodoList"  The culprit, the tick in the page name and thus the filename.  The scripts themselves weren't the problem, it was the xargs parameter passing that was the issue (getting to that below).  So I ran the below script to rename all the troublesome filenames and then added similar tick to underscore substitution in the main Ruby script above.  </p>
<pre  name="code" class="ruby:nocontrols">
require 'find'
require 'ftools'

total_size = 0

Find.find('./page') do |path|
  if FileTest.directory?(path)
    if File.basename(path)[0] == ?.
      Find.prune       # Don't look any further into this directory.
    else
      next
    end
  else
    if path.match(/[\']/)
      safe_path = path.gsub("'", "_")
      puts path + ' to ' + safe_path
      File.move(path, safe_path)
    end
    end
  end
</pre>
<p>A simple up-front execution of the script was all that was needed to now be able to pick up the missing pages:</p>
<pre  name="code" class="ruby:nocontrols">
ruby fix_unsafe_files.rb
</pre>
<p>Finally, to bring it all together, I utilized find to locate all the *.db Oddmuse files which I had located into the various wiki subfolders where these scripts sat, passing the filename to the script via the xargs utility:</p>
<pre  name="code" class="ruby:nocontrols">
find . -name *.db -print0 | xargs -0 -L 1 ruby rdpage.rb
</pre>
<p>I then essentially repeated the above, but for the generated *.om files, passing to the Python mkpage.py script:</p>
<pre  name="code" class="ruby:nocontrols">
find . -name *.om -print0 | xargs -0 -L 1 python mkpage.py
</pre>
<p>One last thing, before we're done.  Each wiki had attachments.  So I had to think how to get those attachments referenced and linked to the Trac wiki.  A little investigation revealed that each attachment got a database entry in the attachment table.  My task was a bit complicated by the fact that the users over the years in the Oddmuse environment scattered their attachments everywhere and also used varying syntax to refer to them, hence, if you review the main Ruby parsing script, you'll see the "replace_a_url" method above that cleans all those funky URLs up.  So the script I used below simply scans through all the directories where we had files stored and attached them to one "ConvertedAttachments" page.  Probably not the ideal solution, but again, got the job done quickly.</p>
<pre  name="code" class="ruby:nocontrols">
#  type |          id          |  filename  |  size  |    time    | description | author |      ipnr
# ------+----------------------+------------+--------+------------+-------------+--------+----------------
#  wiki | ConvertedAttachments | gotapi.png | 200272 | 1218381947 |             | michael | 192.168.16.100

path = ARGV[0] || 'generate_attachments.rb'
filename = File.basename(path)

puts "insert into attachment values ('wiki', 'ConvertedAttachments', '#{filename}', #{FileTest.size(path)}, null, null, 'oddmuse','192.168.1.246');"
</pre>
<p>So, running the above needs to be piped to a SQL fisle and then executed against your Trac database.  In my case, Postgresql.  Since I had many directories to snatch up, I ran it for each folder as appropriate and simply kept appending until I had all the attachment directories represented.</p>
<pre  name="code" class="ruby:nocontrols">
ls | xargs -0 -L 1 >> attachments.sql
psql trac_db < attachments.sql
</pre>
<p>All in all, not too bad of a conversion for a few hour's work.  Its about 95% correct.  Some of the page names didn't translate to Trac, and could probably do with some regexp search and replace on the page names themselves.  The biggest gain is that we have preserved the information in the old systems by porting it to the new systems we all know and use on a daily basis, so WikiGardening is more likely to keep the content fresher and its one less system for our end-users to figure out how to use.  </p>
<p>I can now retire the server running Oddmuse, stop doing backups and keeping up with documentation and training new system administrators on how to manage Oddmuse AND Trac.  </p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/converting-oddmuse-wiki-to-edgewall-trac/13/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Geocoded Zipcodes</title>
		<link>http://ramblings.gibberishcode.net/archives/geocoded-zipcodes/11</link>
		<comments>http://ramblings.gibberishcode.net/archives/geocoded-zipcodes/11#comments</comments>
		<pubDate>Mon, 07 Jul 2008 23:20:26 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Ruby Language]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[loading data]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[zip codes]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=11</guid>
		<description><![CDATA[You would think that loading up a database of zip codes would be an extremely simple case of finding a public database on www.usps.gov, and then loading up with a database bulk load.  It turns out that, while the USPS does offer products for you to purchase, and a rather nice lookup interface for [...]]]></description>
			<content:encoded><![CDATA[<p>You would think that loading up a database of zip codes would be an extremely simple case of finding a public database on www.usps.gov, and then loading up with a database bulk load.  It turns out that, while the USPS does offer products for you to purchase, and a rather nice lookup interface for looking up zip codes and so on, there&#8217;s not really any free data to be had that I could tell.  So the hunt was on to find some zip code data, preferably geocoded (latitude/longitude for GIS) zip codes and then load them into MySQL.</p>
<p>A bit of hunting around turned up <a href="">this list of zip code databases</a>.  The first link to CivicSpace database appears to be defunct (I got a Go Daddy &#8220;this page is parked&#8221; page).  So, the next free one, offered by <a href="http://www.populardata.com/index.html">Popular Data</a> turned out to be exactly what I was looking for.  It may be a &#8220;little old&#8221; as the review site says, but its good enough for me to get started with associating my data with geocoded locations and explore the wonderful world of mapping offered by Google&#8217;s Maps API.  </p>
<p>To load it up in MySQL and PostgreSQL, I used the following Ruby script to generate a bunch of insert statements:</p>
<pre  name="code" class="ruby:nocontrols">
require 'rubygems'
require 'fastercsv'
source_file = 'ZIP_CODES.txt'
outfile = File.new('us_zip_codes.sql','w')

# generate the truncate statement if we want to empty the table
outfile.puts "truncate us_zip_codes;"

# generate the SQL Insert statements
FasterCSV.foreach(source_file, :headers => false) do |cols|
  row = "INSERT INTO us_zip_codes VALUES ("
  row << as_string(cols[0]) << ","
  row << as_number(cols[1]) << ","
  row << as_number(cols[2]) << ","
  row << as_string(cols[3]) << ","
  row << as_string(cols[4]) << ","
  row << as_string(cols[5]) << ","
  row << as_string(cols[6]) << ");"
  outfile.puts row
end
outfile.close
puts "copied #{i} records."
</pre>
<p>Create the zip code table with the following SQL:</p>
<pre  name="code" class="sql:nocontrols">
create table us_zip_codes (
  zip_code char(5),
  latitude float(10,6),
  longitude float(10,6),
  city varchar(255),
  state char(2),
  county varchar(255),
  zip_class varchar(255)
);
</pre>
<p>And finally, with the table in place and the SQL insert statements generated, I loaded the data into MySQL with the following from command line:</p>
<pre  name="code" class="ruby:nocontrols">
mysql target_db_name < us_zip_codes.sql
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/geocoded-zipcodes/11/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>DRYing your Views</title>
		<link>http://ramblings.gibberishcode.net/archives/drying-your-views/8</link>
		<comments>http://ramblings.gibberishcode.net/archives/drying-your-views/8#comments</comments>
		<pubDate>Sat, 05 Jul 2008 20:48:27 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Ruby Language]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[dry]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby block]]></category>
		<category><![CDATA[views]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=8</guid>
		<description><![CDATA[Let me start out by saying that I am finally beginning to understand a bit about that magical Ruby block notion and how implementing methods through block passing can really empower you as a Ruby developer.  Thanks to, a most excellent Ruby tutorial, I am definitely feeling a good bit more empowered about getting [...]]]></description>
			<content:encoded><![CDATA[<p>Let me start out by saying that I am finally beginning to understand a bit about that magical Ruby block notion and how implementing methods through block passing can really empower you as a Ruby developer.  Thanks to, a most excellent <a href="http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_containers.html">Ruby tutorial</a>, I am definitely feeling a good bit more empowered about getting my Views in Rails all DRY&#8217;d up.</p>
<p>The problem:  I wanted to introduce a property editor metaphor for the website I&#8217;m working on where properties could expose edit forms for just about any situation, much like wordpress&#8217; widget interface does.  </p>
<p>That is, there&#8217;s an area of the current page that shows basic information about the property in a read-only (and preferably compact) form, and user could click an &#8220;edit&#8221; button and exposed an editable version of the properties.  The &#8220;edit&#8221; link becomes a &#8220;cancel&#8221; link, which, when clicked, simply discards the edit form and return the initial compact view state.</p>
<p><a href='http://ramblings.gibberishcode.net/wp-content/uploads/2008/07/click_edit_to_show.png'><img src="http://ramblings.gibberishcode.net/wp-content/uploads/2008/07/click_edit_to_show.png" alt="Clicking EDIT to extend the display for editing" title="click_edit_to_show" width="280" height="206" class="alignleft size-full wp-image-10" /></a></p>
<p>The problem is, Rails doesn&#8217;t really have any good helpers for reusing a design concept in your views.  I found that even with partials, the code was really starting to bog down and become both unmaintainable and unreadable.  I needed a simple mechanism to construct my property editors.  I started out with helper methods.  But that wasn&#8217;t much better as the code just started ending as ruby here-docs and became a maintenance issue of another sort!  Take a look at this embarrassment:</p>
<pre name="code" class="ruby:nocontrols">
result = "&lt;table&gt;&lt;tr&gt;"
result += "&lt;th colspan=2&gt;"
result += button_image_action('Group selected together', 'link.png')
result += button_image_action('Copy selected to this group', 'link_add.png')
result += button_image_action('Move selected to this group', 'link_go.png')
result += button_image_action('copy group', 'page_copy.png')
result += button_image_action('cut group', 'cut.png')
result += button_image_action('paste group', 'page_paste.png')
result += button_image_action('delete group', 'delete.png')
result += "&lt;/th&gt;&lt;/tr&gt;"
result += "&lt;/table&gt;"
</pre>
<p>Ok, I admit it, I chopped out more than half again as many lines as you&#8217;re looking at there, and perhaps worse, this isn&#8217;t actually the original HTML code that led me down this path, but lets take a look at this approach anyway, as I learned something valuable in the process and it <em>was</em> my first attempt at DRYing my views. First thing, first, get the above HTML back into the erb file:</p>
<pre name="code" class="ruby:nocontrols">
<table>
<th colspan=2>
			<%= button_image_action('Group selected together', 'link.png') %>
			<%= button_image_action('Copy selected to this group', 'link_add.png') %>
			<%= button_image_action('Move selected to this group', 'link_go.png') %>
			<%= button_image_action('copy group', 'page_copy.png') %>
			<%= button_image_action('cut group', 'cut.png') %>
			<%= button_image_action('paste group', 'page_paste.png') %>
			<%= button_image_action('delete group', 'delete.png') %>
		</th>
</tr>
</table>
</pre>
<p>Which promptly let me to trying something like this:</p>
<pre name="code" class="ruby:nocontrols">
def button_image_action(title, image_filename, button_text = '', url = '')
    form_for @cube url do |f|
  	  <<-TEXT
  	    <BUTTON alt='#{title}' title='#{title}' type='submit'>#{button_text}
  	    <IMG alt='#{title}' title='#{title}' src='/images/#{image_filename}'>
  	    </BUTTON>
  	  TEXT
     end
  end
</pre>
<p>I thought it was shaping up nicely, but I got completely blindsided when it came to rendering this bit.  You see, the Rails Form Helpers are unavailable in a regular helper!  They require ERB (being inside <% %> and <%= %>) to actually properly render their blocks.</p>
<p>I fiddled around for a bit to try to jump this hurdle, but it just simply ain&#8217;t gonna work without some serious hacking. I&#8217;m happy to report that I got a little wiser and stepped back from the problem and reevaluated my approach in general.  I realized, I wasn&#8217;t getting away from doing HTML code in helpers!  Bottom line:  Helpers are great, but you&#8217;re doing something clearly against &#8220;The Rails Way&#8221; when you start rendering large chunks of HTML code from helpers.  It just isn&#8217;t a great approach and if you have designers on your team, they can get quite lost hunting around for all those HTML snippets. </p>
<p>So, how could I get <em>all</em> of my HTML back into *.erb files where they belonged, yet develop some helpers that would make mincemeat of the coding effort to build these fancy toolbars and forms I had coming?  Fellow colleague, Steven Beales said, &#8220;you need to use Ruby blocks.&#8221;  To which, my first reaction was, &#8220;what are Ruby blocks?&#8221;  To my great benefit, he explained by pointing me straight at <a href="http://www.igvita.com/2007/03/15/block-helpers-and-dry-views-in-rails/ ">Block Helpers and DRY Views in Rails</a>.</p>
<p>Truth be told, I mostly understood this article, but not entirely.  Even so, I dutifully set off to solve my problem with this approach.  This led me to a bit of copying and pasting (and crediting)&#8230;</p>
<pre name="code" class="ruby:nocontrols">
  # From:  http://errtheblog.com/posts/11-block-to-partial
  #
  # captures the output of our passed block using Rails’ capture method and adds
  # it to our options hash under the key of :body. Next it renders our passed
  # partial, sending in our options hash as :locals
  def block_to_partial(partial_name, options = {}, &#038;block)
    options.merge!(:body => capture(&#038;block))
    concat(render(:partial => partial_name, :locals => options), block.binding)
  end
</pre>
<p>Look at that carefully and, if I may be so bold to suggest, go read up on <a href="http://errtheblog.com/posts/11-block-to-partial">ERR the Blog</a> to better understand as this method is key and I daresay was appropriately called &#8220;tomfoolery&#8221; by the wise authors therein.  This bit of code really forced me to finally begin to learn and understand what the heck block passing was all about.  In a nutshell, this little gem lets you pass options that become local variables to the partial and the block is the contents to be rendered within the partial when you <em>yield</em> to the passed block.</p>
<div class="aside">
<h2> An Aside </h2>
<p>There were a couple instances where I was building a repeated elements with pretty much the same resulting HTML (namely toolbar buttons where only the image, caption, alt, title, and onclick events were changing).  Taking inspiration from <a href="http://www.igvita.com/2007/03/15/block-helpers-and-dry-views-in-rails/">Ilya Grigorik&#8217;s rounded box implementation</a>, I felt he had the approach I wanted, but I didn&#8217;t always need to pass in blocks for what I was do in every situation.  Unfortunately, I didn&#8217;t understand blocks!  I had to go back to school and understand them in order to adapt his approach to my situation.  After schooling myself a bit, I came up with this modified version of the block_to_partials to pass those attributes in (no block necessary!).  I named the simplified version &#8220;variables_to_partial&#8221; as follows:</p>
<pre name="code" class="ruby:nocontrols">
  # Calls given partial, injecting the options hash into the partial's local hash
  def variables_to_partial(partial_name, options = {})
    render(:partial => partial_name, :locals => options)
  end
</pre>
</div>
<p>The block_to_partial is a great start, but I wanted to simplify my calls and also enforce a bit of use-pattern on the developer (i.e. me) so that all property editors had essentially the same behavior pattern (and components).  Thus, we introduce the property editor container which in turn can have one or more sections associated with it:</p>
<pre name="code" class="ruby:nocontrols">
  # Generates a division around the given block that is styled with "property-editor"
  # Property editors are intended as a view on data that has both a condensed display and
  # an expanded display that lets you edit the information contained in the property editor.
  def property_editor(id, title, options = {}, &#038;block)
    @id = id
    block_to_partial 'layouts/property_editor', options.merge(:id => id, :title => title), &#038;block
  end
</pre>
<pre name="code" class="ruby:nocontrols">
  # Renders content of a section (provided by block) inside a property editor division
  # The stylesheet contains styling for the following section_id's:
  #   => "show" - the section initially visible
  #   => "edit" - the section made visible when the edit button is clicked
  def property_editor_section(section_id, options = {}, &#038;block)
    block_to_partial('layouts/property_editor_section',
      options.merge(:section_class => "property-editor-#{section_id}",
        :section_id => "#{@id}-#{section_id}"),
        &#038;block)
  end
</pre>
<p>The property editor partial:</p>
<pre name="code" class="ruby:nocontrols">
&lt;div &lt;%= %[id="#{id}"] %&gt; class="property-editor"&gt;
	&lt;div class="property-editor-title"&gt;
		&lt;div style="float:right"&gt;
			&lt;a href="javascript: void(0)" onclick="toggle_div('&lt;%= id %&gt;-show');toggle_div('&lt;%= id %&gt;-edit');"&gt;edit&lt;/a&gt;
		&lt;/div&gt;
  		&lt;%= title %&gt;
	&lt;/div&gt;
	&lt;%= body %&gt;
&lt;/div&gt;
</pre>
<p>and the property editor sections are rendered with this partial:</p>
<pre name="code" class="ruby:nocontrols">
&lt;div &lt;%= %[id="#{section_id}"] %&gt; &lt;%= %[class="#{section_class}"] %&gt; &lt;%= %[style="display:none"] if section_id =~ /(.+)?\-edit/  %&gt;&gt;
	&lt;%= body %&gt;
&lt;/div&gt;
</pre>
<p>With these helpers and partials, property editors as a concept is very easy to pull together in my view files.  As you can see, if my section is an &#8220;edit&#8221; section, its hidden at the outset.  The button to toggle between show and edit is in the property editor partial itself in which an onclick javascript calls toggle_div to make this happen.  The Javascript is very straightforward:</p>
<pre name="code" class="javascript:nocontrols">
function toggle_div(div_name) {
	var div = $(div_name);
	div.getStyle('display') == 'none' ? div.setStyle('display', 'block') :	div.setStyle('display', 'none');
}
</pre>
<p>Here&#8217;s just one such property editor operating on my &#8220;cube model&#8221;:</p>
<pre name="code" class="ruby:nocontrols">
&lt;% property_editor 'properties', 'Properties' do %&gt;
	&lt;% property_editor_section 'show' do %&gt;
		&lt;%= @cube.name %&gt;&lt;br/&gt;
		&lt;%= render :partial =&gt; '/cubes/cube_stats' %&gt;
	&lt;% end %&gt;

	&lt;% property_editor_section 'edit' do %&gt;
		&lt;%= render :partial =&gt; 'form' %&gt;
	&lt;% end %&gt;
&lt;% end %&gt;
</pre>
<p>What&#8217;s more, I had a complete set of CSS selectors with which I could control my property editor and its associated sections:  </p>
<pre name="code" class="css:nocontrols">
.property-editor {
	margin-top: 5px;
	border: 3px solid #FDEFE6;
}

.property-editor-title {
	padding: 2px;
	padding-left: 5px;
	padding-right: 5px;
	background-color: #EEE;
	color: #2F6569;
}

.property-editor-show, .property-editor-edit {
	padding: 5px;
}

.property-editor p {
	margin: 0px;
	padding: 0px;
}
</pre>
<p>Well, all in all, that&#8217;s a good bit of rambling, but I hope others can benefit from my experiences with Ruby, blocks, partials, and DRY up those views.</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/drying-your-views/8/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
