<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>fixedd.com Full Site Feed</title>
  <link href="http://fixedd.com/feeds/site" rel="self"/>
  <link href="http://fixedd.com/" rel="alternate"/>
  <id>http://fixedd.com/activities</id>
  <updated>2008-08-07T00:54:02Z</updated>
  <author>
    <name>Jeremy Logan (aka. fixedd)</name>
  </author>
  <entry>
    <title>Jyte Statistics 1: Text-Based</title>
    <link href="http://fixedd.com/articles/article/Jyte%20Statistics%201:%20Text-Based" rel="alternate"/>
    <id>http://fixedd.com/articles/article/Jyte%20Statistics%201:%20Text-Based</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>This article gives step-by-step instructions on how you could reimplement my Jyte Statistics project.</summary>
    <content type="html">
&lt;p&gt;I was asked by some of the users of my Jyte Stats/Charts if I would share the source code for my different Jyte Statistics projects. In lue of that I've decided to write an article explaining what I did. You're free to do whatever you want with the code in the articles, but I'd ask that you credit me if you make use of it.&lt;/p&gt;
&lt;p&gt;For the all of the articles I'm going to use the following conventions:&lt;/p&gt;
&lt;ul class="explain"&gt;
    &lt;li class="explain_black"&gt;&lt;p&gt;Black Backgrounds represents commands that is to be typed into a shell (command line) prompt. There will be one command per line.&lt;/p&gt;&lt;/li&gt;
    &lt;li class="explain_yellow"&gt;&lt;p&gt;Yellow Backgrounds indicates that the enclosed text is a filename.&lt;/p&gt;&lt;/li&gt;
    &lt;li class="explain_blue"&gt;&lt;p&gt;Blue Backgrounds represent actual source-code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the first article in the Jyte Statistics series we're going to cover the text based statistics as most of the concepts that the other charts use as demonstrated here. As I'm still pretty new to Rails myself I'm not going to say that anything I show you below is the best way to do things, it's just how I did it.&lt;/p&gt;

&lt;p&gt;Before we get started I'm going to assume a few things;&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;That you have Ruby and Rails installed.&lt;/li&gt;
    &lt;li&gt;That you have some basic understanding of both Ruby and Rails.&lt;/li&gt;
    &lt;li&gt;That you have SQLite3 (prefered for simplicity) or some other database available and setup.&lt;/li&gt;
    &lt;li&gt;That you can configure Rails to use the aforementioned database (if it isn't SQLite3).&lt;/li&gt;
    &lt;li&gt;That you're using a version of Rails that is at least 2.0.0.&lt;/li&gt;
    &lt;li&gt;That you can either perform this exercise in a Linux environment or figure out how to translate it to your environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let's begin.&lt;/p&gt;

&lt;p&gt;Create a new Rails app named &lt;q&gt;myapp&lt;/q&gt; and kill the pesky &lt;code class="file"&gt;./public/index.html&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class="command_line"&gt;&lt;code&gt;rails myapp
cd myapp
rm ./public/index.html&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I don't want to be constantly hitting the Jyte servers when the stats don't really change all that quickly so I'm going to store everyone's data in a database and only refresh it every 15 minutes. &lt;/p&gt;
&lt;p&gt;To access the data we'll need to create a new model named &lt;q&gt;Jyter&lt;/q&gt;:&lt;/p&gt;
&lt;pre class="command_line"&gt;&lt;code&gt;./script/generate model Jyter&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make the generated class file (&lt;code class="file"&gt;/app/models/jyter.rb&lt;/code&gt;) look like this:&lt;/p&gt;
&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Jyter&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;
    &lt;span class="ident"&gt;validates_uniqueness_of&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:identifier&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;validates_numericality_of&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:claims&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:comments&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:agree&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:disagree&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:reverse&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:total_votes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;validates_presence_of&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:identifier&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:claims&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:comments&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:agree&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:disagree&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:reverse&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:total_votes&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="comment"&gt;# this will return the first (and supposidly only) record for the supplied identifier&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.find_by_openid&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;find&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;
            &lt;span class="symbol"&gt;:first&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
            &lt;span class="symbol"&gt;:conditions&lt;/span&gt; &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:identifier&lt;/span&gt; &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;}&lt;/span&gt;
        &lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;and edit the generated migration (&lt;code class="file"&gt;./db/migrate/001_create_jyters.rb&lt;/code&gt;) so it reads:&lt;/p&gt;
