<?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"
	>

<channel>
	<title>Echographia</title>
	<atom:link href="http://www.echographia.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.echographia.com/blog</link>
	<description>making things better, making better things</description>
	<pubDate>Mon, 19 Apr 2010 15:52:28 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6</generator>
	<language>en</language>
			<item>
		<title>sane Sinatra stack traces in Cucumber</title>
		<link>http://www.echographia.com/blog/2010/04/19/sane-sinatra-stack-traces-in-cucumber/</link>
		<comments>http://www.echographia.com/blog/2010/04/19/sane-sinatra-stack-traces-in-cucumber/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 15:52:28 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=114</guid>
		<description><![CDATA[I&#8217;m building an app in Sinatra and testing it (in part) with Cucumber. When exceptions were raised in my application, I&#8217;d get a stack trace wrapped in pages and pages of HTML - very nice when you&#8217;re testing in a browser, but almost impossible to work with in a command-line test. After a surprisingly long [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m building an app in <a href="http://www.sinatrarb.com/">Sinatra</a> and testing it (in part) with <a href="http://cukes.info/">Cucumber</a>. When exceptions were raised in my application, I&#8217;d get a stack trace wrapped in pages and pages of HTML - very nice when you&#8217;re testing in a browser, but almost impossible to work with in a command-line test. After a surprisingly long investigation, I added these lines to <code>env.rb</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby"><span style="color:#008000; font-style:italic;"># Don't generate fancy HTML for stack traces.</span>
disable <span style="color:#ff3333; font-weight:bold;">:show_exceptions</span>
<span style="color:#008000; font-style:italic;"># Allow errors to get out of the app so Cucumber can display them.</span>
enable <span style="color:#ff3333; font-weight:bold;">:raise_errors</span></pre></div></div>

<p>Both are required. If you just <code>disable :show_exceptions</code>, you&#8217;re spared the fancy HTML, but you&#8217;re also spared any useful information about what&#8217;s going wrong. (You just get &#8220;Internal Server Error&#8221;.) But if you just enable <code>:raise_errors</code>, well, the errors don&#8217;t get raised - they&#8217;re trapped by the exception-showing code.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2010/04/19/sane-sinatra-stack-traces-in-cucumber/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Release announcement: Flickr Watchr</title>
		<link>http://www.echographia.com/blog/2010/03/08/release-announcement-flickr-watchr/</link>
		<comments>http://www.echographia.com/blog/2010/03/08/release-announcement-flickr-watchr/#comments</comments>
		<pubDate>Tue, 09 Mar 2010 03:42:36 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=111</guid>
		<description><![CDATA[Just kidding, mostly. I got email today informing me that Watchr &#8220;has been released successfully in Brothersoft&#8221;. Congratulations!
Watchr is a tiny program I wrote five years ago. Ken and I were talking about how we&#8217;d like something that pops up images when they&#8217;re posted to Flickr. For reasons I don&#8217;t now recall, we then wrote [...]]]></description>
			<content:encoded><![CDATA[<p>Just kidding, mostly. I got email today informing me that Watchr &#8220;has been released successfully in Brothersoft&#8221;. Congratulations!</p>
<p>Watchr is a tiny program I wrote five years ago. <a href="http://www.notes.xythian.net/">Ken</a> and I were talking about how we&#8217;d like something that pops up images when they&#8217;re posted to Flickr. For reasons I don&#8217;t now recall, we then wrote two separate applications, <a href="http://www.notes.xythian.net/2005/01/10/watcher/">his in Python</a> and <a href="http://www.erikostrom.com/watchr/watchr.html">mine in Java</a>. (By the way, that page is the only use so far of erikostrom.com, except as a mirror of <a href="http://drowning.org/">drowning.org</a>, the equally dated site about my singer-songwriter career.)</p>
<p>I&#8217;m a little surprised to find that it still basically works. Write once, run everywhere, I guess! Although it still has the same Mac bug noted in 2005, which bothers me more now that I&#8217;m a Mac user.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2010/03/08/release-announcement-flickr-watchr/feed/</wfw:commentRss>
		</item>
		<item>
		<title>setting up browser automation with FireWatir</title>
		<link>http://www.echographia.com/blog/2009/12/23/setting-up-browser-automation-with-firewatir/</link>
		<comments>http://www.echographia.com/blog/2009/12/23/setting-up-browser-automation-with-firewatir/#comments</comments>
		<pubDate>Wed, 23 Dec 2009 19:14:30 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=109</guid>
		<description><![CDATA[Ruby-based testing will only get you so far in the web development world - sooner or later you need to make sure your code works in a real browser. So for the last few years I&#8217;ve been occasionally trying to get a browser-based test automation tool running - often something in the Selenium family. It [...]]]></description>
			<content:encoded><![CDATA[<p>Ruby-based testing will only get you so far in the web development world - sooner or later you need to make sure your code works in a real browser. So for the last few years I&#8217;ve been occasionally trying to get a browser-based test automation tool running - often something in the <a href="http://seleniumhq.org/">Selenium</a> family. It usually takes a full day for me to give up, usually because I just can&#8217;t get the tool to actually drive the browser.</p>
<p>Today I got <a href="http://wiki.openqa.org/display/WTR/FireWatir">FireWatir</a> working! It wasn&#8217;t that hard, but I had to consult a few different web pages, so here it is all in one place.</p>
<ol>
<li>Via <a href="http://wiki.openqa.org/display/WTR/FireWatir+Installation">FireWatir Installation</a>, install the gem:

<div class="wp_syntax"><div class="code"><pre>sudo gem install firewatir</pre></div></div>

<p>and the <code>JSSh</code> Firefox extension (<a href="http://wiki.openqa.org/download/attachments/13893658/jssh-3.5.x-Darwin-param.xpi?version=1">Mac FF 3.5 version</a>) to make Firefox steerable.</p>
<li><a href="http://support.mozilla.com/en-US/kb/Managing+profiles">Set up a Firefox profile</a>, so you can run tests without all your weird extensions in play, and without messing with your personal browser session and history. Mine is called &#8220;tester&#8221;.
<li><a href="http://alephzarro.com/blog/2007/03/12/jssh-for-firefox-on-linux-because-firewatir-loves-it/">Start up Firefox with JSSh</a> and your new profile (you can do this while Firefox is running):

<div class="wp_syntax"><div class="code"><pre>/Applications/Firefox.app/Contents/MacOS/firefox -jssh -P &quot;tester&quot;</pre></div></div>

<li>In an <code>irb</code> session, <a href="http://wiki.openqa.org/display/WTR/FireWatir+Example+Script">try some stuff</a> - for example, on a new <a href="http://spreecommerce.com/">Spree</a> instance:

<div class="wp_syntax"><div class="code"><pre class="ruby">ff = <span style="color:#6666ff; font-weight:bold;">FireWatir::Firefox</span>.<span style="color:#9900CC;">new</span>
ff.<span style="color:#9900CC;">goto</span> <span style="color:#996600;">'http://localhost:3000/'</span>
ff.<span style="color:#9900CC;">link</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:text</span>, <span style="color:#996600;">'Clothing'</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">click</span></pre></div></div>

</ol>
<p>Voila! Now I want to try <a href="http://code.google.com/p/cucumber-and-watir/">Cucumber and Watir</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2009/12/23/setting-up-browser-automation-with-firewatir/feed/</wfw:commentRss>
		</item>
		<item>
		<title>John Resig on JavaScript testing, performance analysis, and jQuery 1.4</title>
		<link>http://www.echographia.com/blog/2009/12/11/john-resig-on-javascript-testing-performance-analysis-and-jquery-14/</link>
		<comments>http://www.echographia.com/blog/2009/12/11/john-resig-on-javascript-testing-performance-analysis-and-jquery-14/#comments</comments>
		<pubDate>Sat, 12 Dec 2009 06:39:52 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=104</guid>
		<description><![CDATA[John Resig, of jQuery and other fame, spoke tonight at the BayJax meetup in the Yahoo! cafeteria. His slides are probably online somewhere, but they are lucid, informative, illustrated with pictures and code samples, and written by someone who knows what he&#8217;s talking about, not to mention who was paying attention while writing them. (Update: [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://ejohn.org/">John Resig</a>, of jQuery and other fame, spoke tonight at the <a href="http://www.meetup.com/BayJax/">BayJax meetup</a> in the Yahoo! cafeteria. His slides are probably online somewhere, but they are lucid, informative, illustrated with pictures and code samples, and written by someone who knows what he&#8217;s talking about, not to mention who was paying attention while writing them. (<strong>Update:</strong> Video <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=263846173">on iTunes</a> and <a href="http://yuiblog.com/yuitheater/resig-testing.m4v">not on iTunes</a>.) If that&#8217;s not your bag, you&#8217;ll want to read my notes instead:<br />
<span id="more-104"></span><br />
PART I: Testing JavaScript</p>
<p>javascript testing is different from desktop/server testing<br />
- cross-browser issues</p>
<p>there are a lot of javascript testing frameworks!<br />
- long tail<br />
- it&#8217;s easy to write one</p>
<p>basic components for a testing framework<br />
- the usual, plus async tests<br />
- minimum: assertions<br />
(&#8217;glorified assertion-and-logging framework&#8217;)<br />
- async tests<br />
- pause (stop executing other tests) and resume</p>
<p>why write your own?<br />
- a good way to understand what testing is for<br />
- not too many cross-browser issues</p>
<p>popular frameworks:<br />
- (none)<br />
- JSUnit<br />
- 2001, and looks it<br />
- YUITest<br />
- good code, supports async tests and event simulation<br />
- (looks like nested contexts?)<br />
- QUnit<br />
- async testing, test timeouts<br />
- no dependencies<br />
- easy to use<br />
- FireUnit<br />
- firebug extension</p>
<p>standardization<br />
- CommonJS: standard API across client and server environments<br />
- including testing API, now adopted by QUnit (I think)</p>
<p>server-side testing of client-side code<br />
- usually Java + Rhino<br />
- Crosscheck (Java)<br />
- Env.js (pure JS)<br />
- BlueRidge (Env.js + Screw.unit + Rhino)</p>
<p>browser launching<br />
- WebDriver (Java)<br />
- Watir (Ruby)<br />
- JsTestDriver (Java)<br />
- Selenium RC (Java)</p>
<p>distributed (for scale)<br />
- Selenium Grid - on Amazon servers<br />
- TestSwarm - crowd-sourced</p>
<p>PART II: Accurately Measuring JavaScript</p>
<p>major use cases:<br />
- same code, different platforms<br />
- which browser is fastest?<br />
- different code, same platform<br />
- which framework is fastest?<br />
- how should we implement this?</p>
<p>same code, different platform<br />
- SunSpider (WebKit), V8 (Chrome), Dromaeo (Mozilla)</p>
<p>&#8230; statistics&#8230;</p>
<p>error rate<br />
- faster tests =&gt; less accuracy<br />
- faster *browsers* =&gt; less accuracy<br />
- dromaeo/v8 test runs/sec instead of msec/run<br />
- use harmonic mean to average</p>
<p>different code, same platform<br />
- most solutions are bad<br />
- few runs, inaccurate<br />
- garbage collection<br />
- use mode instead of mean<br />
- less accurate than mean, but more consistent<br />
- don&#8217;t discard &#8220;bad&#8221; results - GC is significant<br />
- may reflect memory issues<br />
- getTime() &#8220;very very imprecise&#8221;<br />
- 15ms intervals on XP (except FF/Chrome)<br />
- IE in Wine gives you precision, but it&#8217;s not IE on Windows</p>
<p>- browser tools<br />
- Safari, Firebug, IE 8 profilers<br />
- DynaTrace Ajax for IE 6-8<br />
- Shark - watches internal function calls</p>
<p>PART III: jQuery 1.4<br />
- complexity reduction<br />
- bubbling change, submit, focus, blur<br />
- required script loading</p>
<p>analyzing performance<br />
- stop measuring absolute execution time<br />
- improve code quality and flow</p>
<p>complexity analysis<br />
- count function calls instead of seconds<br />
- may indicate not slowness, but poorly written code<br />
- remove() did a lot more work than it needed to<br />
- and so did everything that used remove()</p>
<p>event bubbling in IE<br />
- focus, blur, change, and submit don&#8217;t bubble<br />
- alternative solutions: piggyback on events that DO bubble<br />
- focus/blur: use focusin/focusout<br />
- submit: watch for click&#8230; or &#8216;enter&#8217; keypress&#8230;<br />
- change: basically reimplement the whole event<br />
- track previous value<br />
- watch for keyup navigation, checkbox clicks<br />
- beforeactive on radio buttons</p>
<p>jQuery.require()<br />
- don&#8217;t try to load a script multiple times<br />
- load synchronously if needed for dependent code<br />
- load asynchronously if you can get away with it!</p>
<p>URL mapping<br />
- require(&#8217;ajax&#8217;) =&gt; ajax.js<br />
- namespaces expand to URLs</p>
<p>PART IV: HTML 5 Elements in IE</p>
<p>problem #1<br />
- unknown elements are unstyled, and &#8220;inner contents escape&#8221;<br />
- fix: document.createElement(&#8217;section&#8217;) makes section work<br />
- html5shiv does that for all HTML5</p>
<p>problem #2<br />
- unknown elements can&#8217;t hold other unknown elements<br />
- even with html5shiv<br />
- no fix</p>
<p>problem #3<br />
- nodeName of unknown elements is treated as XML<br />
- fix: don&#8217;t assume nodeName is uppercase</p>
<p>problem #4<br />
- injecting unknown elements via innerHTML fails<br />
- &#8220;fix&#8221;: parse HTML and build DOM in JS</p>
<p>Q&amp;A:</p>
<p>&#8230; i zoned out&#8230;</p>
<p>Q: clojure?<br />
A: framework&#8230; okay. compiler&#8230; pretty slick!<br />
- but designed to compile your whole application<br />
- can&#8217;t compile the framework separately</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2009/12/11/john-resig-on-javascript-testing-performance-analysis-and-jquery-14/feed/</wfw:commentRss>
		</item>
		<item>
		<title>sometimes I&#8217;m not smart</title>
		<link>http://www.echographia.com/blog/2009/10/31/sometimes-im-not-smart/</link>
		<comments>http://www.echographia.com/blog/2009/10/31/sometimes-im-not-smart/#comments</comments>
		<pubDate>Sat, 31 Oct 2009 18:39:32 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[rails]]></category>

		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=102</guid>
		<description><![CDATA[You know those times when you&#8217;re looking all over the house for something and then somebody says, &#8220;Is that it in your other hand?&#8221;
Last night I spent at least an hour trying to figure out why I was getting a nil.rewrite error from Rails&#8217;s url_for method. I&#8217;ve seen hard-to-debug errors from there before, so I [...]]]></description>
			<content:encoded><![CDATA[<p>You know those times when you&#8217;re looking all over the house for something and then somebody says, &#8220;Is that it in your other hand?&#8221;</p>
<p>Last night I spent at least an hour trying to figure out why I was getting a <code>nil.rewrite</code> error from Rails&#8217;s <code>url_for</code> method. I&#8217;ve seen hard-to-debug errors from there before, so I dug right down into it, but I couldn&#8217;t figure out why the <code>@url</code> variable was set in some cases and not in others&#8230; and then I determined that it actually was being set in all cases, but then it was mysteriously blank by the time <code>url_for</code> needed it&#8230;.</p>
<p>And then I realized it was because I had a <code>@url</code> variable in my own controller action. And then I went to bed.</p>
<p>Most of the time I really love how dynamic and open Ruby is as a language, but sometimes private instance variables are just what you&#8217;d like to have had.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2009/10/31/sometimes-im-not-smart/feed/</wfw:commentRss>
		</item>
		<item>
		<title>assert_select from arbitrary text</title>
		<link>http://www.echographia.com/blog/2009/08/22/assert_select-from-arbitrary-text/</link>
		<comments>http://www.echographia.com/blog/2009/08/22/assert_select-from-arbitrary-text/#comments</comments>
		<pubDate>Sun, 23 Aug 2009 03:57:26 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=100</guid>
		<description><![CDATA[A useful testing method in Rails is assert_select. In a functional test, you can use it to make sure the controller&#8217;s response has the HTML you expect:

should &#34;display the suggestion as a link&#34; do
  assert_select 'a[href=?]', @click_url, @display_url
end

&#8230; says there should be an &#60;a&#62; tag with the given href attribute and content. Clearer and [...]]]></description>
			<content:encoded><![CDATA[<p>A useful testing method in Rails is <code>assert_select</code>. In a functional test, you can use it to make sure the controller&#8217;s response has the HTML you expect:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">should <span style="color:#996600;">&quot;display the suggestion as a link&quot;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  assert_select <span style="color:#996600;">'a[href=?]'</span>, <span style="color:#0066ff; font-weight:bold;">@click_url</span>, <span style="color:#0066ff; font-weight:bold;">@display_url</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>&#8230; says there should be an <code>&lt;a&gt;</code> tag with the given <code>href</code> attribute and content. Clearer and less brittle than computing a string and searching for it.</p>
<p>But what if you want to assert something about the HTML structure of something that isn&#8217;t the controller response? Like, if you want to look for a link in the <code>flash</code> hash?</p>
<p>I just added this to my <code>test_helper.rb</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby"><span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#6666ff; font-weight:bold;">ActiveSupport::TestCase</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> assert_select_from<span style="color:#006600; font-weight:bold;">&#40;</span>text, <span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@selected</span> = <span style="color:#6666ff; font-weight:bold;">HTML::Document</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>text<span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">root</span>.<span style="color:#9900CC;">children</span>
    assert_select<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">*</span>args<span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Now in my tests I can use the full power of assert_select on an arbitrary string:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">should <span style="color:#996600;">&quot;provide an undo/delete link&quot;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  assert_select_from<span style="color:#006600; font-weight:bold;">&#40;</span>flash<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:notice</span><span style="color:#006600; font-weight:bold;">&#93;</span>, <span style="color:#996600;">'a[href=?]'</span>,
    venue_path<span style="color:#006600; font-weight:bold;">&#40;</span>assigns<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:venue</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>, <span style="color:#996600;">'undo'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2009/08/22/assert_select-from-arbitrary-text/feed/</wfw:commentRss>
		</item>
		<item>
		<title>bugmash</title>
		<link>http://www.echographia.com/blog/2009/08/10/bugmash/</link>
		<comments>http://www.echographia.com/blog/2009/08/10/bugmash/#comments</comments>
		<pubDate>Mon, 10 Aug 2009 17:34:18 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=98</guid>
		<description><![CDATA[This weekend RailsBridge ran its first Rails BugMash:
The idea is simple: RailsBridge has a lot of energy. The Rails Lighthouse has a lot of open tickets. With the help of some Rails Core team members, we&#8217;re going to see what we can do to cut down the number of open tickets, encourage more people to [...]]]></description>
			<content:encoded><![CDATA[<p>This weekend <a href="http://railsbridge.org/">RailsBridge</a> ran its first <a href="http://wiki.railsbridge.org/projects/railsbridge/wiki/BugMash">Rails BugMash</a>:<br />
<blockquote>The idea is simple: RailsBridge has a lot of energy. The Rails Lighthouse has a lot of open tickets. With the help of some Rails Core team members, we&#8217;re going to see what we can do to cut down the number of open tickets, encourage more people to get involved with the Rails source, and have some fun.</p></blockquote>
<p> Definitely a success - reportedly at least 120 tickets were closed, I&#8217;m sure a lot of new people got involved, and the people on <a href="http://wiki.railsbridge.org/projects/railsbridge/wiki/IRCGuide">#railsbridge</a> seemed to be having fun.<br />
<span id="more-98"></span><br />
I meant to get involved early - even thought about driving to San Francisco to join some other bug mashers face to face - but instead I spent most of the weekend tracking down an <a href="http://sourceforge.net/tracker/?func=detail&#038;aid=2834335&#038;group_id=47038&#038;atid=448266">obscure interaction in HtmlUnit/Rhino, Yahoo! Maps, and Prototype</a>. What with that and <a href="http://tincatland.com/">two Tin Cat shows</a>, I didn&#8217;t get around to the BugMash until Sunday afternoon.</p>
<p>Still, I did get to it. And now I&#8217;m <a href="http://contributors.rubyonrails.org/contributors/erik-ostrom/commits">officially a Rails contributor</a>! Those two contributions are actually one - the early version that was committed by accident, and the later, more thorough revision. (I also shepherded <a href="http://github.com/rails/rails/commit/8c32248acbd71f7906a037fad499e2f8cae61bed">this new feature</a> into the core.)</p>
<p>So that&#8217;s great! I&#8217;ve been wanting to help out somehow with Rails for a while, but it&#8217;s, you know, big. It helped that someone had gone through the open tickets and picked out good ones for us to work on. It helped that there were people around on IRC to talk about the code and how to recover from newbie git mistakes. And it helped that there was a bot ready to supply a new project every time I finished one (or thought I&#8217;d finished it). I&#8217;m a sucker for a steady flow of work - in my head I&#8217;m still typing &#8220;!gimme&#8221; at the IRC prompt.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2009/08/10/bugmash/feed/</wfw:commentRss>
		</item>
		<item>
		<title>deploying Thinking Sphinx on DreamHost PS</title>
		<link>http://www.echographia.com/blog/2009/06/27/deploying-thinking-sphinx-on-dreamhost-ps/</link>
		<comments>http://www.echographia.com/blog/2009/06/27/deploying-thinking-sphinx-on-dreamhost-ps/#comments</comments>
		<pubDate>Sun, 28 Jun 2009 00:47:53 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=87</guid>
		<description><![CDATA[The time had come to add a search engine to Touring Machine. I went pretty far down the road with Xapian/Xapit, but:
You may want to trigger [reindexing] via a cron job on a recurring schedule (i.e. every day) to update the Xapian database. However it will only take effect after the Rails application is restarted [...]]]></description>
			<content:encoded><![CDATA[<p>The time had come to add a search engine to <a href="http://touringmachine.org/">Touring Machine</a>. I went pretty far down the road with <a href="http://xapian.org/">Xapian</a>/<a href="http://github.com/ryanb/xapit/tree/master">Xapit</a>, but:</p>
<blockquote><p>You may want to trigger [reindexing] via a cron job on a recurring schedule (i.e. every day) to update the Xapian database. However it will only take effect after the Rails application is restarted because the Xapian database is stored in memory.</p></blockquote>
<p>I see the Xapit Sync project to fix this has since ceased to be vaporware. Well, maybe next time.</p>
<p>Anyway, so <a href="http://rubytrends.com/styles/popular/search?searchby=search&amp;x=0&amp;y=0">RubyTrends tells me</a> the cool kids use <a href="http://freelancing-god.github.com/ts/en/">Thinking Sphinx</a>, and I want to be cool, but I&#8217;m running Touring Machine on the cheap - on a shared DreamHost server - and they don&#8217;t want me to run server processes, and although <a href="http://hughevans.net/2009/03/10/thinking-sphinx-dreamhost">some guy on the Internet</a> says it&#8217;s probably fine, I&#8217;m leery of defying them. But last week they were running a discount offer on DreamHost PS, their quasi-VPS service - no root, but you can run whatever you want, within the resources (memory, CPU) you pay for.</p>
<p>Sounds like a fine place to run the search engine, but I didn&#8217;t want to run the whole Rails app there - since I&#8217;ve already got a place to run it that is effectively free. (DreamHost is very cheap, and I run a bunch of sites on it.) So I set out to run a distributed site, with the web app running on DreamHost shared hosting, and the search engine running on DreamHost PS.</p>
<p>It took some setup, but I think I&#8217;ve got it working. Here are some notes. As usual, this isn&#8217;t a tutorial. You should read everyone else&#8217;s instructions - especially the official <a href="http://freelancing-god.github.com/ts/en/quickstart.html">Thinking Sphinx documentation</a>, and J. Wade Winningham&#8217;s post about <a href="http://www.updrift.com/article/thinkingsphinx-capistrano-tasks">Capistrano tasks</a>. (I didn&#8217;t use his <code>deploy.rb</code>, though.)<br />
<span id="more-87"></span></p>
<h4>The Approach</h4>
<p>As I said, I&#8217;ve got software installed on two machines - call them <code>app</code> (the web application) and <code>search</code> (the Sphinx server). (<code>app</code> has no special access to <code>search</code> - it&#8217;s open to DOS and other attacks. I&#8217;ll fix it later.) So Capistrano must be told about the new server and its new role. In <code>deploy.rb</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">role <span style="color:#ff3333; font-weight:bold;">:search</span>, <span style="color:#996600;">&quot;USERNAME@SEARCHHOST&quot;</span></pre></div></div>

<p>To keep things simple (sort of), I deploy the whole codebase to both hosts. <code>app</code> is configured via the DreamHost control panel to run Passenger out of the Rails directory; <code>search</code> is configured not to, and I manage the search server via Capistrano.</p>
<p>There doesn&#8217;t seem to be a way <em>not</em> to run a web server on the PS host, so I created a <code>public/redirect</code> directory, told DreamHost to use that as my web directory, and put this in <code>public/redirect/.htaccess</code>:</p>
<pre>Redirect 301 / http://touringmachine.org/</pre>
<p>The standard Capistrano recipes assume you want to deploy to the same directory on each host. Unfortunately DreamHost won&#8217;t let you have the same username on both a shared and a PS host - and you can&#8217;t install your app anywhere outside of your home directory. Stuff breaks if you use a relative path for installation. This worked for me, at the top of <code>deploy.rb</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">set <span style="color:#ff3333; font-weight:bold;">:application</span>, <span style="color:#996600;">&quot;touringmachine.org&quot;</span>
set <span style="color:#ff3333; font-weight:bold;">:applicationdir</span>, <span style="color:#996600;">&quot;`(cd ~; pwd)`/#{application}&quot;</span></pre></div></div>

<h4>Thinking Sphinx</h4>
<p>Thinking Sphinx&#8217;s Sphinx installer can&#8217;t be used as-is for DreamHost PS, for two reasons: It fails if PostgreSQL isn&#8217;t available, and it assumes you have <code>sudo</code> permission. I&#8217;ve submitted a <span style="text-decoration: line-through;"><a href="http://github.com/eostrom/thinking-sphinx/blob/2639ee5a47cc9f9e705625b4cbc57b77e0ed5783/lib/thinking_sphinx/deploy/capistrano.rb">revised version</a></span> that fixes both problems. (It&#8217;ll still use sudo if you set :use_sudo to true.) For now, <span style="text-decoration: line-through;">you&#8217;ll need to copy my <a href="http://github.com/eostrom/thinking-sphinx/blob/2639ee5a47cc9f9e705625b4cbc57b77e0ed5783/lib/thinking_sphinx/deploy/capistrano.rb">capistrano.rb</a> into your project if you want to use it</span>, (<strong>Update:</strong> it&#8217;s now <a href="http://github.com/freelancing-god/thinking-sphinx/blob/7e323a1d9b15d8c529c67edfbac29f9f04d664cd/lib/thinking_sphinx/deploy/capistrano.rb">in the master repository</a>, and should be in the next gem version) and add this to <code>deploy.rb</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">set <span style="color:#ff3333; font-weight:bold;">:thinking_sphinx_configure_args</span>, <span style="color:#996600;">&quot;--prefix=$HOME/software&quot;</span> <span style="color:#008000; font-style:italic;"># where to install Sphinx</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">&quot;#{File.dirname(__FILE__)}/thinking_sphinx_capistrano&quot;</span></pre></div></div>

<p>You&#8217;ll also need this in <code>config/sphinx.yml</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">production:
  ...
  <span style="color:#9900CC;">bin_path</span>: <span style="color:#996600;">'/home/USERNAME/software/bin'</span></pre></div></div>

<p>Thinking Sphinx lets you specify a path to keep the indexes in, and comes with a task to create the path when you set up the server. Unfortunately the task is assigned to the <code>app</code> role. I haven&#8217;t patched Thinking Sphinx for this yet - I just put this in <code>deploy.rb</code> (after the above snippet):</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">namespace <span style="color:#ff3333; font-weight:bold;">:thinking_sphinx</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#008000; font-style:italic;"># Override TS's task to use :search instead of :web, and not use sudo</span>
  desc <span style="color:#996600;">&quot;Add the shared folder for sphinx files for the production environment&quot;</span>
  task <span style="color:#ff3333; font-weight:bold;">:shared_sphinx_folder</span>, <span style="color:#ff3333; font-weight:bold;">:roles</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:search</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    run <span style="color:#996600;">&quot;mkdir -p #{shared_path}/db/sphinx/#{rails_env}&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
after <span style="color:#996600;">&quot;deploy:setup&quot;</span>, <span style="color:#996600;">&quot;thinking_sphinx:shared_sphinx_folder&quot;</span></pre></div></div>

<p>The other tasks assume you want them run on all machines; for now I&#8217;ve dealt with that by using, e.g., <code>cap ROLES=search thinking_sphinx:install:sphinx</code> to do Sphinx stuff.</p>
<h4>Delayed Deltas</h4>
<p>I used <a href="http://freelancing-god.github.com/ts/en/deltas.html">delayed deltas</a> to keep the index up to date. The <a href="http://github.com/collectiveidea/delayed_job/tree/master">delayed_job</a> plugin also comes with Capistrano tasks for managing a DJ daemon. (DJ is packaged with Thinking Sphinx, but you should install the latest version as a plugin.) <code>cap delayed_job:start</code> runs <code>rake delayed_job:start</code> on the server. This Rake task loads the whole Rails environment, then forks itself to run as a daemon. Combine this with Sphinx itself and an ssh session, and suddenly you&#8217;re using too much memory for a minimal DreamHost PS server (150M + swap). The forked daemon is killed before it can start.</p>
<p>I could upgrade, but it&#8217;s pretty ridiculous: You don&#8217;t need a whole Rails environment to start a daemon. I wrote a lo-fi replacement for <code>rake delayed_job:*</code> that does only what I need:</p>

<div class="wp_syntax"><div class="code"><pre>#!/bin/sh
PIDFILE=tmp/pids/delayed_job.pid
command=$1
cd `dirname $0`/..
case $command in
  start)
    nohup script/delayed_job run 2&gt;&amp;1 &gt;&gt;log/delayed_job.log &amp;
    <SEMI><SEMI>
  stop)
    if [ -f $PIDFILE ]
    then
      pid=`cat $PIDFILE`
      rm $PIDFILE
      kill $pid 2&gt;/dev/null
    fi
    <SEMI><SEMI>
  restart)
    $0 stop
    $0 start
    <SEMI><SEMI>
esac</pre></div></div>

<p>Put that in <code>script/dj</code>, and add another chunk to <code>deploy.rb</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">namespace <span style="color:#ff3333; font-weight:bold;">:delayed_job</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  desc <span style="color:#996600;">&quot;Stop the delayed_job process&quot;</span>
  task <span style="color:#ff3333; font-weight:bold;">:stop</span>, <span style="color:#ff3333; font-weight:bold;">:roles</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:search</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    run <span style="color:#996600;">&quot;cd #{current_path}; env RAILS_ENV=#{rails_env} script/dj stop&quot;</span>
    <span style="color:#CC0066; font-weight:bold;">sleep</span> <span style="color:#006666;">1</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  desc <span style="color:#996600;">&quot;Start the delayed_job process&quot;</span>
  task <span style="color:#ff3333; font-weight:bold;">:start</span>, <span style="color:#ff3333; font-weight:bold;">:roles</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:search</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    run <span style="color:#996600;">&quot;cd #{current_path}; env RAILS_ENV=#{rails_env} script/dj start&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  desc <span style="color:#996600;">&quot;Restart the delayed_job process&quot;</span>
  task <span style="color:#ff3333; font-weight:bold;">:restart</span>, <span style="color:#ff3333; font-weight:bold;">:roles</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#ff3333; font-weight:bold;">:search</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    run <span style="color:#996600;">&quot;cd #{current_path}; env RAILS_ENV=#{rails_env} script/dj restart&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
before <span style="color:#996600;">&quot;thinking_sphinx:configure&quot;</span>, <span style="color:#996600;">&quot;delayed_job:stop&quot;</span>  <span style="color:#008000; font-style:italic;"># limited resources</span>
after  <span style="color:#996600;">&quot;thinking_sphinx:configure&quot;</span>, <span style="color:#996600;">&quot;delayed_job:start&quot;</span> <span style="color:#008000; font-style:italic;"># limited resources</span>
&nbsp;
after <span style="color:#996600;">&quot;deploy:stop&quot;</span>,    <span style="color:#996600;">&quot;delayed_job:stop&quot;</span>
after <span style="color:#996600;">&quot;deploy:start&quot;</span>,   <span style="color:#996600;">&quot;delayed_job:start&quot;</span>
after <span style="color:#996600;">&quot;deploy:restart&quot;</span>, <span style="color:#996600;">&quot;delayed_job:restart&quot;</span></pre></div></div>

<p>(Note that you can&#8217;t get by on delta indexes alone; I have a cron job that rebuilds the Sphinx index every now and then.)</p>
<p>Of course, you also don&#8217;t need a whole Rails environment to wake up every 5 seconds, see if there are tables that need reindexing, and fire off a request to the Sphinx server. If I want to keep this site cheap, I may need to optimize delayed_job out of the way. But for now, this is working, and it&#8217;s time to get back to making the app cool.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2009/06/27/deploying-thinking-sphinx-on-dreamhost-ps/feed/</wfw:commentRss>
		</item>
		<item>
		<title>where it&#8217;s at</title>
		<link>http://www.echographia.com/blog/2009/06/12/where-its-at/</link>
		<comments>http://www.echographia.com/blog/2009/06/12/where-its-at/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 07:22:06 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[rails]]></category>

		<category><![CDATA[ruby]]></category>

		<category><![CDATA[suggestions]]></category>

		<category><![CDATA[touring machine]]></category>

		<category><![CDATA[ydn]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=84</guid>
		<description><![CDATA[With the infrastructure in place for the suggestions feature, it&#8217;s easy to add new kinds of suggestions - especially if someone else has already done most of the work. For example, given an address, we should be able to look up the names of businesses at that address, and suggest them as venue names.
Yahoo! and [...]]]></description>
			<content:encoded><![CDATA[<p>With the <a href="http://www.echographia.com/blog/2009/06/10/how-to-help/">infrastructure</a> in place for the <a href="http://www.echographia.com/blog/2009/06/10/how-to-help/">suggestions feature</a>, it&#8217;s easy to add new kinds of suggestions - especially if someone else has already done most of the work. For example, given an address, we should be able to look up the names of businesses at that address, and suggest them as venue names.</p>
<p>Yahoo! and Google both do this automatically if you search for <a href="http://search.yahoo.com/search?p=1805+geary+blvd%2C+san+francisco%2C+ca&amp;fr=yfp-t-105&amp;toggle=1&amp;cop=mss&amp;ei=UTF-8">something they recognize as an address</a>. But Google doesn&#8217;t seem to provide an API. <a href="http://developer.yahoo.com/search/local/V3/localSearch.html">Yahoo! Local does</a>, and the <a href="http://ym4r.rubyforge.org/">YM4R gem</a> already provides a nice Ruby wrapper for it. So:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">  <span style="color:#9966CC; font-weight:bold;">def</span> make_suggestions_based_on_address
    businesses = <span style="color:#6666ff; font-weight:bold;">Ym4r::YahooMaps::BuildingBlock::LocalSearch</span>::get<span style="color:#006600; font-weight:bold;">&#40;</span>
      <span style="color:#ff3333; font-weight:bold;">:location</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; address.<span style="color:#9900CC;">block</span>,
      <span style="color:#ff3333; font-weight:bold;">:radius</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; <span style="color:#006666;">0.001</span>,
      <span style="color:#ff3333; font-weight:bold;">:results</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; <span style="color:#006666;">20</span>,
      <span style="color:#ff3333; font-weight:bold;">:query</span> =<span style="color:#006600; font-weight:bold;">&amp;</span>gt; <span style="color:#996600;">'*'</span>
      <span style="color:#006600; font-weight:bold;">&#41;</span>
    businesses.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>business<span style="color:#006600; font-weight:bold;">|</span>
      add_suggestion<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:name</span>, business.<span style="color:#9900CC;">title</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      add_suggestion<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:url</span>, business.<span style="color:#9900CC;">business_url</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">unless</span> business.<span style="color:#9900CC;">business_url</span>.<span style="color:#9900CC;">blank</span>?
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>You might wonder about the <code>radius</code> parameter. Technically, Yahoo! doesn&#8217;t provide the service I want; I want &#8220;tell me what&#8217;s <em>at</em> this address&#8221;, it has &#8220;tell me what&#8217;s <em>near</em> this address&#8221;. With a radius of 0, it returns up to 10 results. After a little experimentation, it looks to me like 0.01 is a tight enough radius to get me all the businesses at an address - or at least the top 10 - and none of the ones next door.</p>
<p>This is actually one of two ways I&#8217;m subverting the API here. It&#8217;s intended for search, not simple lookup, which is why it&#8217;s a little awkward to get a simple address. It&#8217;s also why Yahoo! provides multiple URLs for each business. The <code>BusinessUrl</code> is the actual URL for the business&#8217;s web site. There&#8217;s also a <code>BusinessClickUrl</code>, which &#8220;contains extra information that helps [them] to optimize [their] search services. Yahoo! requests that you display the <code>BusinessUrl</code>, but link to the <code>BusinessClickUrl</code>, so they can track usage.</p>
<p>The latest code actually uses both URLs, but I&#8217;m going to stop here for now.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2009/06/12/where-its-at/feed/</wfw:commentRss>
		</item>
		<item>
		<title>how to help</title>
		<link>http://www.echographia.com/blog/2009/06/10/how-to-help/</link>
		<comments>http://www.echographia.com/blog/2009/06/10/how-to-help/#comments</comments>
		<pubDate>Wed, 10 Jun 2009 20:55:03 +0000</pubDate>
		<dc:creator>erik</dc:creator>
		
		<category><![CDATA[rails]]></category>

		<category><![CDATA[ruby]]></category>

		<category><![CDATA[suggestions]]></category>

		<category><![CDATA[touring machine]]></category>

		<guid isPermaLink="false">http://www.echographia.com/blog/?p=77</guid>
		<description><![CDATA[Here&#8217;s how the suggestions feature for Touring Machine works so far.
UI

As the user enters data in the new venue form, if we&#8217;re able to make any guesses, we pop them up in a panel to the right of the form.
Each suggestion is a link. If a user clicks a suggestion link, the suggestion is copied [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s how the <a href="http://www.echographia.com/blog/2009/06/09/help-a-brother-out/">suggestions feature</a> for <a href="http://touringmachine.org/">Touring Machine</a> works so far.</p>
<h4>UI</h4>
<ul>
<li>As the user enters data in the new venue form, if we&#8217;re able to make any guesses, we pop them up in a panel to the right of the form.</li>
<li>Each suggestion is a link. If a user clicks a suggestion link, the suggestion is copied into the corresponding form field, and the field is given focus, so the user can edit it. (For example, if the &#8220;page title&#8221; suggestion has additional text around the venue name.)</li>
</ul>
<p>There&#8217;s also an &#8220;accessible&#8221; JavaScript-free UI, but it&#8217;s bad, so I&#8217;m going to pretend it&#8217;s not there.</p>
<h4>Client and Server</h4>
<p>I started drawing an interaction diagram, but it&#8217;s really standard Rails stuff:</p>
<ul>
<li>Model, controller, and view produce the new venue form.</li>
<li>When any form input&#8217;s value changes, we submit the whole form (as an AJAX GET) to the controller&#8217;s <code>suggest</code> action.</li>
<li>The server responds with an RJS view that populates and shows the suggestions panel (if there are any suggestions) or hides it (if not).</li>
<li>The suggestions are links that call a JavaScript function that copies, highlights, and focuses.</li>
<li>Once the user is done editing, the <code>create</code> action just works as normal.</li>
</ul>
<h4>Model</h4>
<p>In keeping with the <a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model">Skinny Controller, Fat Model</a> approach, the only interesting code is in the model. It took me a couple of tries to get a design I liked, but eventually I went with Rails-style magic:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">  <span style="color:#9966CC; font-weight:bold;">def</span> make_suggestions
    <span style="color:#0066ff; font-weight:bold;">@suggestions</span> = <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
    methods.<span style="color:#9900CC;">grep</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">/</span>^make_suggestions_based_on_<span style="color:#006600; font-weight:bold;">/</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>method<span style="color:#006600; font-weight:bold;">|</span>
      attrs = method.<span style="color:#CC0066; font-weight:bold;">sub</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'make_suggestions_based_on_'</span>, <span style="color:#996600;">''</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#CC0066; font-weight:bold;">split</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'_and_'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      values = attrs.<span style="color:#9900CC;">map</span> <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#006600; font-weight:bold;">|</span>attr<span style="color:#006600; font-weight:bold;">|</span> send<span style="color:#006600; font-weight:bold;">&#40;</span>attr<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#125;</span>
      send<span style="color:#006600; font-weight:bold;">&#40;</span>method<span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">unless</span> values.<span style="color:#9900CC;">any</span>?<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">&amp;</span>:blank?<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    suggestions
  <span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>When asked for suggestions:</p>
<ul>
<li> We look through the Venue object for methods of the form <code>make_suggestions_from_attr_and_other_attr</code>. (We support multiple attributes so that, for example, given a venue name and vague address - city and state - we can look up the exact address.) Call these &#8220;suggesters&#8221;.</li>
<li>We call each suggester only if we have non-blank values for all the named attributes (because how are you going to base a suggestion on the venue&#8217;s address if you don&#8217;t know its address?). Note that although I&#8217;m saying &#8220;attributes&#8221;, this code works for attributes, associatons, and in fact any method. This is important because a venue&#8217;s address is actually a <code>has_one</code> association.</li>
<li>The suggesters populate <code>@suggestions</code>, and then we return it.</li>
</ul>
<p>A small thing I&#8217;m pleased by: The <code>make_suggestions</code> method works both as &#8220;create suggestion objects&#8221; and in the idiomatic sense of &#8220;make a suggestion&#8221; as &#8220;suggest something&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.echographia.com/blog/2009/06/10/how-to-help/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
