<?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>Kickass Labs &#187; Rails</title>
	<atom:link href="http://www.kickasslabs.com/category/rails/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kickasslabs.com</link>
	<description>We &#9829; code.</description>
	<lastBuildDate>Sun, 04 Jul 2010 03:00:07 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Quick Hits: Sass demands syntax perfection!</title>
		<link>http://www.kickasslabs.com/2010/01/25/quick-hits-sass-demands-syntax-perfection/</link>
		<comments>http://www.kickasslabs.com/2010/01/25/quick-hits-sass-demands-syntax-perfection/#comments</comments>
		<pubDate>Mon, 25 Jan 2010 22:01:58 +0000</pubDate>
		<dc:creator>abel</dc:creator>
				<category><![CDATA[Quick Hits]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Sass]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=388</guid>
		<description><![CDATA[Just a quick note about sass.  Let&#8217;s say you had the following in /public/stylesheets/sass/foo.sass:
body
  line-height:1.2em

h1
  color:red
This would render a blank document for /public/stylesheets/foo.css.  You won&#8217;t get an error about it, you&#8217;ll just get a blank document.  Here&#8217;s the thing, when you&#8217;re declaring a CSS property, you MUST put a space after the colon.  [...]]]></description>
			<content:encoded><![CDATA[<p>Just a quick note about sass.  Let&#8217;s say you had the following in <strong>/public/stylesheets/sass/foo.sass</strong>:</p>
<pre>body
  line-height:1.2em

h1
  color:red</pre>
<p>This would render a <span style="text-decoration: underline;">blank</span> document for <strong>/public/stylesheets/</strong><strong>foo.css</strong>.  You won&#8217;t get an error about it, you&#8217;ll just get a blank document.  Here&#8217;s the thing, when you&#8217;re declaring a CSS property, you MUST put a space after the colon.  Failing to do so will just wind up in parsing errors on the sass side.</p>
<p>I hope I&#8217;ve saved you some head scratching. <img src='http://www.kickasslabs.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2010%2F01%2F25%2Fquick-hits-sass-demands-syntax-perfection%2F&amp;linkname=Quick%20Hits%3A%20Sass%20demands%20syntax%20perfection%21"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2010/01/25/quick-hits-sass-demands-syntax-perfection/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick Hits: Things to keep in mind</title>
		<link>http://www.kickasslabs.com/2009/05/17/quick-hits-things-to-keep-in-mind/</link>
		<comments>http://www.kickasslabs.com/2009/05/17/quick-hits-things-to-keep-in-mind/#comments</comments>
		<pubDate>Mon, 18 May 2009 02:32:55 +0000</pubDate>
		<dc:creator>abel</dc:creator>
				<category><![CDATA[Quick Hits]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=324</guid>
		<description><![CDATA[(1) Before you unpack your gems, navigate to your vendors directory:
/Users/[yourname]/[your-app]/vendor/gems: gem unpack [gem name]
It isn&#8217;t the end of the world if you don&#8217;t do it ahead of time, but it does make life slightly easier.
(2) Remember that when you find a bug, you should write a test for the expected behavior BEFORE fixing the [...]]]></description>
			<content:encoded><![CDATA[<p>(1) Before you unpack your gems, navigate to your vendors directory:</p>
<p>/Users/[yourname]/[your-app]/vendor/gems: gem unpack [gem name]</p>
<p>It isn&#8217;t the end of the world if you don&#8217;t do it ahead of time, but it does make life slightly easier.</p>
<p>(2) Remember that when you find a bug, you should write a test for the expected behavior BEFORE fixing the bug.  &#8220;The Rails Way&#8221; has a great quote about testing:</p>
<p>&#8220;It&#8217;s not that Rails encourages you to do test-driven development, it&#8217;s that it makes it difficult for you not to do test-driven development.&#8221; ~Brian Eng</p>
<p>I&#8217;ve interpreted this quote into the following mantra:</p>
<p>&#8220;It&#8217;s not that rails makes it easy to test, it that it makes you pay when you don&#8217;t&#8221;.</p>
<p>Finding a bug after creating your test suite just means that you missed a test.  Take the opportunity to correct that.</p>
<p>Happy hacking.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2009%2F05%2F17%2Fquick-hits-things-to-keep-in-mind%2F&amp;linkname=Quick%20Hits%3A%20Things%20to%20keep%20in%20mind"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2009/05/17/quick-hits-things-to-keep-in-mind/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Rails Gotcha: ActiveRecord Caches Associated Records by Default</title>
		<link>http://www.kickasslabs.com/2009/04/23/rails-gotcha-activerecord-caches-associated-records-by-default/</link>
		<comments>http://www.kickasslabs.com/2009/04/23/rails-gotcha-activerecord-caches-associated-records-by-default/#comments</comments>
		<pubDate>Fri, 24 Apr 2009 03:00:00 +0000</pubDate>
		<dc:creator>gabe</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[active record]]></category>
		<category><![CDATA[activerecord]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=258</guid>
		<description><![CDATA[ActiveRecord will cache the results of association method calls by default, unless you tell it not to. 
(This applies to Rails 2.3.2 and perhaps earlier versions.)
From the documentation:

project.milestones             # fetches milestones from the database
project.milestones.size        # uses [...]]]></description>
			<content:encoded><![CDATA[<p><strong>ActiveRecord will cache the results of association method calls by default, unless you tell it not to. </strong></p>
<p>(This applies to Rails 2.3.2 and perhaps earlier versions.)</p>
<p>From <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html">the documentation:</a></p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">project.<span style="color:#9900CC;">milestones</span>             <span style="color:#008000; font-style:italic;"># fetches milestones from the database</span>
project.<span style="color:#9900CC;">milestones</span>.<span style="color:#9900CC;">size</span>        <span style="color:#008000; font-style:italic;"># uses the milestone cache</span>
project.<span style="color:#9900CC;">milestones</span>.<span style="color:#9900CC;">empty</span>?      <span style="color:#008000; font-style:italic;"># uses the milestone cache</span>
project.<span style="color:#9900CC;">milestones</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">true</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">size</span>  <span style="color:#008000; font-style:italic;"># fetches milestones from the database</span></pre></div></div>

<p>Normally, this is great.  However, if you&#8217;re not aware of this default caching, you might see some strange behavior in your app or tests and have no idea what&#8217;s going on.  This default caching behavior had me and Abel stumped until we read about it in the docs.</p>
<p>Working with the same concept as the example in the documentation, where a Project has many Milestones, here&#8217;s a more explicit example of the caching behavior in action:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">project_1 = Project.<span style="color:#9900CC;">find_by_id</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">1</span><span style="color:#006600; font-weight:bold;">&#41;</span>  
project_2 = Project.<span style="color:#9900CC;">find_by_id</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">1</span><span style="color:#006600; font-weight:bold;">&#41;</span>  
<span style="color:#008000; font-style:italic;"># Load the same Project into two variables</span>
&nbsp;
project_1.<span style="color:#9900CC;">milestones</span>.<span style="color:#9900CC;">length</span>   
<span style="color:#008000; font-style:italic;"># - Hits the db's Milestones table.  </span>
<span style="color:#008000; font-style:italic;"># - Caches milestones object on project_1.</span>
<span style="color:#008000; font-style:italic;"># - Returns 0. </span>
&nbsp;
Milestone.<span style="color:#9900CC;">create</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:project_id</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006666;">1</span>, <span style="color:#ff3333; font-weight:bold;">:name</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'New Milestone'</span><span style="color:#006600; font-weight:bold;">&#41;</span>  
<span style="color:#008000; font-style:italic;"># Adds a milestone to the project. </span>
<span style="color:#008000; font-style:italic;"># But we don't do it through the project_1.milestones association</span>
<span style="color:#008000; font-style:italic;"># because that _would_ update project_1.milestones's cached value</span>
&nbsp;
project_1.<span style="color:#9900CC;">milestones</span>.<span style="color:#9900CC;">length</span>   
<span style="color:#008000; font-style:italic;"># Returns 0 (not 1, like you'd expect) </span>
<span style="color:#008000; font-style:italic;"># because project_1.milestones was cached when </span>
<span style="color:#008000; font-style:italic;"># previously requested above.</span>
&nbsp;
project_2.<span style="color:#9900CC;">milestones</span>.<span style="color:#9900CC;">length</span>  
<span style="color:#008000; font-style:italic;"># Returns 1, because project_2.milestones </span>
<span style="color:#008000; font-style:italic;"># hasn't been requested/cached yet.</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Note: project_2.milestones is </span>
<span style="color:#008000; font-style:italic;"># a COMPLETELY DIFFERENT IN-MEMORY OBJECT </span>
<span style="color:#008000; font-style:italic;"># than project_1.milestones.</span>
<span style="color:#008000; font-style:italic;"># Taking this point further, here's an explicit example:</span>
&nbsp;
project_2.<span style="color:#9900CC;">milestones</span> <span style="color:#006600; font-weight:bold;">&lt;&lt;</span> Milestone.<span style="color:#9900CC;">create</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:name</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">'Another Milestone'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#008000; font-style:italic;"># Put another milestone on the project through </span>
<span style="color:#008000; font-style:italic;"># project_2's milestones association.</span>
&nbsp;
project_1.<span style="color:#9900CC;">milestones</span>.<span style="color:#9900CC;">length</span>
<span style="color:#008000; font-style:italic;"># Still returns 0, because project_1 already cached it's copy of </span>
<span style="color:#008000; font-style:italic;"># the milestones association back when there were 0.</span>
&nbsp;
project_2.<span style="color:#9900CC;">milestones</span>.<span style="color:#9900CC;">length</span> 
<span style="color:#008000; font-style:italic;"># Now returns 2, because only project_2 knows about the new milestone.</span>
&nbsp;
project_1.<span style="color:#9900CC;">milestones</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">true</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">length</span>  
<span style="color:#008000; font-style:italic;"># Returns 2, because ActiveRecord updates the cache </span>
<span style="color:#008000; font-style:italic;"># when association(true) is present.</span></pre></div></div>

<p>Another funny something to note about the association caching behavior is that <strong>even when ActiveRecord uses a cached value, it still emits SQL to the log file</strong>.  So, don&#8217;t let that trip you up either.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2009%2F04%2F23%2Frails-gotcha-activerecord-caches-associated-records-by-default%2F&amp;linkname=Rails%20Gotcha%3A%20ActiveRecord%20Caches%20Associated%20Records%20by%20Default"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2009/04/23/rails-gotcha-activerecord-caches-associated-records-by-default/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Quick Hits: Setting the User Agent Header in Webrat</title>
		<link>http://www.kickasslabs.com/2009/03/31/quick-hits-setting-the-user-agent-header-in-webrat/</link>
		<comments>http://www.kickasslabs.com/2009/03/31/quick-hits-setting-the-user-agent-header-in-webrat/#comments</comments>
		<pubDate>Tue, 31 Mar 2009 16:35:51 +0000</pubDate>
		<dc:creator>Brad</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[rspec]]></category>
		<category><![CDATA[webrat]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=248</guid>
		<description><![CDATA[If you&#8217;ve read the new PragProg beta e-book on RSpec, you may have read that you can set HTTP headers for your Webrat request like so:

Given /^I am browsing the site using Safari$/ do
  header &#34;User-Agent&#34; , &#34;Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us)&#34;
end

Like me, you may have found out the hard [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve read the new <a href="http://pragprog.com/" title="Pragmatic Programmers" target="pragprog">PragProg</a> <a href="http://www.pragprog.com/titles/achbd/the-rspec-book" title="The RSpec Book beta ebook" target="pragprog">beta e-book on RSpec</a>, you may have read that you can set HTTP headers for your Webrat request like so:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">Given <span style="color:#006600; font-weight:bold;">/</span>^I am browsing the site using Safari$<span style="color:#006600; font-weight:bold;">/</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  header <span style="color:#996600;">&quot;User-Agent&quot;</span> , <span style="color:#996600;">&quot;Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us)&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Like me, you may have found out the hard way that this doesn&#8217;t work.  Webrat does not automagically apply these new HTTP headers to your request &#8211; they certainly don&#8217;t make it to my controller.  What worked for me:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">Given <span style="color:#006600; font-weight:bold;">/</span>^I am browsing the site using Safari$<span style="color:#006600; font-weight:bold;">/</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  headers<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;User-Agent&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span> = <span style="color:#996600;">&quot;Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us)&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">When</span> <span style="color:#006600; font-weight:bold;">/</span>^I visit my precious site$<span style="color:#006600; font-weight:bold;">/</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  get <span style="color:#996600;">'/my/precious/path'</span>, my_query_string, headers
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>In the code above, <code>headers</code> is a method call that returns all the HTTP headers for your request.  Just tack <code>headers</code> on as the third argument of your request, and you&#8217;re good to go.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2009%2F03%2F31%2Fquick-hits-setting-the-user-agent-header-in-webrat%2F&amp;linkname=Quick%20Hits%3A%20Setting%20the%20User%20Agent%20Header%20in%20Webrat"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2009/03/31/quick-hits-setting-the-user-agent-header-in-webrat/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>FasterCSV and noob-ish silliness</title>
		<link>http://www.kickasslabs.com/2009/03/23/fastercsv-and-noob-ish-silliness/</link>
		<comments>http://www.kickasslabs.com/2009/03/23/fastercsv-and-noob-ish-silliness/#comments</comments>
		<pubDate>Tue, 24 Mar 2009 03:44:52 +0000</pubDate>
		<dc:creator>abel</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Quick Hits]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[fastercsv]]></category>
		<category><![CDATA[noob]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=238</guid>
		<description><![CDATA[Here are two things I discovered this weekend:
Thing 1: FasterCSV is really cool!  It&#8217;s easy to use and does exactly what it should.  Here&#8217;s Scott Becker&#8217;s exporting tutorial.  For importing, i&#8217;ve hacked together bits from Peter Larkmund&#8217;s travels and the FasterCSV documentation.  Thanks to these tutorials, AMB will give users the ability to upload and [...]]]></description>
			<content:encoded><![CDATA[<p>Here are two things I discovered this weekend:</p>
<p><strong>Thing 1:</strong> <a href="http://fastercsv.rubyforge.org/" target="_blank">FasterCSV</a> is really cool!  It&#8217;s easy to use and does exactly what it should.  Here&#8217;s Scott Becker&#8217;s <a href="http://synthesis.sbecker.net/articles/2007/06/07/how-to-generate-csv-files-in-rails" target="_blank">exporting tutorial</a>.  For importing, i&#8217;ve hacked together bits from Peter Larkmund&#8217;s <a href="http://marklunds.com/articles/one/310" target="_blank">travels</a> and the FasterCSV <a href="http://fastercsv.rubyforge.org/classes/FasterCSV.html" target="_blank">documentation</a>.  Thanks to these tutorials, <a href="http://www.am-budget.com" target="_blank">AMB</a> will give users the ability to upload and export CSVs.</p>
<p><strong>Thing 2:</strong> Default values for the win!  If you have a table that has a column for an amount, feel free to set the default value to 0.00.  Otherwise you won&#8217;t be able to do the following:</p>
<p>a = Account.new<br />
a.amount += 20</p>
<p>&#8230;because a.amount will be nil.  You&#8217;ll throw an error and if you just updated a bunch of code, you might waste an hour trying to figure out why you&#8217;re throwing an exception.  However, if you declare a default value, you&#8217;ll be able to avoid any issues.</p>
<p>If you want me to go into further detail on either thing, just leave a comment.</p>
<p>Thanks for reading and happy hacking.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2009%2F03%2F23%2Ffastercsv-and-noob-ish-silliness%2F&amp;linkname=FasterCSV%20and%20noob-ish%20silliness"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2009/03/23/fastercsv-and-noob-ish-silliness/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>F@#$ing get V1 out</title>
		<link>http://www.kickasslabs.com/2009/03/15/fing-get-v1-out/</link>
		<comments>http://www.kickasslabs.com/2009/03/15/fing-get-v1-out/#comments</comments>
		<pubDate>Sun, 15 Mar 2009 18:45:51 +0000</pubDate>
		<dc:creator>abel</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[ambudget]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=204</guid>
		<description><![CDATA[I’m not new to software development, but unlike Gabe and Brad I’m new to hacking rails.  I&#8217;ve created simple tutorial apps in rails (an online store, a blog, etc.) and I now feel comfortable enough to  create something for the world to see.  At the same time, I realized that I would need to care [...]]]></description>
			<content:encoded><![CDATA[<p>I’m not new to software development, but unlike Gabe and Brad I’m new to hacking rails.  I&#8217;ve created simple tutorial apps in rails (an online store, a blog, etc.) and I now feel comfortable enough to  create something for the world to see.  At the same time, I realized that I would need to care about the project in order to keep coding it.  I decided to turn the spreadsheet that I use for budgeting into a rails app.  <a href="http://www.am-budget.com" target="_blank">AM-Budget</a> (which I like to call AMB) is my first rails app and I’ve learned a lot leading up to its deployment.  I’d like to share the following with you in the hopes that I can encourage other ruby-newbies to release a v1.</p>
<p><span id="more-204"></span><span style="text-decoration: underline;"><strong>Reduce your feature list</strong></span><br />
When I first started AMB, I wanted to do a TON of things:</p>
<ul>
<li> Implement Calendar &amp; tabular layouts for my transactions.</li>
<li> Add AJAX sliders, like Kayak.com, which would allow users to view a range of transactions and dynamically update the sum of those transactions.</li>
<li> Add Dynamic charts of data like Finance.Google.com where users could dynamically add tracked months to the chart to compare trends.</li>
<li> Create a way to track my checking, savings, &amp; credit cards as individual accounts with individual transactions.</li>
<li>Create a way to track credit card payments or moves from checking/savings.</li>
<li>Track an estimated balance &amp; current balance for each month for each account.</li>
<li>Create a way to display and create recurring transactions.</li>
</ul>
<p>Yeah&#8230;while ambition is important, limits are also important.  Getting all of these things done for v1 is possible of an experienced web team, but as a sole developer trying to scratch an itch and learn something at the same time, it’s not realistic.  Implementing all of these features and fixing bugs would take way too much time.  I needed to remember the original mission of replacing my budgeting spreadsheet.  To help reorient myself with my goal, I took another look at my spreadsheet and compared it against my feature list.  I noticed the following:</p>
<ul>
<li> I only have a tabular view of my transactions.</li>
<li>I don’t have any AJAX sliders.</li>
<li> I don’t have any charts.</li>
<li> I don’t track my credit card or savings balances in the spreadsheet.  In fact, 90% of the time I use my debit card so it’s really all coming out of checking anyway.  Once I put money into savings, I don’t touch it again.</li>
<li> I do track my current monthly estimated and running balances for my checking account.</li>
<li> I implement next month’s budget by copying the previous month and editing values/dates where necessary.</li>
</ul>
<p>This really helped to put things in perspective.  V1 really needs to give me the ability to enter and track the transactions of 1 account without any sexy AJAX or Flash.  The 1 feature I did want to implement was recurring transactions, but I&#8217;ve worked around this limitation already and clearly it&#8217;s not critical for v1.<br />
<strong><br />
<span style="text-decoration: underline;">Do it the wrong way, but get something out</span></strong><br />
The beauty of web development is that it’s not like building a car.  Car manufactures deploy once a year.  Websites can be deployed and re-deployed every hour on the hour if we want.  Implementing new features and optimizing code is part of the fun.  As a newbie, you’re going to put code that should be in your model in your view or helper code in your controller.  It’s ok.  Getting yourself into the correct mind set for where code lives takes time.  For v1, if you can get the desired effect, call it a victory and move on.  If this project becomes something that you want to maintain and improve upon, you will.<br />
Also, though test driven development (TDD) is ideal, feel free to get something out without it.  TDD is something that you can come back to if you’ve started without it.  HOWEVER, I’d advise that you stop yourself from advancing to v2 without creating a few REAL tests first.<br />
<strong><br />
<span style="text-decoration: underline;"> No one will judge you harshly</span></strong><br />
Well, that’s not true.  There will be jerks to will give you a ton of crap for not having a “MUST HAVE” feature in your app and how you&#8217;re dumb for not implementing it first.  Honestly, F@#$ those people.  You have to realize that your first rails app isn’t about pleasing the world.  It’s about the realization of your idea and getting it out the door.  That&#8217;s not to say you shouldn&#8217;t listen to a good idea here or there, but don&#8217;t feel bad if everyone isn&#8217;t a fan.</p>
<p>That&#8217;s it.  Now get out there and get v1 f@#$ing done and enjoy your release!</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2009%2F03%2F15%2Ffing-get-v1-out%2F&amp;linkname=F%40%23%24ing%20get%20V1%20out"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2009/03/15/fing-get-v1-out/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using assert_select to find an attribute that has square brackets in the value</title>
		<link>http://www.kickasslabs.com/2009/03/01/using-assert_select-to-find-an-attribute-that-has-square-brackets-in-the-value/</link>
		<comments>http://www.kickasslabs.com/2009/03/01/using-assert_select-to-find-an-attribute-that-has-square-brackets-in-the-value/#comments</comments>
		<pubDate>Sun, 01 Mar 2009 19:31:58 +0000</pubDate>
		<dc:creator>gabe</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[assert_select]]></category>
		<category><![CDATA[invalid selector]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=194</guid>
		<description><![CDATA[Problem: assert_select 'input[name=model_name[field_name]]' gives ArgumentError: Invalid selector: ] 
Solution: assert_select 'input[name=?]', 'model_name[field_name]' 
Thanks to the Boston Ruby Group thread for the answer.
Bonus: Here&#8217;s a helpful assert_select cheat sheet.
]]></description>
			<content:encoded><![CDATA[<p><strong>Problem</strong>: <code>assert_select 'input[name=model_name[field_name]]'</code> gives <code>ArgumentError: Invalid selector: ] </code></p>
<p><strong>Solution</strong>: <code>assert_select 'input[name=?]', 'model_name[field_name]' </code></p>
<p>Thanks to <a href="http://groups.google.com/group/boston-rubygroup/browse_thread/thread/47b9d0b3798a7858">the Boston Ruby Group thread</a> for the answer.</p>
<p><strong>Bonus</strong>: Here&#8217;s a helpful <a href="http://labnotes.org/svn/public/ruby/rails_plugins/assert_select/cheat/assert_select.html">assert_select cheat sheet</a>.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2009%2F03%2F01%2Fusing-assert_select-to-find-an-attribute-that-has-square-brackets-in-the-value%2F&amp;linkname=Using%20assert_select%20to%20find%20an%20attribute%20that%20has%20square%20brackets%20in%20the%20value"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2009/03/01/using-assert_select-to-find-an-attribute-that-has-square-brackets-in-the-value/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>InvalidAuthenticityToken, Forms, Tables, and Rails</title>
		<link>http://www.kickasslabs.com/2009/03/01/invalidauthenticitytoken-forms-tables-and-rails/</link>
		<comments>http://www.kickasslabs.com/2009/03/01/invalidauthenticitytoken-forms-tables-and-rails/#comments</comments>
		<pubDate>Sun, 01 Mar 2009 19:12:41 +0000</pubDate>
		<dc:creator>gabe</dc:creator>
				<category><![CDATA[HTML]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[forms]]></category>
		<category><![CDATA[invalidauthenticitytoken]]></category>
		<category><![CDATA[tables]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=167</guid>
		<description><![CDATA[Recently, while pairing with Abel as we hacked on some code for his budget tracking app, we came across an interesting problem while trying to acomplish what seems like a pretty straightforward task.

The Context: We had a table of records were using remote_form_for to output an edit form for each row so that we could [...]]]></description>
			<content:encoded><![CDATA[<p>Recently, while pairing with Abel as we hacked on some code for his budget tracking app, we came across an interesting problem while trying to acomplish what seems like a pretty straightforward task.</p>
<p><span id="more-167"></span></p>
<p><strong>The Context</strong>: We had a table of records were using remote_form_for to output an edit form for each row so that we could allow inline editing of any row via AJAX.  The code looked something like this:</p>

<div class="wp_syntax"><div class="code"><pre class="haml" style="font-family:monospace;">-remote_form_for(person) do |f|
  %tr
    %td
      =f.text_field :first_name
    %td
      =f.text_field :last_name
    %td
      =f.submit 'Save'</pre></div></div>

<p>If you prefer straight HTML, the relevant code looked something like this:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;form&gt;
&lt;input name=&quot;person[first_name]&quot; type=&quot;text&quot; /&gt;
&lt;input name=&quot;person[last_name]&quot; type=&quot;text&quot; /&gt;
&lt;input type=&quot;submit&quot; value=&quot;Save&quot; /&gt;
&lt;/form&gt;</pre></div></div>

<p><strong>The Problem</strong>: When submitting an edit on any of the people rows, we would see an ActionController::InvalidAuthenticityToken exception in the server log.  This error is usually a sign of a configuration problem with your environment, but our case seemed different.  Firebug showed us that the browser didn&#8217;t appear to be passing any params to the server during it&#8217;s update post and the server log didn&#8217;t show any params coming through, either.  So, it wasn&#8217;t an invalid authenticity token being passed; no token was being pased at all.  I would prefer if Rails gave us a MissingAuthenticityToken error instead but I digress.</p>
<p><strong>The Discovery</strong>: After whipping up a test case where we had a remote form on a page working fine with no errors, we discovered that the problem only occured when the form was wrapping the record&#8217;s table row tag in the view.  And, well, this makes a good deal of sense.  Here&#8217;s what the <a href="http://www.w3.org/TR/html401/sgml/dtd.html#block">HTML 4.01 spec</a> says about FORMs and TABLEs (relevant lines from the spec have been pasted below in a convenient order):</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;!--ELEMENT TABLE - -
     (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)--&gt;
&lt;!--ELEMENT TBODY O O (TR)+   table body  --&gt;
&lt;!--ELEMENT TR       - O (TH|TD)+          table row  --&gt;
&lt;!--ELEMENT (TH|TD)  - O (%flow;)*         table header cell, table data cell --&gt;
&lt;!--ENTITY % flow &quot;%block; | %inline;&quot;--&gt;
&lt;!--ENTITY % block
     &quot;P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
      BLOCKQUOTE | FORM | HR | TABLE | FIELDSET | ADDRESS&quot;--&gt;</pre></div></div>

<p>If you&#8217;re not used to looking at DTDs, all that synax can be hard to grok.  Here&#8217;s what the relevant parts of it say, in plain English:</p>
<ul>
<li>TABLEs must have a TBODY</li>
<li>TBODYs must have a TR</li>
<li>TRs must have THs or TDs</li>
<li>TDs can have any elements inside them as long as those elements belong to the <em>flow</em> group</li>
<li>The <em>flow</em> group consists of elements inside the <em>block</em> group and the <em>inline</em> group</li>
<li>FORM elements are part of the <em>block</em> group.</li>
</ul>
<p>So, what was happening was that the browser saw our FORM element defined as a sibling to the TR.  That&#8217;s not allowed in the spec, so it makes some bit of sense that the browser wouldn&#8217;t pass the form data to the server the way we expect it would.  After all, the browser is dealing with invalid markup here.</p>
<p><strong>The Solution</strong>: The solution we found was to wrap the form input controls inside one big table that&#8217;s wrapped by the form tag, like this:</p>

<div class="wp_syntax"><div class="code"><pre class="haml" style="font-family:monospace;">%table
  -@people.each do |person|
    %tr
      %td
        -remote_form_for(person) do |f|
          %table
            %tr
              %td
                =f.text_field :first_name
              %td
                =f.text_field :last_name
              %td
                =f.submit 'Save'</pre></div></div>

<p>Again, in HTML:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;table border=&quot;0&quot;&gt;
  &amp;lt;% @people.each do |person| %&amp;gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;form&gt;
&lt;table border=&quot;0&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;input name=&quot;person[first_name]&quot; type=&quot;text&quot; /&gt;&lt;/td&gt;
&lt;td&gt;
&lt;input name=&quot;person[last_name]&quot; type=&quot;text&quot; /&gt;&lt;/td&gt;
&lt;td&gt;
&lt;input type=&quot;submit&quot; value=&quot;Save&quot; /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/form&gt;&lt;/td&gt;
&lt;/tr&gt;
&amp;lt;% end %&amp;gt;&lt;/tbody&gt;&lt;/table&gt;</pre></div></div>

<p>Note: at this point we&#8217;re using a table for layout, and it&#8217;s nasty as all hell.  <strong>I don&#8217;t recommend that you structure your markup this way.</strong></p>
<p>But, <strong>this example illustrates is how you can use a FORM element inside of a TABLE and have it actually work</strong>.  And, more importantly, now we know why we were getting an InvalidAuthenticityToken error when everything seemed like it should have worked.</p>
<p>Hope this helps anyone else in the same situation.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2009%2F03%2F01%2Finvalidauthenticitytoken-forms-tables-and-rails%2F&amp;linkname=InvalidAuthenticityToken%2C%20Forms%2C%20Tables%2C%20and%20Rails"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2009/03/01/invalidauthenticitytoken-forms-tables-and-rails/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Better Rails Searching with Named Scopes using Scope Builder</title>
		<link>http://www.kickasslabs.com/2008/12/22/better-rails-searching-with-named-scopes-using-scope-builder/</link>
		<comments>http://www.kickasslabs.com/2008/12/22/better-rails-searching-with-named-scopes-using-scope-builder/#comments</comments>
		<pubDate>Mon, 22 Dec 2008 19:54:16 +0000</pubDate>
		<dc:creator>gabe</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[named scope]]></category>
		<category><![CDATA[search]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=109</guid>
		<description><![CDATA[When it comes writing elegant search code in a Rails app, Named Scopes immediately come to mind.  And for good reason: they&#8217;re a fantastic way to express, well, scopes, for your searches.  In your Person model, you might have named scopes like by_last_name, by_age, and by_sex.  But, what do you do when you want to [...]]]></description>
			<content:encoded><![CDATA[<p>When it comes writing elegant search code in a Rails app, Named Scopes immediately come to mind.  And for good reason: they&#8217;re a fantastic way to express, well, scopes, for your searches.  In your Person model, you might have named scopes like <code>by_last_name</code>, <code>by_age</code>, and <code>by_sex</code>.  But, what do you do when you want to give your users a search form and let them find people by any combination of last name, age, and sex?</p>
<p><span id="more-109"></span></p>
<p>Well, you might try something like executing each named scope that you have params for and find the intersection of the IDs of all people returned from each named_scope.  That might look something like this:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">p1 = Person.<span style="color:#9900CC;">by_last_name</span> params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:last_name</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#9966CC; font-weight:bold;">if</span> params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:last_name</span><span style="color:#006600; font-weight:bold;">&#93;</span>
p2 = Person.<span style="color:#9900CC;">by_age</span> params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:age</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#9966CC; font-weight:bold;">if</span> params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:age</span><span style="color:#006600; font-weight:bold;">&#93;</span>
p3 = Person.<span style="color:#9900CC;">by_sex</span> params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:sex</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#9966CC; font-weight:bold;">if</span> params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:sex</span><span style="color:#006600; font-weight:bold;">&#93;</span>
&nbsp;
people <span style="color:#006600; font-weight:bold;">+</span>= p1 <span style="color:#9966CC; font-weight:bold;">if</span> p1.<span style="color:#9900CC;">length</span> <span style="color:#006600; font-weight:bold;">+</span> p2 <span style="color:#006600; font-weight:bold;">+</span> p3</pre></div></div>

<p>What you really want is <a href="http://github.com/ryanb/scope-builder/tree/master">Ryan Bates&#8217; Scope Builder gem</a>.</p>
<p>Scope Builder lets you &#8220;build up named scopes conditionally.&#8221;  From the README:</p>
<blockquote><p>This gem adds the scope_builder method to all Active Record models. A<br />
builder behaves exactly like any other named scope except that calling<br />
other named scopes on it will alter the builder itself rather than<br />
returning a new named scope.</p></blockquote>
<p>Here&#8217;s some example code using that defines Person.search using named scopes for last_name, age, and sex:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> Person <span style="color:#006600; font-weight:bold;">&amp;</span>lt; <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Base</span>
  named_scope <span style="color:#ff3333; font-weight:bold;">:by_last_name</span>, <span style="color:#CC0066; font-weight:bold;">lambda</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>last_name<span style="color:#006600; font-weight:bold;">|</span>
    <span style="color:#006600; font-weight:bold;">&#123;</span>
      <span style="color:#ff3333; font-weight:bold;">:conditions</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'last_name = :last_name'</span>,
        <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:last_name</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; last_name <span style="color:#006600; font-weight:bold;">&#125;</span> <span style="color:#006600; font-weight:bold;">&#93;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span> 
&nbsp;
  named_scope <span style="color:#ff3333; font-weight:bold;">:by_age</span>, <span style="color:#CC0066; font-weight:bold;">lambda</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>age<span style="color:#006600; font-weight:bold;">|</span>
    <span style="color:#006600; font-weight:bold;">&#123;</span>
      <span style="color:#ff3333; font-weight:bold;">:conditions</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'age = :age'</span>,
        <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:age</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; age <span style="color:#006600; font-weight:bold;">&#125;</span> <span style="color:#006600; font-weight:bold;">&#93;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span> 
&nbsp;
  named_scope <span style="color:#ff3333; font-weight:bold;">:by_sex</span>, <span style="color:#CC0066; font-weight:bold;">lambda</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#006600; font-weight:bold;">|</span>sex<span style="color:#006600; font-weight:bold;">|</span>
    <span style="color:#006600; font-weight:bold;">&#123;</span>
      <span style="color:#ff3333; font-weight:bold;">:conditions</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'sex = :sex'</span>,
        <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:sex</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; sex <span style="color:#006600; font-weight:bold;">&#125;</span> <span style="color:#006600; font-weight:bold;">&#93;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">search</span><span style="color:#006600; font-weight:bold;">&#40;</span>options<span style="color:#006600; font-weight:bold;">&#41;</span>
    scope_builder <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>builder<span style="color:#006600; font-weight:bold;">|</span>
      builder.<span style="color:#9900CC;">by_last_name</span><span style="color:#006600; font-weight:bold;">&#40;</span>options<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:last_name</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">if</span> options<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:last_name</span><span style="color:#006600; font-weight:bold;">&#93;</span>
      builder.<span style="color:#9900CC;">by_age</span><span style="color:#006600; font-weight:bold;">&#40;</span>options<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:age</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">if</span> options<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:age</span><span style="color:#006600; font-weight:bold;">&#93;</span>
      builder.<span style="color:#9900CC;">by_sex</span><span style="color:#006600; font-weight:bold;">&#40;</span>options<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:sex</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">if</span> options<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:sex</span><span style="color:#006600; font-weight:bold;">&#93;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>So, now all you need is a People Search form and an action to handle finding the right people.  Here&#8217;s what your search action might look like inside PeopleController:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">def</span> search
  <span style="color:#0066ff; font-weight:bold;">@people</span> = Person.<span style="color:#9900CC;">search</span><span style="color:#006600; font-weight:bold;">&#40;</span>params<span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">all</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Important note: When using ScopeBuilder as shown above, you&#8217;ll have to call <code>#all</code> because, in this case, <code>Person.search</code> returns a ScopeBuilder object, not a collection of ActiveRecord Person objects.  To get at the people collection, you call <code>#all</code> on the ScopeBuilder object returned by <code>Person.search</code>.</p>
<p>So, see?  Isn&#8217;t that pretty?  You&#8217;ve got a simple search that will find all the people that meet any all of the attributes passed in from the search form.  Ta da!</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2008%2F12%2F22%2Fbetter-rails-searching-with-named-scopes-using-scope-builder%2F&amp;linkname=Better%20Rails%20Searching%20with%20Named%20Scopes%20using%20Scope%20Builder"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2008/12/22/better-rails-searching-with-named-scopes-using-scope-builder/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick and Dirty Messaging</title>
		<link>http://www.kickasslabs.com/2008/11/22/93/</link>
		<comments>http://www.kickasslabs.com/2008/11/22/93/#comments</comments>
		<pubDate>Sat, 22 Nov 2008 20:05:55 +0000</pubDate>
		<dc:creator>Brad</dc:creator>
				<category><![CDATA[Great Minds]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[gm]]></category>
		<category><![CDATA[message queue]]></category>
		<category><![CDATA[messaging]]></category>
		<category><![CDATA[rails rumble]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[rumble]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=93</guid>
		<description><![CDATA[Our Rails Rumble 2008 entry, Great Minds (you can have a look at the latest version or the original Rumble version), required a messaging system.  Such systems are easy to do wrong, and we knew we&#8217;d need something that would stay solid under unknown load during Rumble judging.
The solution was quick &#38; dirty (as [...]]]></description>
			<content:encoded><![CDATA[<p>Our <a href="http://railsrumble.com/" title="Rails Rumble 2008" target="rr2008">Rails Rumble 2008</a> entry, <i>Great Minds</i> (you can have a look at the <a href="http://greatminds.kickasslabs.com/" title="Great Minds" target="gmkal">latest version</a> or the <a href="http://greatminds.r08.railsrumble.com/" title="Great Minds - Rumble Version" target="gmrr">original Rumble version</a>), required a messaging system.  Such systems are easy to do wrong, and we knew we&#8217;d need something that would stay solid under unknown load during Rumble judging.</p>
<p>The solution was quick &amp; dirty (as most solutions are during Rails Rumble), but the results worked, and allowed us to qualify for judging and reach a respectable 28th place finish in the &#8220;Completeness&#8221; category.</p>
<p>Check out the details after the fold.</p>
<p><span id="more-93"></span></p>
<h3>Design Constraints</h3>
<p><i>Great Minds</i> is a word game played by 2 or more players.  The game is played in a virtual chat <b>Room</b>, containing one or more <b>Players.</b>  Players can take various actions, such as submitting a word to the game, chatting, or changing their handle/username.  The results of these actions must be transmitted to other players in the room.</p>
<p>Other constraints on the design included:</p>
<ul>
<li><b>No New Technologies:</b>  There are tools like <a href="http://juggernaut.rubyforge.org/" title="Juggernaut" target="juggernaut">Juggernaut</a> that make messaging pretty easy, but we had no hands-on experience with them, and learning a new tool on a 48-hour schedule was deemed a risk to completing the project &#038; qualifying for Rumble judging.</li>
<li><b>Broadcast:</b>  We needed something more than a simple message queue like <a href="http://xph.us/software/beanstalkd/" title="Beanstalk" target="beanstalk">Beanstalk</a>; the consumer of a message couldn&#8217;t remove it from the queue, as there might be other consumers needing the same message.</li>
<li><b>Room State Reconstruction:</b>  Someone coming to the room for the first time (or leaving and returning) should be able to see the current room state, including all participants, recently played words, and recent chat.</li>
<li><b>Replay:</b>  Though we didn&#8217;t get around to implementing it, we wanted to offer the ability to review a particularly funny game.</li>
</ul>
<p>The first two constraints affected our design and technology choices; the second two demanded that the messages be persisted.  (Alternatively, we could have saved the room state in some other way, but as long as we had the messages anyway, it seemed simpler to just keep them and reconstruct the state.)</p>
<h3>Client Implementation</h3>
<p>In the absence of a viable push solution like Juggernaut, we opted for a polling solution:  Clients would periodically send a signal that:</p>
<ul>
<li>let the server know they&#8217;re still alive and connected,</li>
<li>sent any new messages to the server, and</li>
<li>requested any pending messages from the server.</li>
</ul>
<p>On the client side, this meant that every message got packaged as JSON, stuck in a queue, and periodically collected and sent to the server as the optional payload of a keep-alive signal.  With some help from Prototype, this was as simple as:</p>
<pre>
  function SyncMessages() {
          var data = Object.toJSON(MESSAGES_QUEUE);
          var url = '/room/sync_messages';
          var params = {
                  authenticity_token: AUTH_TOKEN,
                  messages: '[]',
                  //messages: Object.toJSON(MESSAGES_QUEUE),
                  last_message_id: LAST_MESSAGE_ID };

          new Ajax.Request(url, {
            method: 'post',
                  parameters: params,
            onSuccess: HandleMessages });
  }

  SyncMessages(); //start first on load
  var message_syncer = new PeriodicalExecuter(SyncMessages, 3);
</pre>
<p><code>HandleMessages()</code> updates the DOM based on messages coming back.</p>
<p>The other feature to note is the <code>LAST_MESSAGE_ID</code> token above.  Each client is responsible for keeping track of the last message ID it got.  On the server side, message IDs are assigned sequentially, so all new messages are guaranteed to have a higher ID.</p>
<p>We could have kept track of each client&#8217;s last message on the server side, but this would have been much more complex &#8211; we would have needed to store data either in the session (which already held a fair amount of stuff) or in another table in order to keep track of who had received what.</p>
<h3>Server Implementation</h3>
<p>On the server side, messages were persisted in a table.  This table would have relatively frequent inserts, somewhat more frequent reads, and no updates or deletions.  For this reason, we went with the default MyISAM table architecture.  (We had considered a design that would have updated the table with delivery status information and would have required InnoDB&#8217;s row-level locking, but in the end we opted for the simpler design.)  The table looks like:</p>
<pre>
  class CreateMessages < ActiveRecord::Migration
    def self.up
      create_table :messages do |t|
        t.integer :message_type_id, :room_id, :game_id, :user_id
        t.string  :data, :user_handle
        t.timestamps
      end
    end

    def self.down
      drop_table :messages
    end
  end
</pre>
<p>It also has an index:</p>
<pre>
  class AddRoomIndexToMessages < ActiveRecord::Migration
    def self.up
      add_index :messages, [:room_id, :id]
    end

    def self.down
      remove_index :messages, [:room_id, :id]
    end
  end
</pre>
<p>Recall that the client keeps track of the last message it got, and periodically requests all messages since.  This index speeds that request - which is just <code>msgs = Message.find :all, :conditions => ['room_id = ? AND id > ?', session['room_id'], last_message_id], <img src='http://www.kickasslabs.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> rder => 'id'</code>.  (Yes, I know that should be a <a href="http://ryandaigle.com/articles/2008/8/20/named-scope-it-s-not-just-for-conditions-ya-know" title="Named Scopes" title="namedscopes">named scope</a>.  I hadn't gotten around to learning those yet at the time of the Rumble.)</p>
<p>When the a keep-alive signal hits the server:</p>
<ul>
<li>any new messages attached to the signal are added to the <code>messages</code> table,</li>
<li>any game mechanics related to those messages are executed (which may result in more messages being added to the table), and</li>
<li>all messages for the caller's room with an ID greater than the caller's LAST_MESSAGE_ID are packaged as JSON and sent back down in the HTTP response.</li>
</ul>
<p>A note:  If you're in a room and you enter (for example) a chat message, there's actually a server round-trip involved (pushing the message to the server and getting the same message back as a response) before that chat message is shown in your browser.  We worried about this, but not for long - the interactivity delay turned out to be minimal, and it was much simpler to keep it this way (every message is treated the same for all clients) than to create an exception (you update your DOM with your chat messages immediately, but your own chat messages are somehow filtered from your message stream).</p>
<h3>What's Missing?</h3>
<p>Obviously, this was a quick &amp; dirty solution, tailored to the immediate needs of a Rails Rumble project.  Here's what I'd do differently (and indeed, will do differently, as we have plans for the future of Great Minds - stay tuned):</p>
<ul>
<li><b>Security:</b>  We do use the Rails session <code>authenticity_token</code>, but there's still no protection from a server flood from someone who has a token, either by cranking down the polling interval or by fiddling with the LAST_MESSAGE_ID.</li>
<li><b>Server Push:</b>  Polling is wasteful - requests are made whether or not there are any messages to send or receive.  We plan to move to Juggernaut soon.</li>
<li><b>Decoupling from Game Mechanics:</b>  In the first iteration, message receipt and delivery were too intimately tied to game mechanics, with <code>Message</code> objects being explictly constructed and followed by <code>Message.save!</code> calls.  I've already started the process of teasing them apart in preparation for the move to Juggernaut, factoring this into a <code>deliver_message()</code> method.</li>
</ul>
<p>Questions?  Thoughts?  Add a comment!  I promise to get back to you.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.kickasslabs.com%2F2008%2F11%2F22%2F93%2F&amp;linkname=Quick%20and%20Dirty%20Messaging"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share/Bookmark"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2008/11/22/93/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