&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;CreateJyters&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Migration&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.up&lt;/span&gt;
        &lt;span class="ident"&gt;create_table&lt;/span&gt; &lt;span class="symbol"&gt;:jyters&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;timestamps&lt;/span&gt;

            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;column&lt;/span&gt; &lt;span class="symbol"&gt;:identifier&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;        &lt;span class="symbol"&gt;:string&lt;/span&gt;
            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;column&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;         &lt;span class="symbol"&gt;:string&lt;/span&gt;
            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;column&lt;/span&gt; &lt;span class="symbol"&gt;:claims&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;        &lt;span class="symbol"&gt;:integer&lt;/span&gt;
            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;column&lt;/span&gt; &lt;span class="symbol"&gt;:comments&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;        &lt;span class="symbol"&gt;:integer&lt;/span&gt;
            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;column&lt;/span&gt; &lt;span class="symbol"&gt;:agree&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;        &lt;span class="symbol"&gt;:integer&lt;/span&gt;
            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;column&lt;/span&gt; &lt;span class="symbol"&gt;:disagree&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;        &lt;span class="symbol"&gt;:integer&lt;/span&gt;
            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;column&lt;/span&gt; &lt;span class="symbol"&gt;:reverse&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;        &lt;span class="symbol"&gt;:integer&lt;/span&gt;
            &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;column&lt;/span&gt; &lt;span class="symbol"&gt;:total_votes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;         &lt;span class="symbol"&gt;:integer&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.down&lt;/span&gt;
        &lt;span class="ident"&gt;drop_table&lt;/span&gt; &lt;span class="symbol"&gt;:jyters&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Now Rails (or more specifically, Rake) knows how to &lt;q&gt;migrate&lt;/q&gt; the Jyters table.&lt;/p&gt;

&lt;p&gt;Next tell Rails how to talk to your database (I'm using SQLite3 here) by editing &lt;code class="file"&gt;./config/database.yml&lt;/code&gt; (this will be the default for many people):&lt;/p&gt;
&lt;pre&gt;&lt;span class="key"&gt;development&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="key"&gt;adapter&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; sqlite3
  &lt;span class="key"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; db/development.sqlite3
  &lt;span class="key"&gt;timeout&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; 5000
&lt;span class="key"&gt;test&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="key"&gt;adapter&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; sqlite3
  &lt;span class="key"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; db/test.sqlite3
  &lt;span class="key"&gt;timeout&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; 5000
&lt;span class="key"&gt;production&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt;
  &lt;span class="key"&gt;adapter&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; sqlite3
  &lt;span class="key"&gt;database&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; db/production.sqlite3
  &lt;span class="key"&gt;timeout&lt;/span&gt;&lt;span class="punct"&gt;:&lt;/span&gt; 5000&lt;/pre&gt;

&lt;p&gt;Now that Rails/Rake know how to talk to the database and how to build our table we tell them to do it:&lt;/p&gt;
&lt;pre class="command_line"&gt;&lt;code&gt;rake db:migrate&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Ok... that's it for the Model/database. On to the Controller:&lt;/p&gt;

&lt;p&gt;Firstly I built a number of methods into the ApplicationController class in &lt;code class="file"&gt;./app/controllers/application.rb&lt;/code&gt;. Some of these didn't really need to be seperate methods, but I thought it handy since I reuse this code for all the Jyte Statistics projects. I'll briefly describe them now:&lt;/p&gt;

&lt;p&gt;This one's dead simple, it simply divides the first number by the second to give us our ratio:&lt;/p&gt;
&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;get_ratio&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;first&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;second&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;first&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_f&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt; &lt;span class="ident"&gt;second&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_f&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;The next method formats the numbers so they'll look pretty. It makes sure everything only has 2 digits after the decimal and it handles infinite and division by zero conditions:&lt;/p&gt;
&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;jyte_format&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;to_format&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;to_format&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0.00&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;to_format&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nan?&lt;/span&gt; &lt;span class="comment"&gt;# make sure it's a valid number&lt;/span&gt;
    &lt;span class="ident"&gt;to_format&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;Infinity&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;to_format&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;infinite?&lt;/span&gt; &lt;span class="comment"&gt;# make sure we catch infinites&lt;/span&gt;
    &lt;span class="ident"&gt;to_format&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;sprintf&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;%0.02f&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;to_format&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;to_format&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Float&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="comment"&gt;# format the float to only have 2 decimal places&lt;/span&gt;
    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;to_format&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Here's where we're starting to get into a little logic. This method asks the database if we have a record for this user that's less than 15 minutes old and if we don't then it updates.&lt;/p&gt;
&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;get_jyte_stats_hash&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;identifier&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nil?&lt;/span&gt;

    &lt;span class="ident"&gt;user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Jyter&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_by_openid&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="comment"&gt;# do we have a record, or a recent enough record, for the user to use?  if not create it!&lt;/span&gt;
    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nil?&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;or&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;updated_at&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Time&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="number"&gt;15&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;minutes&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;
        &lt;span class="comment"&gt;# create/update the user&lt;/span&gt;
        &lt;span class="ident"&gt;user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;record_jyte_profile&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt; 

    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;This is where the fun takes place. The short description for what's going on is that we're grabbing a user's profile page from Jyte and extracting the information we want from it. I've tried to include enough comments that you'll be able to understand what's going on.&lt;/p&gt;
&lt;pre&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;record_jyte_profile&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;identifier&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nil?&lt;/span&gt;

    &lt;span class="comment"&gt;# if the user already exists, grab them&lt;/span&gt;
    &lt;span class="ident"&gt;user&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Jyter&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_by_openid&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        
    &lt;span class="comment"&gt;# pull in the library we'll use for making the HTTP connection to Jyte&lt;/span&gt;
    &lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;net/http&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    
    &lt;span class="ident"&gt;data_from_jyte&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;
    
    &lt;span class="comment"&gt;# get the user page from Jyte&lt;/span&gt;
    &lt;span class="ident"&gt;response&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Net&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;HTTP&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;get_response&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;URI&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;parse&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;http://jyte.com/profile/&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;
    &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;
        &lt;span class="comment"&gt;# on Success continue&lt;/span&gt;
        &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="constant"&gt;Net&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;HTTPSuccess&lt;/span&gt; 
            &lt;span class="ident"&gt;resp&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;body&lt;/span&gt;    
        
            &lt;span class="comment"&gt;# grab the user's current name (alias)&lt;/span&gt;
            &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;resp&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;title&gt;Jyte - Profile for (.+)&amp;lt;&lt;span class="escape"&gt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
                &lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$1&lt;/span&gt;
            
                &lt;span class="comment"&gt;# pull out all the numberic data&lt;/span&gt;
                &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;resp&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;has made (&lt;span class="escape"&gt;d&lt;/span&gt;+) claims?,&lt;span class="escape"&gt; &lt;/span&gt;+(&lt;span class="escape"&gt;d&lt;/span&gt;+) comments?,&lt;span class="escape"&gt; &lt;/span&gt;+agreed with (&lt;span class="escape"&gt;d&lt;/span&gt;+) claims?,&lt;span class="escape"&gt; &lt;/span&gt;+disagreed with (&lt;span class="escape"&gt;d&lt;/span&gt;+), and changed positions&lt;span class="escape"&gt; &lt;/span&gt;+(&lt;span class="escape"&gt;d&lt;/span&gt;+) times?&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;m&lt;/span&gt;
                    &lt;span class="comment"&gt;# if we already have a record for that user in the DB update them then return the updated record&lt;/span&gt;
                    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;user&lt;/span&gt;
                        &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;Jyter&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;update&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;
                            &lt;span class="ident"&gt;user&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:id&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt;
                            &lt;span class="punct"&gt;{&lt;/span&gt;
                                &lt;span class="symbol"&gt;:name&lt;/span&gt;         &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                                &lt;span class="symbol"&gt;:identifier&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                                &lt;span class="symbol"&gt;:claims&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$1&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                                &lt;span class="symbol"&gt;:comments&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$2&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                                &lt;span class="symbol"&gt;:agree&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$3&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                                &lt;span class="symbol"&gt;:disagree&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$4&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                                &lt;span class="symbol"&gt;:reverse&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$5&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                                &lt;span class="symbol"&gt;:total_votes&lt;/span&gt;     &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="global"&gt;$3&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="global"&gt;$4&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;
                            &lt;span class="punct"&gt;}&lt;/span&gt;
                        &lt;span class="punct"&gt;)&lt;/span&gt;
                    &lt;span class="comment"&gt;# if we don't have a record for that user in the DB create one then return the new record&lt;/span&gt;
                    &lt;span class="keyword"&gt;else&lt;/span&gt;
                        &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;Jyter&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;create&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;
                            &lt;span class="symbol"&gt;:name&lt;/span&gt;         &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                            &lt;span class="symbol"&gt;:identifier&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="ident"&gt;identifier&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                            &lt;span class="symbol"&gt;:claims&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$1&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                            &lt;span class="symbol"&gt;:comments&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$2&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                            &lt;span class="symbol"&gt;:agree&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$3&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                            &lt;span class="symbol"&gt;:disagree&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$4&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                            &lt;span class="symbol"&gt;:reverse&lt;/span&gt;        &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="global"&gt;$5&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                            &lt;span class="symbol"&gt;:total_votes&lt;/span&gt;     &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="global"&gt;$3&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="global"&gt;$4&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;to_i&lt;/span&gt;
                        &lt;span class="punct"&gt;)&lt;/span&gt;
                    &lt;span class="keyword"&gt;end&lt;/span&gt;
                &lt;span class="keyword"&gt;end&lt;/span&gt;
            &lt;span class="keyword"&gt;end&lt;/span&gt;    
        &lt;span class="comment"&gt;# on anything other that Success we fail out&lt;/span&gt;
        &lt;span class="keyword"&gt;else&lt;/span&gt;
            &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Ok, that's all the heavy lifting. No we just need to get that info to the user.&lt;/p&gt;

&lt;p&gt;First, we create a &lt;q&gt;charts&lt;/q&gt; controller.&lt;/p&gt;

&lt;pre class="command_line"&gt;&lt;code&gt;./script/generate controller charts&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Inside the charts controller class file (&lt;code class="file"&gt;./app/controllers/charts_controller.db&lt;/code&gt;) we need to create an &lt;q&gt;index&lt;/q&gt; action. Make your file look like the following:&lt;/p&gt;
&lt;pre&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;ChartsController&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;index&lt;/span&gt;
        &lt;span class="comment"&gt;# if we've gotten an openid identifier from the form then gather the data and display it to the user&lt;/span&gt;
        &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt; &lt;span class="symbol"&gt;:identifier&lt;/span&gt;
            &lt;span class="comment"&gt;# get the data from the database (remember that this will also update the DB if necessary)&lt;/span&gt;
            &lt;span class="attribute"&gt;@data&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;get_jyte_stats_hash&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:identifier&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;
            &lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:comments_to_claims&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;     &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;jyte_format&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt; &lt;span class="ident"&gt;get_ratio&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:comments&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt;     &lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:claims&lt;/span&gt;&lt;span class="punct"&gt;]))&lt;/span&gt;
            &lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:votes_to_claims&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;     &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;jyte_format&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt; &lt;span class="ident"&gt;get_ratio&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:total_votes&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt;     &lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:claims&lt;/span&gt;&lt;span class="punct"&gt;]))&lt;/span&gt;
            &lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:agree_to_disagree&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;     &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;jyte_format&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt; &lt;span class="ident"&gt;get_ratio&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:agree&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt;         &lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:disagree&lt;/span&gt;&lt;span class="punct"&gt;]))&lt;/span&gt;
            &lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:votes_per_switch&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;     &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;jyte_format&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt; &lt;span class="ident"&gt;get_ratio&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:total_votes&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt;     &lt;span class="attribute"&gt;@data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:reverse&lt;/span&gt;&lt;span class="punct"&gt;]))&lt;/span&gt;

            &lt;span class="comment"&gt;# show the output view&lt;/span&gt;
            &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="symbol"&gt;:jyte_stats_output&lt;/span&gt;
        &lt;span class="comment"&gt;# we didn't get any identifiers so we need to show the form&lt;/span&gt;
        &lt;span class="keyword"&gt;else&lt;/span&gt;
            &lt;span class="comment"&gt;# show the form view&lt;/span&gt;
            &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="symbol"&gt;:jyte_stats_form&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;You may have noticed the &lt;q&gt;render&lt;/q&gt; statements from the block above. For those we'll need to create two files. We'll need a page with a form for them to input the data into and a page for the output.&lt;/p&gt;
&lt;p&gt;First, the form. Create &lt;code class="file"&gt;./app/views/charts/jyte_stats_form.rhtml&lt;/code&gt; and make it look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;% form_tag({:action =&gt; :index}, {:method =&amp;gt; :get}) do %&amp;gt;
    &amp;lt;label for="jyte_lookup_identifier"&amp;gt;Please input the user you wish to look up: http://jyte.com/profile/&amp;lt;/label&amp;gt;
    &amp;lt;%= text_field_tag(:identifier, 'fixedd.com', {:id =&amp;gt; 'jyte_lookup_identifier'}) %&amp;gt;
    &amp;lt;%= submit_tag('check') %&amp;gt;
&amp;lt;% end %&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, the output page. Create &lt;code class="file"&gt;./app/views/charts/jyte_stats_output.rhtml&lt;/code&gt; and make it look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h3&amp;gt;Results:&amp;lt;/h3&amp;gt;
&amp;lt;h4&amp;gt;As of &amp;lt;%= Date.today %&amp;gt; &amp;lt;%= @data[:name] %&amp;gt; has the following stats:&amp;lt;/h4&amp;gt;
&amp;lt;h5&amp;gt;Ratios:&amp;lt;/h5&amp;gt;
&amp;lt;dl&amp;gt;
    &amp;lt;dt&amp;gt;Comments-to-Claims&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:comments_to_claims] %&amp;gt; (&amp;lt;%= @data[:comments] %&amp;gt; / &amp;lt;%= @data[:claims] %&amp;gt;)&amp;lt;/dd&amp;gt;

    &amp;lt;dt&amp;gt;Votes-to-Claims&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:votes_to_claims] %&amp;gt; (&amp;lt;%= @data[:total_votes] %&amp;gt; / &amp;lt;%= @data[:claims] %&amp;gt;)&amp;lt;/dd&amp;gt;

    &amp;lt;dt&amp;gt;Agreed-to-Disagreed&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:agree_to_disagree] %&amp;gt; (&amp;lt;%= @data[:agree] %&amp;gt; / &amp;lt;%= @data[:disagree] %&amp;gt;)&amp;lt;/dd&amp;gt;

    &amp;lt;dt&amp;gt;Votes-Per-Switches&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:votes_per_switch] %&amp;gt; (&amp;lt;%= @data[:total_votes] %&amp;gt; / &amp;lt;%= @data[:reverse] %&amp;gt;)&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;

&amp;lt;h5&amp;gt;Raw Stats:&amp;lt;/h5&amp;gt;
&amp;lt;p&amp;gt;This is everything that the user has done.&amp;lt;/p&amp;gt;
&amp;lt;dl&amp;gt;
    &amp;lt;dt&amp;gt;Claims&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:claims] %&amp;gt;&amp;lt;/dd&amp;gt;

    &amp;lt;dt&amp;gt;Comments&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:comments] %&amp;gt;&amp;lt;/dd&amp;gt;

    &amp;lt;dt&amp;gt;Agreed&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:agree] %&amp;gt;&amp;lt;/dd&amp;gt;

    &amp;lt;dt&amp;gt;Disagreed&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:disagree] %&amp;gt;&amp;lt;/dd&amp;gt;

    &amp;lt;dt&amp;gt;Reversals&amp;lt;/dt&amp;gt;
    &amp;lt;dd&amp;gt;&amp;lt;%= @data[:reverse] %&amp;gt;&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That's it, you're done. To test it out simply run:&lt;/p&gt;
&lt;pre class="command_line"&gt;&lt;code&gt;./script/server&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then navigate your browser to &lt;a href="http://0.0.0.0:3000/charts" rel="nofollow"&gt;http://0.0.0.0:3000/charts&lt;/a&gt; and you should see your app.&lt;/p&gt;

&lt;p&gt;Now that you've done all that by hand I'm going to be mean and offer the code for download: &lt;a href="http://fixedd.com/project_files/jyte_stats_demo_1.zip"&gt;Jyte Statistics 1: Text-Based Source Files&lt;/a&gt;.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>AT&amp;amp;T Wireless DNS Correction Script</title>
    <link href="http://fixedd.com/projects/att_dns_correction" rel="alternate"/>
    <id>http://fixedd.com/projects/att_dns_correction</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>Getting my Laptop Connect &lt;acronym title="Personal Computer Memory Card International Association"&gt;PCMCIA&lt;/acronym&gt; PC-Card working under Linux was time-consuming, at best. Even after I got it working AT&amp;amp;T would still set the &lt;acronym title="Domain Name Servics"&gt;DNS&lt;/acronym&gt; incorrectly about 1/3 of the time. This script corrects that.</summary>
    <content type="text">
Getting my Laptop Connect &lt;acronym title="Personal Computer Memory Card International Association"&gt;PCMCIA&lt;/acronym&gt; PC-Card working under Linux was time-consuming, at best. Even after I got it working AT&amp;amp;T would still set the &lt;acronym title="Domain Name Servics"&gt;DNS&lt;/acronym&gt; incorrectly about 1/3 of the time. This script corrects that.    </content>
  </entry>
  <entry>
    <title>Site Scanner</title>
    <link href="http://fixedd.com/projects/site_scanner" rel="alternate"/>
    <id>http://fixedd.com/projects/site_scanner</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>A script to scan a website looking for dead links.</summary>
    <content type="text">
A script to scan a website looking for dead links.    </content>
  </entry>
  <entry>
    <title>Jyte Stats</title>
    <link href="http://fixedd.com/projects/jyte_stats" rel="alternate"/>
    <id>http://fixedd.com/projects/jyte_stats</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>A quick-and-dirty way to view any Jyte users' statistics.</summary>
    <content type="text">
A quick-and-dirty way to view any Jyte users' statistics.    </content>
  </entry>
  <entry>
    <title>Jyte Charts (Flash-based)</title>
    <link href="http://fixedd.com/projects/jyte_chart_flash" rel="alternate"/>
    <id>http://fixedd.com/projects/jyte_chart_flash</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>This project allows you to generate a set of Flash-based charts (that illustrate your general statistics) for your Jyte profile page. It makes use of the &lt;a href="http://www.maani.us/charts/index.php"&gt;SWF Charts library&lt;/a&gt;.</summary>
    <content type="text">
This project allows you to generate a set of Flash-based charts (that illustrate your general statistics) for your Jyte profile page. It makes use of the &lt;a href="http://www.maani.us/charts/index.php"&gt;SWF Charts library&lt;/a&gt;.    </content>
  </entry>
  <entry>
    <title>Jyte Charts (Image-based)</title>
    <link href="http://fixedd.com/projects/jyte_chart_image" rel="alternate"/>
    <id>http://fixedd.com/projects/jyte_chart_image</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>This project allows you to generate a set of &lt;acronym title="Portable Network Graphics"&gt;PNG&lt;/acronym&gt; charts (that illustrate your general statistics) for your Jyte profile page. It makes use of the &lt;a href="http://code.google.com/apis/chart/"&gt;Google Charts API&lt;/a&gt;.</summary>
    <content type="text">
This project allows you to generate a set of &lt;acronym title="Portable Network Graphics"&gt;PNG&lt;/acronym&gt; charts (that illustrate your general statistics) for your Jyte profile page. It makes use of the &lt;a href="http://code.google.com/apis/chart/"&gt;Google Charts API&lt;/a&gt;.    </content>
  </entry>
  <entry>
    <title>Microsoft Points Converter</title>
    <link href="http://fixedd.com/projects/microsoft_points" rel="alternate"/>
    <id>http://fixedd.com/projects/microsoft_points</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>A quick-and-dirty way to convert the XBox 360's Microsoft Points to real money.</summary>
    <content type="text">
A quick-and-dirty way to convert the XBox 360's Microsoft Points to real money.    </content>
  </entry>
  <entry>
    <title>Phone Number Info Scraper</title>
    <link href="http://fixedd.com/projects/phone_scraper" rel="alternate"/>
    <id>http://fixedd.com/projects/phone_scraper</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>This is a script I worked up for a relative that needed to look a large number of phone numbers as quickly as possible. It scrapes the data from the &lt;a href="http://superpages.com"&gt;SuperPages.com&lt;/a&gt; web site and saves it to a &lt;acronym title="Comma Seperated Value"&gt;CSV&lt;/acronym&gt; file.</summary>
    <content type="text">
This is a script I worked up for a relative that needed to look a large number of phone numbers as quickly as possible. It scrapes the data from the &lt;a href="http://superpages.com"&gt;SuperPages.com&lt;/a&gt; web site and saves it to a &lt;acronym title="Comma Seperated Value"&gt;CSV&lt;/acronym&gt; file.    </content>
  </entry>
  <entry>
    <title>Jyte Cred Bar</title>
    <link href="http://fixedd.com/projects/jyte_cred_bar" rel="alternate"/>
    <id>http://fixedd.com/projects/jyte_cred_bar</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>This little widget thingy allows you to embed an image into your Jyte profile that shows your current cred standings.</summary>
    <content type="text">
This little widget thingy allows you to embed an image into your Jyte profile that shows your current cred standings.    </content>
  </entry>
  <entry>
    <title>Jyte Cred Deficit</title>
    <link href="http://fixedd.com/projects/jyte_deficit" rel="alternate"/>
    <id>http://fixedd.com/projects/jyte_deficit</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>This project page allows you to check to see if a specific Jyte user is running a cred deficit.</summary>
    <content type="text">
This project page allows you to check to see if a specific Jyte user is running a cred deficit.    </content>
  </entry>
  <entry>
    <title>Jyte API library for PHP 5: Services_Jyte</title>
    <link href="http://fixedd.com/projects/services_jyte" rel="alternate"/>
    <id>http://fixedd.com/projects/services_jyte</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>Services_Jyte is a &lt;acronym title="PHP Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt; implementation of the Jyte &lt;acronym title="Application Programming Interface"&gt;API&lt;/acronym&gt;.</summary>
    <content type="text">
Services_Jyte is a &lt;acronym title="PHP Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt; implementation of the Jyte &lt;acronym title="Application Programming Interface"&gt;API&lt;/acronym&gt;.    </content>
  </entry>
  <entry>
    <title>Security Through Obscurity is Neither</title>
    <link href="http://fixedd.com/articles/article/Security%20Through%20Obscurity%20is%20Neither" rel="alternate"/>
    <id>http://fixedd.com/articles/article/Security%20Through%20Obscurity%20is%20Neither</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>This article shows how to defeat a certain type of obfuscated PHP source code.</summary>
    <content type="html">
&lt;p&gt;Recently one of my friends was working with some commercial PHP software where the code had been heavily obfuscated. He needed to customize it, so he, obviously, needed access to the source code. The original authors had run it through a long line of things like gzdeflate(), base64_encode(), etc then wrapped it in an eval. Ultimately it ended up looking something like this (I deleted about 300 lines of rubbish for sanity):&lt;/p&gt;
&lt;pre&gt;
&lt;span class="keyword"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="method"&gt;eval&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="method"&gt;gzinflate&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="method"&gt;base64_decode&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;'&lt;span class="string"&gt;FZzHjuNAlkV/ZXbTDS7oHWa6G/SkSNH7zYDee8+v
HyVQmypAylBEvHvPUUn5n3//73/mev6v4kz6f1RvM5Z9shf/SJOtILD/y4tsyot//LcYv3K3mUzn
2aCdFj5T5ZhIHBazYw7aUwimQShr8vBDjt8oithczMM4xboabM8SJGmU8GOwFwui5E1gQMaka0xq
puNjzgR6pMUho0UgA0Eh1sN3LDSpvJhTQ3jcWFYeel+swFlCnk66rfGhueJ9Fu8hOFA246x7lA2W
AN6lYxI0wzhRDVU2GnhJ6HES6jwOZPsYMeJ24CgakRzn6FZjvYxZNpk4TIl4NPHICot4u4ioC28v
OQFdtMldIgk/IvSM2VoZRpU593jzFjSAfvIgI8f9qQWehgrwHQpo4ySQQVNgk5MBd1jDLDw+QpUn
A/jVL7wDzrpFpsluE7fvnr/coyqIrtXUyYh+TFJ5uSfcdz5PnQncr0ewMJc1vFxrDVWRt+Rv&lt;/span&gt;'&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;?&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The problem was that every time you ran an eval() on it you were left with another obfuscated layer. After decoding down 2-3 layers I quickly realized that this was a losing battle and I needed to let code do my work for me, a good thing too. On the first file I decoded there were 122 layers of obfuscation.&lt;/p&gt;
&lt;p&gt;Here's the code I wrote to do the work:&lt;/p&gt;
&lt;pre&gt;
&lt;span class="keyword"&gt;&amp;lt;?php&lt;/span&gt;
    &lt;span class="comment"&gt;// this wants to be run like this:&lt;/span&gt;
    &lt;span class="comment"&gt;//     $php decoder.php input_file.php&lt;/span&gt;

    &lt;span class="comment"&gt;// open the input file&lt;/span&gt;
    $fh &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="method"&gt;fopen&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$argv[1]&lt;span class="punct"&gt;,&lt;/span&gt; '&lt;span class="string"&gt;r&lt;/span&gt;'&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
    $encoded_script &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="method"&gt;fread&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$fh&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="method"&gt;filesize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$argv[1]&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
    &lt;span class="method"&gt;fclose&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$fh&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
    
    &lt;span class="comment"&gt;// decode&lt;/span&gt;
    &lt;span class="keyword"&gt;while&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;true&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;
        &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="method"&gt;preg_match&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;'&lt;span class="string"&gt;/^\??&amp;gt;?&amp;lt;\?php\s+eval(.+)\s+\?&amp;gt;&amp;lt;?\??/s&lt;/span&gt;'&lt;span class="punct"&gt;,&lt;/span&gt; $encoded_script&lt;span class="punct"&gt;,&lt;/span&gt; $matches&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
            $encoded_script &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="method"&gt;eval&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;'&lt;span class="string"&gt;return &lt;/span&gt;' &lt;span class="punct"&gt;.&lt;/span&gt; $matches[1] &lt;span class="punct"&gt;.&lt;/span&gt; '&lt;span class="string"&gt;;&lt;/span&gt;'&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
        else
            &lt;span class="keyword"&gt;break&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
    &lt;span class="punct"&gt;}&lt;/span&gt;
    
    &lt;span class="comment"&gt;// write the output file&lt;/span&gt;
    $fh &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="method"&gt;fopen&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$argv[1] &lt;span class="punct"&gt;.&lt;/span&gt; '&lt;span class="string"&gt;.decoded&lt;/span&gt;'&lt;span class="punct"&gt;,&lt;/span&gt; '&lt;span class="string"&gt;w&lt;/span&gt;'&lt;span class="punct"&gt;)&lt;/span&gt; or &lt;span class="method"&gt;die&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;"&lt;span class="string"&gt;can't open file&lt;/span&gt;"&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
    &lt;span class="method"&gt;fwrite&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$fh&lt;span class="punct"&gt;,&lt;/span&gt; $encoded_script&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
    &lt;span class="method"&gt;fclose&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$fh&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;;&lt;/span&gt;
&lt;span class="keyword"&gt;?&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This simply starts up a loop that runs the eval() as many times as it takes to get to the real source. It then writes the decoded source to a new file. This is pretty specific to the source files in question, but I'm sure it could prove useful as a starting point if you ever come across anything like this.&lt;/p&gt;
    </content>
  </entry>
  <entry>
    <title>Random Array Sorting is Tricky</title>
    <link href="http://fixedd.com/articles/article/Random%20Array%20Sorting%20is%20Tricky" rel="alternate"/>
    <id>http://fixedd.com/articles/article/Random%20Array%20Sorting%20is%20Tricky</id>
    <updated>2008-08-07T00:54:02Z</updated>
    <author>
      <name>Jeremy Logan</name>
    </author>
    <summary>This article shows how drastically different the results of different array randomization techniques will be.</summary>
    <content type="html">
&lt;p&gt;I was asked in an interview recently how I would go about randomizing the order of an array if the language/framework didn't supply any functions/methods to do it for me (I don't think he really cared what my answer was... just wanted to see how I thought). When I was answering the question it was a psuedo-code, but I'm going to use PHP here since that's what the job has me writing most the time (he offered me the job and I took it).&lt;/p&gt;

&lt;p&gt;My solution came out something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="keyword"&gt;&amp;lt;?php&lt;/span&gt;
    &lt;span class="comment"&gt;// this loops over the source array &amp;quot;popping&amp;quot; a random element out of it &lt;/span&gt;
    &lt;span class="comment"&gt;// and placing it in the new array until the source array is empty&lt;/span&gt;
    &lt;span class="keyword"&gt;function&lt;/span&gt; randomize_array&lt;span class="punct"&gt;(&lt;/span&gt;$array_to_randomize&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;
        $new_array = &lt;span class="method"&gt;array&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;;
        &lt;span class="keyword"&gt;while&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="method"&gt;count&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$array_to_randomize&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; 0&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;
            $rand_num = &lt;span class="method"&gt;rand&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;0&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="method"&gt;count&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$array_to_randomize&lt;span class="punct"&gt;)&lt;/span&gt;&lt;span class="punct"&gt;-&lt;/span&gt;1&lt;span class="punct"&gt;)&lt;/span&gt;;
            $extracted = &lt;span class="method"&gt;array_splice&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$array_to_randomize&lt;span class="punct"&gt;,&lt;/span&gt; $rand_num&lt;span class="punct"&gt;,&lt;/span&gt; 1&lt;span class="punct"&gt;)&lt;/span&gt;;
            $new_array&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; = $extracted&lt;span class="punct"&gt;[&lt;/span&gt;0&lt;span class="punct"&gt;]&lt;/span&gt;;
        &lt;span class="punct"&gt;}&lt;/span&gt;
        &lt;span class="keyword"&gt;return&lt;/span&gt; $new_array;
    &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="keyword"&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I was mostly okay with that solution because it 1) worked and 2) didn't seem to waste too much time, I mean, it was just moving the items over one random piece at a time.&lt;/p&gt;

&lt;p&gt;Well, the interviewer (my new boss) suggested a different solution (not suggesting it as any more correct, just as an alternative) and as soon as he got the first word out of his mouth I had one of those "ah ha!" moments where I knew what he was getting at before he said it because the same idea had been knocking around the back of my head already (just hadn't worked it's way to the forefront). His suggestion was something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="keyword"&gt;&amp;lt;?php&lt;/span&gt;
    &lt;span class="comment"&gt;// this uses the custom sorting mechanism of arrays to generate a fake order&lt;/span&gt;
    &lt;span class="keyword"&gt;function&lt;/span&gt; randomize_array&lt;span class="punct"&gt;(&lt;/span&gt;$array_to_randomize&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;
        &lt;span class="method"&gt;usort&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;$array_to_randomize, "rand_sort"&lt;span class="punct"&gt;)&lt;/span&gt;;
        &lt;span class="keyword"&gt;return&lt;/span&gt; $array_to_randomize;
    &lt;span class="punct"&gt;}&lt;/span&gt;
    &lt;span class="keyword"&gt;function&lt;/span&gt; rand_sort&lt;span class="punct"&gt;(&lt;/span&gt;$a, $b&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;
        &lt;span class="keyword"&gt;return&lt;/span&gt; rand&lt;span class="punct"&gt;(&lt;/span&gt;-1, 1&lt;span class="punct"&gt;)&lt;/span&gt;;
    &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="keyword"&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I ran some time comparisons and (on my machine) sorting a 26-element array 100,000 times using both methods yielded a time difference of about 0.08 seconds (the second method was the fastest). In light of this I &lt;a href="http://fixedd.livejournal.com/56963.html"&gt;asked my programmer buddies what they thought&lt;/a&gt; and they had some really great responses (it pays to have smart friends). The gist was that in most modern scripting languages the first method gives you a truly random sorting whereas the second method only gives you the illusion of randomizization. It basically has to do with the fact that they don't compare every element to every other... put another (incredibly simplistic) way, the optimizations do things like: if you have three elements in your array; A, B, and C then it assumes that if &lt;code&gt;A &gt; B&lt;/code&gt; and &lt;code&gt;B &gt; C&lt;/code&gt; then &lt;code&gt;A &gt; C&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Well... I decided to do some tests (&lt;a href="http://fixedd.livejournal.com/56963.html?thread=178563#t178563"&gt;inspired by Paul&lt;/a&gt;). I ran both methods a million times and tallied the results. Here's the output:&lt;/p&gt;

&lt;pre&gt;&lt;samp&gt;Method 1:
    [3, 2, 1] =&gt; 166342
    [3, 1, 2] =&gt; 165804
    [2, 3, 1] =&gt; 167113
    [1, 2, 3] =&gt; 167414
    [2, 1, 3] =&gt; 166929
    [1, 3, 2] =&gt; 166398

Method 2:
    [3, 2, 1] =&gt; 444010
    [1, 3, 2] =&gt; 147944
    [1, 2, 3] =&gt; 259064
    [3, 1, 2] =&gt; 74641
    [2, 1, 3] =&gt; 24776
    [2, 3, 1] =&gt; 49565&lt;/samp&gt;&lt;/pre&gt;

&lt;p&gt;As you can see &lt;q&gt;Method 1&lt;/q&gt; has an almost perfect distribution whereas the difference in the most-to-least common in &lt;q&gt;Method 2&lt;/q&gt; is almost 18 times. What that means is that, using &lt;q&gt;Method 2&lt;/q&gt; you'll get &lt;samp&gt;[3, 2, 1]&lt;/samp&gt; 18 times more often than you'll get &lt;samp&gt;[2, 1, 3]&lt;/samp&gt;. I'm liking my first-instinct more all the time.&lt;/p&gt;
    </content>
  </entry>
</feed>
