<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><description>A crazy ride on Rails.</description><title>Locomotivation - The code blog from Squeejee</title><generator>Tumblr (3.0; @locomotivation)</generator><link>http://locomotivation.squeejee.com/</link><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/locomotivation" type="application/rss+xml" /><item><title>Global Settings With Mongo and Rails in 15 Lines of Code</title><description>&lt;p&gt;There is a nice ActiveRecord Rails plugin called &lt;a href="http://github.com/Squeegy/rails-settings/tree/master"&gt;rails-settings&lt;/a&gt; that does the following:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Settings is a plugin that makes managing a table of global key, value pairs easy. Think of 
  it like a global Hash stored in you database, that uses simple ActiveRecord like methods
  for manipulation. Keep track of any global setting that you dont want to hard code into 
  your rails app. You can store any kind of object. Strings, numbers, arrays, or any object.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have used this plugin in the past for global key / value pairs and it works great.  However, for our Floxee app, we are using MongoDB / MongoRecord so this plugin would not work.&lt;/p&gt;

&lt;p&gt;Lucky for us, MongoDB is already a document store so I was able to recreate similar code in about 15 lines of code inside of a “method_missing” method.&lt;/p&gt;

&lt;script src="http://gist.github.com/138612.js"&gt;&lt;/script&gt;&lt;p&gt;With this, we are now able to do things like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&gt;&gt; AppSetting.jim = "is cool"&lt;/code&gt;&lt;br/&gt;&lt;code&gt;=&gt; "is cool"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&gt;&gt; AppSetting.arr = ["jim", "was", "here"]&lt;/code&gt;&lt;br/&gt;&lt;code&gt;=&gt; ["jim", "was", "here"]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&gt;&gt; AppSetting.arr.first&lt;/code&gt;&lt;br/&gt;&lt;code&gt;=&gt; "jim"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&gt;&gt; AppSetting.hsh = {"hash_1"=&gt;1, "hash_2"=&gt;"demo"}&lt;/code&gt;&lt;br/&gt;&lt;code&gt;=&gt; {"hash_1"=&gt;1, "hash_2"=&gt;"demo"}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&gt;&gt; AppSetting.hsh["hash_2"]&lt;/code&gt;&lt;br/&gt;&lt;code&gt;=&gt; "demo"&lt;/code&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=XJKN5b10Zt8:1WOQzNAnSSA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=XJKN5b10Zt8:1WOQzNAnSSA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=XJKN5b10Zt8:1WOQzNAnSSA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=XJKN5b10Zt8:1WOQzNAnSSA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=XJKN5b10Zt8:1WOQzNAnSSA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/XJKN5b10Zt8/133377121</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/133377121</guid><pubDate>Wed, 01 Jul 2009 01:52:00 -0400</pubDate><feedburner:origLink>http://locomotivation.squeejee.com/post/133377121</feedburner:origLink></item><item><title>MongoDB - Ruby friendly document storage that doesn't rhyme with ouch</title><description>&lt;p&gt;I had the opportunity to share &lt;a href="http://mongodb.org"&gt;MongoDB&lt;/a&gt; with the &lt;a href="http://dallasrb.org"&gt;Dallas.rb&lt;/a&gt; group this evening. Thanks &lt;a href="http://twitter.com/mully"&gt;@mully&lt;/a&gt; and &lt;a href="http://twitter.com/jnunemaker"&gt;@jnunemaker&lt;/a&gt; for contributing slides!&lt;/p&gt;

&lt;div style="width:425px;text-align:left" id="__ss_1524935"&gt;
&lt;a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/pengwynn/mongodb-ruby-document-store-that-doesnt-rhyme-with-ouch?type=presentation" title="MongoDB - Ruby document store that doesn't rhyme with ouch"&gt;MongoDB - Ruby document store that doesn’t rhyme with ouch&lt;/a&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=mongodallasrb-090602212347-phpapp02&amp;stripped_title=mongodb-ruby-document-store-that-doesnt-rhyme-with-ouch"&gt;
&lt;param name="allowFullScreen" value="true"&gt;
&lt;param name="allowScriptAccess" value="always"&gt;
&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=mongodallasrb-090602212347-phpapp02&amp;stripped_title=mongodb-ruby-document-store-that-doesnt-rhyme-with-ouch" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;"&gt;View more &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/"&gt;PDF documents&lt;/a&gt; from &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/pengwynn"&gt;Wynn Netherland&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=2EYRyTmqzGg:Wdc2lKkagFQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=2EYRyTmqzGg:Wdc2lKkagFQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=2EYRyTmqzGg:Wdc2lKkagFQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=2EYRyTmqzGg:Wdc2lKkagFQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=2EYRyTmqzGg:Wdc2lKkagFQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/2EYRyTmqzGg/117119353</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/117119353</guid><pubDate>Tue, 02 Jun 2009 22:30:27 -0400</pubDate><category>mongodb</category><category>dallasrb</category><category>ruby</category><category>database</category><feedburner:origLink>http://locomotivation.squeejee.com/post/117119353</feedburner:origLink></item><item><title>Same train, new station</title><description>&lt;p&gt;At RailsConf recently we met a lot of folks who gave us kudos on this blog. Many were surprised to learn that Locomotivation is brought to you by the &lt;a href="http://squeejee.com"&gt;Squeejee &lt;/a&gt; team.&lt;/p&gt;

&lt;p&gt;So, we’ve decided to integrate our code blog a bit more with our &lt;a href="http://squeejee.com"&gt;web site&lt;/a&gt;. Sorry if the move caused your RSS reader to panic. Hopefully, once the dust settles we’ll have more regular content from our crazy ride on Rails.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=WBXqA_Xquj8:ra0TXi8d6Y8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=WBXqA_Xquj8:ra0TXi8d6Y8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=WBXqA_Xquj8:ra0TXi8d6Y8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=WBXqA_Xquj8:ra0TXi8d6Y8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=WBXqA_Xquj8:ra0TXi8d6Y8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/WBXqA_Xquj8/116258144</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/116258144</guid><pubDate>Mon, 01 Jun 2009 10:18:22 -0400</pubDate><feedburner:origLink>http://locomotivation.squeejee.com/post/116258144</feedburner:origLink></item><item><title>Installing CouchDB From Source On Mac OS X</title><description>&lt;p&gt;Wynn and I started dabbling with &lt;a href="http://couchdb.apache.org/"&gt;CouchDB&lt;/a&gt; this week.  As with any technology, step one when dabbling is installing the software.&lt;/p&gt;

&lt;p&gt;For Mac OS X Leopard Intel users, there is a very nice CouchDB packaged zip file that can be downloaded from the &lt;a href="http://code.google.com/p/couchprojects/downloads/list"&gt;Couch Projects Google Code site&lt;/a&gt;.  The huge positive of going this route is that it is drop dead simple to install.  Simply download the .zip file, extract the code, and double-click the “CouchDBX” file.  You then simply click the “Play” icon and you are off and running with CouchDB.&lt;/p&gt;

&lt;p&gt;The negative going this route is that, as the time of this blog post, this package runs CouchDB version 0.8.1 which is about 7 months old.&lt;/p&gt;

&lt;p&gt;In order to run the new 0.9 version of CouchDB, you will need to pull the latest source code from the SVN trunk, install some prereqs, and compile from source.  Here are the exact steps I took to get up and running.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE:  For details on each of these steps, you can look at the README file that is downloaded with the couchdb source code.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;From the command line:&lt;/p&gt;

&lt;script src="http://gist.github.com/113174.js"&gt;&lt;/script&gt;&lt;p&gt;Browse to the following URL in a browser to view your CouchDB Futon admin tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://127.0.0.1:5984/_utils/"&gt;&lt;a href="http://127.0.0.1:5984/_utils/"&gt;http://127.0.0.1:5984/_utils/&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=farBxPx18zk:fcSN_Y0ULbg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=farBxPx18zk:fcSN_Y0ULbg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=farBxPx18zk:fcSN_Y0ULbg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=farBxPx18zk:fcSN_Y0ULbg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=farBxPx18zk:fcSN_Y0ULbg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/farBxPx18zk/109180933</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109180933</guid><pubDate>Thu, 12 Mar 2009 00:00:00 -0400</pubDate><category>couchdb</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109180933</feedburner:origLink></item><item><title>Ruby on Rails Integration Testing with Integrity</title><description>&lt;p&gt;I did some research on what was new in the Continuous Integration testing field in regards to Ruby this week.  We have used &lt;a href="http://cruisecontrolrb.thoughtworks.com/"&gt;CruiseControl.rb&lt;/a&gt; in the past which worked fine, but I remember the setup being a bigger pain than I thought it had to be.  I was hoping for something simpler.&lt;/p&gt;

&lt;p&gt;With this in mind, I decided to give &lt;a href="http://integrityapp.com/"&gt;Integrity&lt;/a&gt; a shot.  Their title of “The Easy and Fun Automated Continuous Integration Server” was exactly what I was looking for.  After installing, configuring, and playing around with it for the past day or so, I have not been disappointed.&lt;/p&gt;

&lt;p&gt;With that being said, I did have to jump around to a few different sources to get everything running correctly on a server with &lt;a href="http://www.modrails.com/"&gt;Phusion Passenger&lt;/a&gt;, so I thought I would document the steps I had to take here.&lt;/p&gt;

&lt;h3&gt;Installation:&lt;/h3&gt;

&lt;script src="http://gist.github.com/113276.js"&gt;&lt;/script&gt;&lt;p&gt;This will create an Integrity application directory with a couple files on your server, mainly config.yml, config.ru, and a “public” directory we will point our Apache virtual host to.&lt;/p&gt;

&lt;p&gt;Edit config.yml to suit your needs.  This file is very will commented and your edits should be self-explanatory.  I kept the default Sqlite database configuration to keep things simple.  However, due to this, I had to install Sqlite on our Ubuntu server via:&lt;/p&gt;

&lt;script src="http://gist.github.com/113277.js"&gt;&lt;/script&gt;&lt;p&gt;Setup a DNS entry to point to this server and create an Apache virtual host as you would any other Rails or Sinatra web application:&lt;/p&gt;

&lt;script src="http://gist.github.com/113278.js"&gt;&lt;/script&gt;&lt;p&gt;Once this is all complete, simply restart apache to access your new Integrity instance.&lt;/p&gt;

&lt;h3&gt;Project Creation:&lt;/h3&gt;

&lt;h4&gt;Create a Test Database:&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;(You can skip this test if you can use Sqlite databases for testing.  Our code inevitably has MySql specific SQL syntax in it which requires us to use MySql for our tests.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create a MySql user that will be used for Integrity tests:&lt;/p&gt;

&lt;script src="http://gist.github.com/113279.js"&gt;&lt;/script&gt;&lt;p&gt;Note that “IDENTIFIED BY PASSWORD” is optional.  If it is not included, your user can log in without a password.&lt;/p&gt;

&lt;h4&gt;Project Creation:&lt;/h4&gt;

&lt;p&gt;Assuming the above was successful, you should now be able to browse to the Integrity URL you created. From here, click the “Add new project” link.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Name:&lt;/em&gt;  Unique name for the project you will be tracking&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Git Repo:&lt;/em&gt;  .git URL of the project you will be tracking.  For example, for an internal project on GitHub, the url would be something like:  git@github.com:acme/my_cool_application.git&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Branch:&lt;/em&gt;  The git branch Integrity should be tracking&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Build Script:&lt;/em&gt;  The rake command to use for testing after the code is pulled into your CI (Continuous Integration) server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rake spec (for rspec)&lt;/li&gt;
&lt;li&gt;rake test (for test unit)&lt;/li&gt;
&lt;li&gt;rake features (for Cucumber)&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Customizing Your Build Script&lt;/h4&gt;

&lt;p&gt;The build script can be a tad bit tricky.  It is our practice not to keep database.yml in Git for password security purposes.  Because of this, Integrity will not know the test database configuration settings needed to run the tests.  To get around this issue, we created a database.yml.test file with the db credentials Integrity would need to run a test similar to this:&lt;/p&gt;

&lt;p&gt;Using MySql:&lt;/p&gt;

&lt;script src="http://gist.github.com/113280.js"&gt;&lt;/script&gt;&lt;p&gt;Using Sqlite:&lt;/p&gt;

&lt;script src="http://gist.github.com/113281.js"&gt;&lt;/script&gt;&lt;p&gt;In my Rails project, I then created the below custom Rake task in “lib/tasks/my_cool_application.rake”:&lt;/p&gt;

&lt;script src="http://gist.github.com/113282.js"&gt;&lt;/script&gt;&lt;p&gt;Once this is done and the files are added/committed/pushed to git, go back to your edit project page in Integrity and change your “Build Script” to be &lt;em&gt;&lt;em&gt;“rake test:move_test_db_config features”&lt;/em&gt;&lt;/em&gt; for Cucumber tests, for example.&lt;/p&gt;

&lt;h4&gt;Manual Build&lt;/h4&gt;

&lt;p&gt;After all of the above is complete, you should be able to click the “manual build” link for your new project to pull your code and run your tests!&lt;/p&gt;

&lt;h3&gt;Automated Tests Via Git Service Hooks&lt;/h3&gt;

&lt;p&gt;Integrity tests are automated with Git Service Hooks.  Adding a service hook is drop-dead simple with GitHub.  To do so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browse to your project in GitHub&lt;/li&gt;
&lt;li&gt;Click on the “Admin” tab&lt;/li&gt;
&lt;li&gt;Click on “Service Hooks” in the header&lt;/li&gt;
&lt;li&gt;In the “Post-Receive URLs” box, add a URL similar to this:

&lt;ul&gt;
&lt;li&gt;http://username:password@integrity.example.com/my_cool_application/push&lt;/li&gt;
&lt;li&gt;where “username” and “password” are the HTTP authentication credentials setup in the integrity/config.yml file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once this is setup, your CI Integrity test will be run everytime you code is pushed to the appropriate branch in GitHub.&lt;/p&gt;

&lt;h3&gt;Notifiers&lt;/h3&gt;

&lt;p&gt;Integrity also provides a &lt;a href="http://integrityapp.com/notifiers"&gt;notification system&lt;/a&gt; to inform you when tests are run.&lt;/p&gt;

&lt;p&gt;Current notifiers include email, Jabber, Campfire, IRC, Twitter, and Basecamp.  These are installed independently of Integrity via Gems.  Once a gem is installed on your CI server, just log back into your project within your Integrity web site and setup your notifier credentials.  You are also free to write your own custom notifiers for applications that are not supported yet.&lt;/p&gt;

&lt;h3&gt;Resources:&lt;/h3&gt;

&lt;p&gt;Thanks to the following sites for assisting me with my original setup and this blog post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://integrityapp.com/"&gt;Integrity web site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://morethanseven.net/2008/12/28/local-continuous-integration-integrity/"&gt;Local Continuous Integration from MoreThanSeven.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/foca/integrity/tree/master"&gt;GitHub Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://groups.google.com/group/integrityapp"&gt;Google Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=MCCD-dX5f4w:nVvZSb3qB0o:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=MCCD-dX5f4w:nVvZSb3qB0o:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=MCCD-dX5f4w:nVvZSb3qB0o:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=MCCD-dX5f4w:nVvZSb3qB0o:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=MCCD-dX5f4w:nVvZSb3qB0o:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/MCCD-dX5f4w/109227509</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109227509</guid><pubDate>Wed, 25 Feb 2009 00:00:00 -0500</pubDate><category>integrity</category><category>ruby</category><category>continuous integration</category><category>testing</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109227509</feedburner:origLink></item><item><title>Workstreaming with Present.ly</title><description>&lt;p&gt;Having fled the cubicle jungle of Corporate America, we have a strong distaste for status reports. That’s not to say we don’t believe in letting others on the team know what we’re doing. We just prefer to &lt;a href="http://webworkerdaily.com/2007/03/03/workstreaming-the-new-face-time/"&gt;workstream&lt;/a&gt; our activities and let our work announce itself to the rest of the team.&lt;/p&gt;

&lt;h3&gt;
&lt;a href="http://present.ly"&gt;Present.ly&lt;/a&gt; - our workstreaming tool&lt;/h3&gt;

&lt;p&gt;I hate to put &lt;a href="http://present.ly"&gt;Present.ly&lt;/a&gt; in a box by calling it “Twitter for Business” (because it is so much more than that), but that’s the easiest way to describe it.&lt;/p&gt;

&lt;h4&gt;Familiar Twitter feel&lt;/h4&gt;

&lt;p&gt;If you’re comfortable using Twitter, you’ll feel at home with Present.ly because it supports @replies, direct messages, and hashtags.&lt;/p&gt;

&lt;h4&gt;Questions and urgent messages&lt;/h4&gt;

&lt;p&gt;Present.ly adds some smarts to the display of messages or “tweets.” Messages that end in a question mark are displayed as questions to the group: &lt;img src="http://assets.squeejee.com/locomotivation/images/question.png" alt="question mark"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both"/&gt;&lt;/p&gt;

&lt;p&gt;Need to mark a message as urgent? Simply add three exclamation marks: &lt;img src="http://assets.squeejee.com/locomotivation/images/urgent.png" alt="urgent"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both"/&gt;&lt;/p&gt;

&lt;h4&gt;Private and secure&lt;/h4&gt;

&lt;p&gt;Unlike Twitter, only your users see your messages.&lt;/p&gt;

&lt;h4&gt;Groups!&lt;/h4&gt;

&lt;p&gt;I really hope Twitter builds functionality for groups one day and when they do, I hope they rip off Present.ly’s implementation. Admins control who belongs to what groups. Perhaps the coolest feature of Present.ly groups is the broadcast. Broadcasts are like direct messages to a group except they are displayed as if sent from the group itself, which gets its own avatar. The following broadcast message&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;b system Elvis has left the building
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;would display as if it came from the System itself: &lt;img src="http://assets.squeejee.com/locomotivation/images/broadcast.png" alt="broadcast"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both"/&gt;&lt;/p&gt;

&lt;h3&gt;How we use it&lt;/h3&gt;

&lt;p&gt;Much of our Present.ly use is still manual. We tweet each other questions and comments just like we do on Twitter. In fact, Present.ly let’s you cross-post to twitter by using the t! command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;t! This message would go to Twitter, too!
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But, increasingly, we’ve automated much of our workstreaming by sending broadcast messages at certain points in our development cycle.&lt;/p&gt;

&lt;h4&gt;Source control checkins&lt;/h4&gt;

&lt;p&gt;One of the most important things we do as a development team is add code to source control. We use “GitHub”:http://github.com for our code repositories. GitHub has a nifty feature called Service Hooks, which allows you to send a message to a number of third party services for code commits to your repo. Setting up Present.ly really easy:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.squeejee.com/locomotivation/images/posts/github-presently-1.png" alt="github presently"/&gt;&lt;br style="clear: both"/&gt;&lt;img src="http://assets.squeejee.com/locomotivation/images/posts/github-presently-2.png" alt="github presently"/&gt;&lt;br style="clear: both"/&gt;&lt;img src="http://assets.squeejee.com/locomotivation/images/posts/github-presently-3.png" alt="github presently"/&gt;&lt;br style="clear: both"/&gt;&lt;/p&gt;

&lt;p&gt;Once set up, GitHub will send a new broadcast message from the group you specify with each new code push. The message includes the commit message and a link to the commit back on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.squeejee.com/locomotivation/images/posts/github-presently-4.png" alt="github presently"/&gt;&lt;br style="clear: both"/&gt;&lt;/p&gt;

&lt;h5&gt;Deploy notices&lt;/h5&gt;

&lt;p&gt;Before Present.ly we had been sending our deployment notices to &lt;a href="http://www.campfirenow.com"&gt;Campfire&lt;/a&gt; using &lt;a href="http://www.locomotivation.com/blog/2007/09/29/heat-up-your-campfire-with-tinder.html"&gt;Tinder&lt;/a&gt;. While this works well, we really preferred to have our workstream in one spot and leav eour Campfires for discussion and chat. Since Present.ly implements the Twitter API, wiring up the deploy notices from &lt;a href="http://www.capify.org/"&gt;Capistrano&lt;/a&gt; was a piece of cake.&lt;/p&gt;

&lt;script src="http://gist.github.com/113292.js"&gt;&lt;/script&gt;&lt;p&gt;Now everybody knows when the build is done:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.squeejee.com/locomotivation/images/posts/deploy-presently.png" alt="deploy presntly"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both"/&gt;&lt;/p&gt;

&lt;h4&gt;How are you using it?&lt;/h4&gt;

&lt;p&gt;We’d love to hear how you’re using Present.ly in your workflow? Any cool uses we’re missing?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=kuTF4RjPdlM:8uvSqh9ULxA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=kuTF4RjPdlM:8uvSqh9ULxA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=kuTF4RjPdlM:8uvSqh9ULxA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=kuTF4RjPdlM:8uvSqh9ULxA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=kuTF4RjPdlM:8uvSqh9ULxA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/kuTF4RjPdlM/109235014</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109235014</guid><pubDate>Sat, 20 Dec 2008 00:00:00 -0500</pubDate><category>workstreaming</category><category>present.ly</category><category>github</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109235014</feedburner:origLink></item><item><title>People, sites, and software I am thankful for this year</title><description>&lt;p&gt;I’ve been building web applications for a dozen years now and I’m convinced &lt;em&gt;there has never been a better time to be a web developer!&lt;/em&gt; I can’t imagine doing my job if it weren’t for the impact of some great people, sites, and software. This post isn’t a &lt;a href="http://nettuts.com/articles/web-roundups/10-biggest-milestones-in-web-development/"&gt;Ten Biggest&lt;/a&gt; style article. I won’t be thanking &lt;a href="http://en.wikipedia.org/wiki/Tim_Berners-Lee"&gt;Tim Berners-Lee&lt;/a&gt;, although we all should.&lt;/p&gt;

&lt;p&gt;Rather, this list is simply the &lt;strong&gt;extra&lt;/strong&gt;ordinary people and things that have made my job a joy over the past year.&lt;/p&gt;

&lt;h3&gt;People I am thankful for&lt;/h3&gt;

&lt;h4&gt;&lt;a href="http://ejohn.org/"&gt;John Resig&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;Creator of &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;, JavaScript ninja, John casts a large shadow on the web development landscape. Looking at John’s code has taught me more about JavaScript than every book I’ve ever bought on the subject.&lt;/p&gt;

&lt;h4&gt;&lt;a href="http://ozmm.org/"&gt;Chris Wanstrath aka defunkt&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;Half of the &lt;a href="http://errtheblog.com/"&gt;err the blog&lt;/a&gt; duo, a third of the &lt;a href="http://github.com"&gt;GitHub posse&lt;/a&gt;, Chris is the most energetic Rubyist I’ve seen. Just watching the amount of software his company puts out will either inspire you or send you into a shame spiral.&lt;/p&gt;

&lt;h4&gt;&lt;a href="http://b.lesseverything.com/"&gt;The Less Everything guys, Steve and Allan&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;Makers of &lt;a href="http://www.lessaccounting.com/"&gt;cool&lt;/a&gt;, &lt;a href="http://lovdbyless.com/"&gt;free&lt;/a&gt;, and &lt;a href="http://weallhatequickbooks.com/"&gt;downright funny&lt;/a&gt; software, these guys write more &lt;em&gt;less&lt;/em&gt; code than anybody around. I’m thankful that guys like this are around to keep us from taking ourselves too seriously.&lt;/p&gt;

&lt;h4&gt;&lt;a href="http://www.redmine.org"&gt;Jean-Philippe Lang&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;I’ve never seen anyone single-handedly will a project into existence like JP. While there is no doubt that Redmine has a large and growing community behind it, I’m sure everyone would agree that Redmine exists because of the sheer will of Jean-Phillipe.&lt;/p&gt;

&lt;h3&gt;Sites I am thankful for&lt;/h3&gt;

&lt;h4&gt;&lt;a href="http://github.com"&gt;GitHub&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;No other technology has impacted me more this year than &lt;a href="http://git.or.cz/"&gt;Git&lt;/a&gt;, and I don’t think it would have happened without GitHub.  We first signed up for GitHub because we wanted a turnkey central repository for our projects. But GitHub is so much more. GitHub brings social networking to source control. This means you can see what other folks are working on and discover new projects and tools all the time. It also means it’s never been as easy to contribute to open source. Want to improve an open source project? With GitHub, you just fork it and send a pull request. If the original owner goes off the grid, no worries, you’ve got your own repo and keep on chugging.&lt;/p&gt;

&lt;h4&gt;&lt;a href="http://twitter.com"&gt;Twitter&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;I’ll have to admit at first I didn’t get Twitter. I mean, take a look at the &lt;a href="http://twitter.com/public_timeline"&gt;public timeline&lt;/a&gt;. Who cares when random folk are &lt;a href="http://twitter.com/sambam87/status/1026603120"&gt;bathing&lt;/a&gt;? I’ve since learned that there is no better tool for finding those other folks that are just as weird as you. You know, those long lost members of your tribe(s). Folks who are interested in those things you bug everyone else about.&lt;/p&gt;

&lt;h4&gt;&lt;a href="http://presentlyapp.com"&gt;Presently&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;Soon after I climbed on the Twitter train, I longed for something as easy as Twitter to workstream all those source control checkin notices, build statuses, and “what I am doing” updates. The folks at &lt;a href="http://www.intridea.com/"&gt;Intridea&lt;/a&gt; have hit a homerun with Present.ly. The groups and hashtag functionality is awesome. Try it for your team!&lt;/p&gt;

&lt;h3&gt;Software I am thankful for&lt;/h3&gt;

&lt;h4&gt;&lt;a href="http://redmine.org"&gt;Redmine&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;We’ve used a lot of issue trackers. Some &lt;a href="http://unfuddle.com"&gt;great ones&lt;/a&gt;. Some &lt;a href="http://trac.edgewall.org/"&gt;workhorses&lt;/a&gt;. But we’ve never used anything as extensible as Redmine. Plugins, themes, and custom fields, oh my!&lt;/p&gt;

&lt;h4&gt;&lt;a href="http://webby.rubyforge.org"&gt;Webby&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;I want to thank &lt;a href="http://github.com/TwP/webby/tree/master"&gt;Tim Pease&lt;/a&gt; for building such a cool little tool. I’m using Webby to write this post!&lt;/p&gt;

&lt;h4&gt;&lt;a href="http://www.blueprintcss.org/"&gt;Blueprint CSS&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;Blueprint has been a part of my standard toolbox since it came out. Blueprint brings sanity to my layout and typography while make it so easy developers can do CSS!&lt;/p&gt;

&lt;p&gt;This year I’ll be looking to &lt;a href="http://github.com/chriseppstein/compass/tree/master"&gt;Compass&lt;/a&gt; to semantify my Blueprint.&lt;/p&gt;

&lt;h4&gt;&lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;I wasn’t in the market for a new JavaScript framework when I discovered jQuery, but I quickly fell in love with this one. jQuery lets me do more powerful things with less code than any other framework I’ve used. With comprehensive CSS3 (and bonus) selectors, chaining, and plugin architecture it’s a joy to use.&lt;/p&gt;

&lt;h3&gt;How about you?&lt;/h3&gt;

&lt;p&gt;What are the people, sites, and projects you’re thankful for this year?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=zd6DhK-g7fw:ylTv-reRyaY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=zd6DhK-g7fw:ylTv-reRyaY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=zd6DhK-g7fw:ylTv-reRyaY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=zd6DhK-g7fw:ylTv-reRyaY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=zd6DhK-g7fw:ylTv-reRyaY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/zd6DhK-g7fw/109247775</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109247775</guid><pubDate>Thu, 27 Nov 2008 00:00:00 -0500</pubDate><category>thanksgiving</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109247775</feedburner:origLink></item><item><title>Javascript and Ruby Frameworks at Lone Star Ruby Conf 2008</title><description>&lt;p&gt;&lt;a href="http://www.flickr.com/photos/27640669@N02/2831498316/" style="float: right; border: none; background: #fff"&gt;&lt;img class="frame" src="http://farm4.static.flickr.com/3071/2831498316_4b8822eeeb_m.jpg" alt="Wynn giving a talk at Lone Star Ruby Conf 2008"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had the opportunity to give a talk at Lone Star Ruby Conf 2008. There are a lot of hardcore Rubyists here writing great web applications in Rails, Merb, and Waves. It was fun to knock around ideas with so many folks about how Javascript fits into the mix.&lt;/p&gt;

&lt;div id="__ss_585749" style="width: 425px; text-align: left;"&gt;
&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slideshare.net/swf/ssplayer2.swf?doc=javascriptandrubyframworks-1220732647167244-8&amp;stripped_title=javascript-and-ruby-frameworks-presentation"&gt;
&lt;param name="allowFullScreen" value="true"&gt;
&lt;param name="allowScriptAccess" value="always"&gt;
&lt;embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=javascriptandrubyframworks-1220732647167244-8&amp;stripped_title=javascript-and-ruby-frameworks-presentation" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size: 11px; font-family: tahoma,arial; height: 26px; padding-top: 2px;"&gt;View SlideShare &lt;a href="http://www.slideshare.net/pengwynn/javascript-and-ruby-frameworks-presentation?src=embed" title="View Javascript And Ruby Frameworks on SlideShare" style="text-decoration: underline;"&gt;presentation&lt;/a&gt; or &lt;a href="http://www.slideshare.net/upload?src=embed" style="text-decoration: underline;"&gt;Upload&lt;/a&gt; your own. (tags: &lt;a href="http://slideshare.net/tag/ruby" style="text-decoration: underline;"&gt;ruby&lt;/a&gt; &lt;a href="http://slideshare.net/tag/javascript" style="text-decoration: underline;"&gt;javascript&lt;/a&gt;)&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Until the slides are posted on the &lt;a href="http://www.lonestarrubyconf.com/"&gt;official &lt;span class="caps"&gt;LSRC&lt;/span&gt; site&lt;/a&gt; you can grab them from &lt;a href="http://www.slideshare.net/pengwynn/javascript-and-ruby-frameworks-presentation"&gt;SlideShare&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=SQgHhYkXRlM:IFONzEJuYL0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=SQgHhYkXRlM:IFONzEJuYL0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=SQgHhYkXRlM:IFONzEJuYL0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=SQgHhYkXRlM:IFONzEJuYL0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=SQgHhYkXRlM:IFONzEJuYL0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/SQgHhYkXRlM/109260735</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109260735</guid><pubDate>Sun, 07 Sep 2008 00:00:00 -0400</pubDate><category>lsrc</category><category>presentations</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109260735</feedburner:origLink></item><item><title>Simplify ActiveRecord Aggregates and Other Goodies via named_scope</title><description>&lt;p&gt;We’ve been huge fans of named_scope since its introduction in Rails 2.1.  If you are new to the named_scope concept, please read &lt;a href="http://ryandaigle.com/articles/2008/3/24/what-s-new-in-edge-rails-has-finder-functionality"&gt;Ryan Daigle’s intro to named_scope&lt;/a&gt; and watch Ryan Bates’ &lt;a href="http://railscasts.com/episodes/108"&gt;named_scope RailsCast&lt;/a&gt;.  Ryan Daigle also had a good follow up article &lt;a href="http://ryandaigle.com/articles/2008/8/20/named-scope-it-s-not-just-for-conditions-ya-know"&gt;explaining his utility scope gem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With a recent project, we really started utilizing extensions with named_scope which is very powerful and cleaned up our code considerably.&lt;/p&gt;

&lt;p&gt;For example, let’s say you have an issue tracking system that has an Entry model that belongs_to both a User model and a Project model.  Using named_scope, we can easily access Entries for a date range filtered to both the Project and User level using named_scope lambdas:&lt;/p&gt;

&lt;script src="http://gist.github.com/113283.js"&gt;&lt;/script&gt;&lt;p&gt;To find the Entries for the last week for the first user, we would simply do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;u = User.first
u.entries.by_date(1.week.ago, Time.now)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To find the Entries for the last week for the first project, we would simply do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;p = Project.first
p.entries.by_date(1.week.ago, Time.now)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What happens if we want to see the sum of hours worked for a date range?  This is where extensions come in very handy.  Let’s add a “total_duration” extension to our named_scope:&lt;/p&gt;

&lt;script src="http://gist.github.com/113284.js"&gt;&lt;/script&gt;&lt;p&gt;Now to get the duration for the past week for the first user we simply do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;u = User.first
u.entries.by_date(1.week.ago, Time.now).total_duration
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;with similar logic at the project level.&lt;/p&gt;

&lt;p&gt;To group all of the entries by project, we would do:&lt;/p&gt;

&lt;script src="http://gist.github.com/113285.js"&gt;&lt;/script&gt;&lt;p&gt;If you need the lambda arguments inside your named_scope extension, you will need to use “self.proxy_options”.  For example, let’s say you need the from and to dates in order to calculate the average duration per weekday.  That code would look something like this:&lt;/p&gt;

&lt;script src="http://gist.github.com/113287.js"&gt;&lt;/script&gt;&lt;p&gt;In the above example, “weekday_count” is a custom method we created to calculate the number of weekdays between 2 given dates.&lt;/p&gt;

&lt;p&gt;The opportunities to simplify code using named_scope is great.  As I was writing this post, one of our team members, Jason Derrett, created a named_scope to wrap a Xapian full text search into a Lesson model:&lt;/p&gt;

&lt;script src="http://gist.github.com/113288.js"&gt;&lt;/script&gt;&lt;p&gt;In this example, using Xapian to search for a keyword becomes as simple as:&lt;/p&gt;

&lt;pre&gt;&lt;code class="ruby"&gt;Lesson.find_with_xapian "fishing"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;How cool is that?!?&lt;/p&gt;

&lt;p&gt;Are there any other cool named_scope tricks out there that any of you are using?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=yzfov3og-hQ:fAwvRa0ZOj0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=yzfov3og-hQ:fAwvRa0ZOj0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=yzfov3og-hQ:fAwvRa0ZOj0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=yzfov3og-hQ:fAwvRa0ZOj0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=yzfov3og-hQ:fAwvRa0ZOj0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/yzfov3og-hQ/109282292</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109282292</guid><pubDate>Mon, 25 Aug 2008 00:00:00 -0400</pubDate><category>rails</category><category>active record</category><category>database</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109282292</feedburner:origLink></item><item><title>Simple Ruby on Rails Full Text Search Using Xapian</title><description>&lt;p&gt;In my &lt;a href="http://locomotivation.com/2008/07/15/mulling-over-our-ruby-on-rails-full-text-search-options"&gt;previous blog post&lt;/a&gt;, I wrote about the several full-text search engines that are available to us as Rails programmers and how we ultimately decided to go with &lt;a href="http://www.xapian.com/"&gt;Xapian&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Background&lt;/h3&gt;

&lt;p&gt;I am by no means a Xapian expert.  I first heard about it less than a week ago.  However, we have been very impressed with its combination of ease of installation and robust features.&lt;/p&gt;

&lt;p&gt;Xapian is written in C++ and has been around in its current form since 2001.  Its code base is derived from a previous search tool &lt;a href="http://www.xapian.org/history.php"&gt;that went closed source.&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Written in C++&lt;/li&gt;
  &lt;li&gt;
&lt;a href="http://www.xapian.org/features.php"&gt;Feature rich&lt;/a&gt; including relevance feedback, boolean search operators, stemming, wildcard searching, and similar results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Its &lt;a href="http://www.xapian.org/users.php"&gt;user list&lt;/a&gt; includes &lt;a href="http://localhost:3000/del.icio.us"&gt;del.icio.us&lt;/a&gt; and &lt;a href="http://localhost:3000/www.gmane.org"&gt;GMane&lt;/a&gt; both of which are indexing tens of millions of records, so scalability should not be an issue. Here is a &lt;a href="http://www.xapian.org/docs/scalability.html"&gt;quote about scalability from the site&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;People often want to know how Xapian will scale. The short answer is “very well” - a previous version of the software
  powered BrightStation’s Webtop search engine, which offered a search over around 500 million web pages (around 1.5 terabytes of database files).&lt;/p&gt;
  
  &lt;p&gt;Searches took less than a second.&lt;/p&gt;
  
  &lt;p&gt;The largest recent installation we’re aware of is probably gmane, which currently indexes over 50 million mail messages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the other hand, indexing time could take a while if you have a huge set of documents.  According to &lt;a href="http://users.softlab.ece.ntua.gr/~ttsiod/buildWikipediaOfflhttp://xapian.org/users.php#commwebine.html"&gt;this article&lt;/a&gt;, it took about 5 hours to index the entire &lt;a href="http://www.wikipedia.org/"&gt;Wikipedia&lt;/a&gt; database using Xapian for offline Wikipedia access and searching.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://github.com/frabcus/acts_as_xapian/tree/master"&gt;Acts_As_Xapian&lt;/a&gt; is the Rails plugin for the Xapian.  It was written in May 2008 by Francis Irving after he experienced some frustrations with &lt;a href="http://lucene.apache.org/solr/"&gt;Solr&lt;/a&gt;.  Acts_As_Xapian has a google group at &lt;a href="http://groups.google.com/group/acts_as_xapian."&gt;http://groups.google.com/group/acts_as_xapian.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Xapian Installation:&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;For Ubuntu Users:&lt;/em&gt;&lt;/p&gt;

&lt;pre&gt;sudo apt-get install libxapian15 libxapian-ruby1.8&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;For Mac / Other: (taken from the &lt;a href="http://www.xapian.org/docs/install.html"&gt;xapian.org install docs&lt;/a&gt;&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;(version = 1.0.6 at the time of this writing)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;wget &lt;a href="http://oligarchy.co.uk/xapian/1.0.6/xapian-core-1.0.6.tar.gz"&gt;http://oligarchy.co.uk/xapian/1.0.6/xapian-core-1.0.6.tar.gz&lt;/a&gt;
wget &lt;a href="http://oligarchy.co.uk/xapian/1.0.6/xapian-bindings-1.0.6.tar.gz"&gt;http://oligarchy.co.uk/xapian/1.0.6/xapian-bindings-1.0.6.tar.gz&lt;/a&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Alternately if you haven’t installed wget on the mac:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;curl -O &lt;a href="http://oligarchy.co.uk/xapian/1.0.6/xapian-core-1.0.6.tar.gz"&gt;http://oligarchy.co.uk/xapian/1.0.6/xapian-core-1.0.6.tar.gz&lt;/a&gt;
curl -O &lt;a href="http://oligarchy.co.uk/xapian/1.0.6/xapian-bindings-1.0.6.tar.gz"&gt;http://oligarchy.co.uk/xapian/1.0.6/xapian-bindings-1.0.6.tar.gz&lt;/a&gt;

tar zxvf xapian-core-versionnumber.tar.gz
tar zxvf xapian-bindings-versionnumber.tar.gz

cd xapian-core-versionnumber
./configure --prefix=/opt
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you don’t have root access to install Xapian, you can specify a prefix in your home directory, for example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./configure—prefix=/home/jenny/xapian-install


cd xapian-bindings-&lt;version&gt;
./configure XAPIAN_CONFIG=/opt/bin/xapian-config
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Acts_As_Xapian Installation:&lt;/h3&gt;

&lt;p&gt;Install the plugin:
    script/plugin install git://github.com/frabcus/acts_as_xapian.git&lt;/p&gt;

&lt;p&gt;Generate and execute the acts_as_xapian migration:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/generate acts_as_xapian
rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Basic Model Code&lt;/h3&gt;

&lt;p&gt;To include fields to your index, you will need to add an “acts_as_xapian” call to the model you want to index.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Lesson &lt; ActiveRecord::Base
  acts_as_xapian :texts =&gt; [:name, :description]
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this case, only the data found in the “name” and “description” attributes of our Lesson model will be searched.&lt;/p&gt;

&lt;h3&gt;A Quick Test Of Your Index&lt;/h3&gt;

&lt;p&gt;At this point, you can quickly test that your Xapian install, plugin, and model code are all working together via the following rake commands:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To build the index:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;rake xapian:rebuild_index models="Lesson" RAILS_ENV=development&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;(You can add more models with a space delimeter.  “Lesson User Tag”)&lt;/em&gt;
&lt;strong&gt;To update index:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;rake xapian:update_index RAILS_ENV=development&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;To test index:&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;rake xapian:query models="Lesson" query="golf" RAILS_ENV=development&lt;/pre&gt;

&lt;h3&gt;The Basic Search&lt;/h3&gt;

&lt;p&gt;Adding the this line of code
    @search = ActsAsXapian::Search.new([Lesson], @lesson_search_params)&lt;/p&gt;

&lt;p&gt;will return a ActsAsXapian::Search object with the following (From the acts_as_xapian &lt;a href="http://github.com/frabcus/acts_as_xapian/wikis"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;description – a techy one, to check how the query has been parsed&lt;/li&gt;
  &lt;li&gt;matches_estimated – a guesstimate at the total number of hits&lt;/li&gt;
  &lt;li&gt;spelling_correction – the corrected query string if there is a correction, otherwise nil&lt;/li&gt;
  &lt;li&gt;words_to_highlight – list of words for you to highlight, perhaps with TextHelper::highlight&lt;/li&gt;
  &lt;li&gt;results – an array of hashes each containing:
&lt;ul&gt;
&lt;li&gt;:model – your Rails model, this is what you most want!&lt;/li&gt;
  &lt;li&gt;:weight – relevancy measure&lt;/li&gt;
  &lt;li&gt;:percent – the weight as a %, 0 meaning the item did not match the query at all&lt;/li&gt;
  &lt;li&gt;:collapse_count – number of results with the same prefix, if you specified collapse_by_prefix&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, if we want to return an ActiveRecord model object we could do something like this:
    @lessons = @search.results.collect {|r| r[:model]}&lt;/p&gt;

&lt;p&gt;To get the recommended spelling for a misspelled word(s) in a search, we would do:
    @corrections = @search.spelling_correction&lt;/p&gt;

&lt;h3&gt;Similar Results&lt;/h3&gt;

&lt;p&gt;If you have a requirement to show similar or “You may also like” results based on a search result set or a single result, this can be done with a single line of code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@similar_lessons = ActsAsXapian::Similar.new([Lesson], @lessons).results.collect {|r| r[:model]}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;where @lessons in this case would be the list of lessons returned in the original search.&lt;/p&gt;

&lt;h3&gt;Updating The Index&lt;/h3&gt;

&lt;p&gt;Unfortunately, one drawback to Acts_As_Xapian is that the indexers are not updated automatically when data in an indexed model is added, modified, or deleted.  When model data is changed, Xapian will put a record in the table ‘acts_as_xapian_jobs’ in your database to notify the update task to update the index.  A cron job or something of the like will be needed to call the following rake task periodically to keep the index up-to-date:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rake xapian:update_index
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Francis, the acts_as_xapian creator, commented on this on my original &lt;a href="http://locomotivation.com/2008/07/15/mulling-over-our-ruby-on-rails-full-text-search-options"&gt;Mulling Over Our Ruby On Rails Full Text Search Options&lt;/a&gt; blog post.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;with Xapian you can update and search simultaneously, and updates are immediate. However, only one thread can update a Xapian database at the same time. Since I wanted offline indexing anyway (as my index operation is risky, complex and slow, involving parsing Word documents, PDFs etc.), I didnâ€™t try to find a solution that causes a second thread in the web application to, say, wait for the database lock. So acts_as_xapian currently only supports offline indexing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;This is a very basic tutorial meant to highlight the steps needed to get up and running with simple Xapian searches quickly.  There are quite a few topics that I did not touch on such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sorting and grouping with the :values option in the model&lt;/li&gt;
  &lt;li&gt;Advanced searching with the :terms option in the model&lt;/li&gt;
  &lt;li&gt;Adding extended attributes from other models into a searched index.  (Such as adding lesson comments to the Lesson index)&lt;/li&gt;
  &lt;li&gt;Filtering searched data (in our case, we had to filter our results so only “active” lessons were returned)&lt;/li&gt;
  &lt;li&gt;Highlighting keywords in the search results&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=dOIQfZS8-hY:Vup6SKX7HeY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=dOIQfZS8-hY:Vup6SKX7HeY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=dOIQfZS8-hY:Vup6SKX7HeY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=dOIQfZS8-hY:Vup6SKX7HeY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=dOIQfZS8-hY:Vup6SKX7HeY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/dOIQfZS8-hY/109279130</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109279130</guid><pubDate>Wed, 23 Jul 2008 00:00:00 -0400</pubDate><category>xapian</category><category>rails</category><category>search</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109279130</feedburner:origLink></item><item><title>Smells Like a Google Search - Easily Grab Inbound Search Terms in Rails Using search_sniffer</title><description>&lt;p&gt;Sure you can mine your referrers in Google Analytics, but why not tailor your site to your users by peeking at what they were looking for when they found your site? The &lt;a href="http://github.com/squeejee/search_sniffer/tree/master"&gt;search_sniffer&lt;/a&gt; plugin makes it too easy to do just that.&lt;/p&gt;

&lt;h2&gt;Make your site sticky, give ‘em more of what they want&lt;/h2&gt;

&lt;p&gt;Once you get folks to your site, the goal is to keep them there. On &lt;a href="http://www.mindbites.com/"&gt;MindBites&lt;/a&gt;, we wanted to provide additional suggested content based on any search terms that led them to the site. The good news is that getting these terms is as easy as parsing the &lt;code&gt;HTTP_REFERER&lt;/code&gt; from the &lt;span class="caps"&gt;HTTP&lt;/span&gt; request. Unfortunately, the format of this url differs between search engines.&lt;/p&gt;

&lt;p&gt;With &lt;a href="http://www.thestandard.com/news/2008/07/15/google-pushes-towards-70-percent-all-u-s-searches-yahoo-microsoft-push-towards-0"&gt;nearly 70 percent market share&lt;/a&gt; let’s take look at a Google example. A search for “Ruby on Rails Houston” from the Firefox search box results in the following &lt;span class="caps"&gt;URL&lt;/span&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.google.com/search?q=ruby+on+rails+houston&amp;ie=utf-8&amp;oe=utf-8&amp;aq=t&amp;rls=org.mozilla%3Aen-US%3Aofficial&amp;client=firefox-a"&gt;&lt;a href="http://www.google.com/search?q=ruby+on+rails+houston&amp;ie=utf-8&amp;oe=utf-8&amp;aq=t&amp;rls=org.mozilla:en-US:official&amp;client=firefox-a"&gt;http://www.google.com/search?q=ruby+on+rails+houston&amp;ie=utf-8&amp;oe=utf-8&amp;aq=t&amp;rls=org.mozilla:en-US:official&amp;client=firefox-a&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We only care about the &lt;code&gt;q&lt;/code&gt; parameter, “ruby on rails houston.” Since “on” is such a common word, we really only care about “ruby rails houston.” The SearchSniffer plugin includes a list of common English words that are removed from the search terms by default.&lt;/p&gt;

&lt;h3&gt;How do I get it?&lt;/h3&gt;

&lt;p&gt;Installation is easy from &lt;a href="http://github.com/squeejee/search_sniffer/tree"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/plugin install git://github.com/squeejee/search_sniffer.git
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;How do I use it?&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;class ApplicationController &amp;lt; ActionController::Base
    before_filter :sniff_referring_search

    ...
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The plugin populates the &lt;code&gt;@referring_search&lt;/code&gt; object containing info that can be passed to a keyword highlighter or internal site search engine to pull related content.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@referring_search.search_terms 
  =&gt; "ruby rails houston" 
@referring_search.raw
  =&gt; "ruby on rails houston
@referring_search.engine
  =&gt; "google
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The plugin supports all the major search engines including Google, Yahoo!, &lt;span class="caps"&gt;MSN&lt;/span&gt;, and more.&lt;/p&gt;

&lt;h3&gt;Let us hear from you!&lt;/h3&gt;

&lt;p&gt;If you find it useful, or have ideas for improvement, let us know!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=5yn46IthVgU:HnW8VO869lU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=5yn46IthVgU:HnW8VO869lU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=5yn46IthVgU:HnW8VO869lU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=5yn46IthVgU:HnW8VO869lU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=5yn46IthVgU:HnW8VO869lU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/5yn46IthVgU/109263017</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109263017</guid><pubDate>Thu, 17 Jul 2008 00:00:00 -0400</pubDate><category>plugins</category><category>rails</category><category>open source</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109263017</feedburner:origLink></item><item><title>Mulling Over Our Ruby on Rails Full Text Search Options</title><description>&lt;p&gt;There are quite a few choices when it comes to adding a full text search in a Ruby on Rails application.  We thought that had considered all of our options when we ultimately settled on using &lt;a href="http://www.sphinxsearch.com/"&gt;Sphinx&lt;/a&gt; / &lt;a href="http://github.com/fauna/ultrasphinx/tree/master"&gt;Ultrasphinx&lt;/a&gt;.  We learned otherwise, though, after stumbling across &lt;a href="http://localhost:3000/www.xapian.org"&gt;Xapian&lt;/a&gt; / &lt;a href="http://github.com/frabcus/acts_as_xapian/tree/master"&gt;Acts_As_Xapian&lt;/a&gt; while trying to find a fix for our Sphinx implementation after a production build.&lt;/p&gt;

&lt;p&gt;Here are the details of our thought process and how we ultimately ended up deploying with Xapian.&lt;/p&gt;

&lt;p&gt;While looking for a full-text search engine when &lt;a href="http://localhost:3000/www.mindbites.com"&gt;Mindbites&lt;/a&gt; was released a year ago, we were looking for something easy and quick.  We ended up going with Douglas Shearer’s &lt;a href="http://douglasfshearer.com/blog/rails-plugin-acts_as_indexed"&gt;Acts_As_Indexed&lt;/a&gt; which worked out great.  It was written entirely in Ruby and very easy to implement with automatic indexing.  (ie No cron jobs needed to keep the index up to date.)  If you have a simple site and want to implement a basic search very quickly, definitely give Acts_As_Indexed a look.&lt;/p&gt;

&lt;p&gt;However, over the past month or so we decided it was time to find something a little more robust.  We wanted the features that the full-blown full-text search engines give such as spell correction, stemming (ie “connection / connecting / connected” would all search for words containing “connect”), finding “similar” results, and the ability to work across multiple servers.  We were also having a few index corruptions with Acts_As_Indexed that we were wanting to get away from with another tool.&lt;/p&gt;

&lt;p&gt;We went through the usual suspects that are often seen in the Rails Community:&lt;/p&gt;

&lt;h3&gt;
&lt;a href="http://lucene.apache.org/solr"&gt;Solr&lt;/a&gt; / &lt;a href="http://acts-as-solr.rubyforge.org/"&gt;Acts_As_Solr&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Very robust search server based on the &lt;a href="http://lucene.apache.org/java/docs/index.html"&gt;Lucene Java&lt;/a&gt; library with a mature acts_as_solr Rails plugin.  I was blown away by Erik Hatcher’s “Solr On Rails” talk at RailsConf 2007 and thought this could be a good fit.  However,  we decided to check out other options because, all things being equal, we would rather not deal with installing Java on our servers.&lt;/p&gt;

&lt;h3&gt;
&lt;a href="http://ferret.davebalmain.com/trac/"&gt;Ferret&lt;/a&gt; / &lt;a href="http://projects.jkraemer.net/acts_as_ferret/"&gt;Acts_As_Ferret&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We nixed Ferret fairly quickly after hearing a few horror stories about corrupted indexes among other issues with Ferret on production servers.&lt;/p&gt;

&lt;h3&gt;
&lt;a href="http://www.sphinxsearch.com/"&gt;Sphinx&lt;/a&gt; / &lt;a href="http://github.com/fauna/ultrasphinx/tree/master"&gt;Ultrasphinx&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Sphinx appears to be the new defacto standard for full text search among Rails developers. From what we read, it is very powerful, very fast indexing, and easy to use with Ultrasphinx.  This was our choice to replace Acts_As_Indexed.  However, after about a week’s worth of development and a deployment to our production server, there still were a lot of mysteries to the Sphinx.&lt;/p&gt;

&lt;p&gt;We were not thrilled with all of the config files involved.  With Ultrasphinx, you will need a xxx.base file which is accessed via a rake task to generate a config file that Sphinx can use.  This works, but I was hoping for something a little bit simpler.&lt;/p&gt;

&lt;p&gt;We were also not big fans of the daemons that run in the background.  In our &lt;span class="caps"&gt;ITG&lt;/span&gt; environment, the daemons decided to stop running on a couple of occasions which caused a 500 error to be thrown when searching.  (We later fixed this issue by reindexing and restarting the daemon with each Capistrano deployment.)  Lastly, I read this quote from the &lt;a href="http://blog.evanweaver.com/files/doc/fauna/ultrasphinx/files/DEPLOYMENT_NOTES.html"&gt;Ultrasphinx deployment notes&lt;/a&gt; about recommended cron jobs for Sphinx on your production server:&lt;/p&gt;

&lt;p&gt;“The first line reindexes the delta index every 10 minutes. The second line reindexes the main index once a day at 4am. &lt;em&gt;&lt;strong&gt;The third line will try to restart the search daemon every three minutes.&lt;/strong&gt;&lt;/em&gt; If it is already running, nothing happens.”&lt;/p&gt;

&lt;p&gt;Kicking off a job every 3 minutes just to make sure another job is running did not seem right to me.&lt;/p&gt;

&lt;h3&gt;
&lt;a href="http://localhost:3000/www.xapian.org"&gt;Xapian&lt;/a&gt; / &lt;a href="http://github.com/frabcus/acts_as_xapian/tree/master"&gt;Acts_As_Xapian&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I had never heard of Xapian before this past Thursday.  I happened to stumble across &lt;a href="http://blog.evanweaver.com/articles/2008/05/26/xapian-search-plugin/"&gt;this article on on Evan Weaver’s blog&lt;/a&gt; while I was researching a Sphinx issue with our production build.  Always looking for new and better ways to do things, I took a closer look at Xapian.  Within 15 minutes, I had Xapian installed on my local Ubuntu machine and was successfully searching Mindbites lessons using the acts_as_xapian plugin.  I spent the rest of the weekend replacing our Sphinx code with Xapian.&lt;/p&gt;

&lt;p&gt;Installation, configuration, and deployment all went so well over the weekend that we deployed our completely revamped search code to our &lt;a href="http://localhost:3000/www.mindbites.com"&gt;Mindbites&lt;/a&gt; production server this past Monday with spelling corrections, stemming, and “You may also like” functionality intact.&lt;/p&gt;

&lt;p&gt;As a side note, that same &lt;a href="http://blog.evanweaver.com/articles/2008/05/26/xapian-search-plugin/"&gt;Evan Weaver blog post&lt;/a&gt; has a comment about a plugin called &lt;a href="http://github.com/grosser/acts_as_searchable/tree/master"&gt;act_as_searchable&lt;/a&gt; using the &lt;a href="http://hyperestraier.sourceforge.net/"&gt;Hyper Estraier&lt;/a&gt; full-text search system.  I have not looked into this solution, but I would be curious to hear from other readers who have tried this.&lt;/p&gt;

&lt;p&gt;In part 2 of this blog post, I will go into detail about our Rails Xapian implementation.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=G4VZaGXaVEU:d3jG3VnRgVw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=G4VZaGXaVEU:d3jG3VnRgVw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=G4VZaGXaVEU:d3jG3VnRgVw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=G4VZaGXaVEU:d3jG3VnRgVw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=G4VZaGXaVEU:d3jG3VnRgVw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/G4VZaGXaVEU/109284085</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109284085</guid><pubDate>Wed, 16 Jul 2008 00:00:00 -0400</pubDate><category>xapian</category><category>search</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109284085</feedburner:origLink></item><item><title>It's Double Coupon Days! Generate and Redeem Coupons With the acts_as_redeemable Rails Plugin</title><description>&lt;h1&gt;&lt;/h1&gt;

&lt;p&gt;Plugins are an excellent way to share functionality across your Rails applications. When we needed the same coupon and invite functionality across multiple apps, we decided to create &lt;a href="http://github.com/squeejee/acts_as_redeemable/tree/master"&gt;acts_as_redeemable&lt;/a&gt;. Hopefully you’ll find it useful, too!&lt;/p&gt;

&lt;h2&gt;What is it?&lt;/h2&gt;

&lt;p&gt;acts_as_redeemable adds redemption capability to a model for items like coupons, invitation codes, etc. Each redeemable gets a unique code upon creation that can be sent in an email or printed as a coupon code.&lt;/p&gt;

&lt;h2&gt;Installing&lt;/h2&gt;

&lt;p&gt;You can install the plugin from Github:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/plugin install git://github.com/squeejee/acts_as_redeemable.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;or if you’re on an older version of Rails, you can &lt;a href="http://github.com/squeejee/acts_as_redeemable/tarball/master"&gt;download the tarball&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;How do I use it?&lt;/h2&gt;

&lt;p&gt;If you don’t have a model created already, go ahead and generate one:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/generate redeemable Coupon
rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt; Make your ActiveRecord model act as redeemable&lt;/h3&gt;

&lt;p&gt;class Coupon &gt; ActiveRecord::Base
    acts_as_redeemable :valid_for =&gt; 30.days, :code_length =&gt; 8 # optional expiration, code length
  end&lt;/p&gt;

&lt;h3&gt;Create a new instance&lt;/h3&gt;

&lt;p&gt;Now that you’ve created and configured your model, it’s time to create your coupon.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;c = Coupon.new
c.user_id = 1 # The user who created the coupon 
c.save
c.code 
# "4D9110A3" 
c.created_at  
# Fri Feb 15 14:56:37 -0600 2008
c.expires_on 
# Fri Mar 16 14:56:37 -0600 2008 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course, you’ll have other business logic for your coupon, discount, invite or other redeemable item. We just spot you the generation, redeeming, and expiration functionality. If you’re sending coupons via email, be sure and check out Slantwise Design’s excellent &lt;a href="http://slantwisedesign.com/rdoc/acts_as_invitation/"&gt;acts_as_invitation plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Copyright© 2008 Squeejee, released under the &lt;span class="caps"&gt;MIT&lt;/span&gt; license&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=VK4IUOHjAR0:QBH0YgUBMhs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=VK4IUOHjAR0:QBH0YgUBMhs:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=VK4IUOHjAR0:QBH0YgUBMhs:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=VK4IUOHjAR0:QBH0YgUBMhs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=VK4IUOHjAR0:QBH0YgUBMhs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/VK4IUOHjAR0/109264824</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109264824</guid><pubDate>Thu, 10 Jul 2008 00:00:00 -0400</pubDate><category>plugins</category><category>rails</category><category>open source</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109264824</feedburner:origLink></item><item><title>Easily Switch Between Rails Development Sites With Phusion Passenger</title><description>&lt;p&gt;&lt;del&gt;mod_rails&lt;/del&gt; &lt;a href="http://www.modrails.com/"&gt;Phusion Passenger&lt;/a&gt; is gaining some steam as it makes deploying Rails apps easier. Passenger also makes it easier to switch between your Rails projects during development.&lt;/p&gt;

&lt;h3&gt;Multi-tasking, Rails style&lt;/h3&gt;

&lt;p&gt;If you’re like me, you often find yourself flipping between two or three different Rails projects. Each time you flip, you do the same little dance in Terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;wynn$ cd projects/blog 
wynn$ mate . 
wynn$ script/server
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You code along happily on the first project, knock out a couple of bugs, and then it’s time to switch. Either you stop your first mongrel server and dance the terminal jig all over again, or you just &lt;code&gt;cmd+T&lt;/code&gt; a new terminal tab and crank up project two on a different port, like :3001 (and remember to specify the new port when you crank up your browser).&lt;/p&gt;

&lt;h3&gt;Phusion Passenger, not just for your production boxes&lt;/h3&gt;

&lt;p&gt;Passenger eliminates the need to start up your own mongrel server for each of your development Rails apps. Just like your &lt;span class="caps"&gt;PHP&lt;/span&gt; days, you simply set up an Apache virtual host for each app, and Passenger does the rest.&lt;/p&gt;

&lt;h3&gt;Step 1: Set up your host names&lt;/h3&gt;

&lt;p&gt;To set up your host aliases for each domain you want to use, edit your &lt;code&gt;/etc/hosts&lt;/code&gt; file. Here’s mine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;127.0.0.1 beta.mindbites.local
127.0.0.1 mindbites.local
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Step 2: Install Phusion Passenger&lt;/h3&gt;

&lt;p&gt;Assuming you have Apache2 installed (bundled with Leopard, an &lt;code&gt;apt-get install&lt;/code&gt; away on Ubuntu):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo gem install passenger 
sudo passenger-install-apache2-module
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The install script will chug away for a few minutes. Once on the other side, you’re only a couple steps away from multi-tasking bliss.&lt;/p&gt;

&lt;h3&gt;Step 3: Set up your virtual host&lt;/h3&gt;

&lt;p&gt;On &lt;span class="caps"&gt;OSX&lt;/span&gt;, you’ll need to use &lt;a href="http://headdress.twinsparc.com/"&gt;Headdress&lt;/a&gt; or modify &lt;code&gt;/etc/apache2/httpd.conf&lt;/code&gt; with your virtual hosts:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;NameVirtualHost *  
&lt;VirtualHost *:80&gt; 
        DocumentRoot /Users/wynn/projects/mindbites/public 
    ServerName mindbites.local 
    ServerAlias beta.mindbites.local 
    RailsEnv development 
    &lt;directory "/Users/wynn/projects/mindbites/public"&gt;    
        Options FollowSymLinks    
        AllowOverride None    
        Order allow,deny    
        Allow from all 
    &lt;/directory&gt; 
&lt;/virtualhost&gt;  
&lt;VirtualHost *:80&gt;
    DocumentRoot /Users/wynn/projects/blog/public 
    ServerName locomotivation.local 
    RailsEnv development 
    &lt;directory "/Users/wynn/projects/blog/public"&gt;
        Options FollowSymLinks    
        AllowOverride None
        Order allow,deny
        Allow from all
     &lt;/directory&gt; 
&lt;/virtualhost&gt;  
&lt;VirtualHost *:80&gt;
    DocumentRoot /Users/wynn/projects/goalistics/public 
    ServerName goalistics.local 
    RailsEnv development 
    &lt;directory "/Users/wynn/projects/goalistics/public"&gt;
        Options FollowSymLinks
        AllowOverride None
        Order allow,deny 
        Allow from all
  &lt;/directory&gt; 
&lt;/virtualhost&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After initially setting up my virtuals, I discovered that only the first one worked. A &lt;a href="http://alexking.org/blog/2007/11/01/apache-2-only-serves-first-virtual-host"&gt;quick look on Google&lt;/a&gt; pointed me to the &lt;code&gt;NameVirtualHost *&lt;/code&gt; directive which solved the problem.  Note that the &lt;code&gt;DocumentRoot /Users/wynn/projects/goalistics/public&lt;/code&gt; directive points to the &lt;code&gt;public&lt;/code&gt; folder within your Rails app, not the root. Second, you’ll need to include the directory overrides in the &lt;code&gt;Directory&lt;/code&gt; block to allow Apache to serve up your static content. (I was shocked when my app was served up &lt;a href="http://naked.dustindiaz.com/"&gt;naked&lt;/a&gt; the first time).&lt;/p&gt;

&lt;p&gt;Also note that the &lt;code&gt;RailsEnv&lt;/code&gt; directive determines the &lt;code&gt;RAILS_ENV&lt;/code&gt; for the application and is &lt;code&gt;production&lt;/code&gt; by default.&lt;/p&gt;

&lt;h3&gt;Wrap-up&lt;/h3&gt;

&lt;p&gt;I’ve been really pleased with Phusion Passenger so far. I discovered one hiccup, though. Passenger does not seem to care for inline &lt;span class="caps"&gt;ERB&lt;/span&gt; code in your database.yml so I had to side step some dynamic configuration we were doing. Error pages from Phusion are much nicer on the eyes though than the standard Rails versions. Also, restarting Rails applications are as easy as bouncing Apache or:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo touch tmp/restart.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;from the project root.  Passenger is definitely worth a look on your development machine.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=JYg1Zhj16ao:YhnwzMJYNd8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=JYg1Zhj16ao:YhnwzMJYNd8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=JYg1Zhj16ao:YhnwzMJYNd8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=JYg1Zhj16ao:YhnwzMJYNd8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=JYg1Zhj16ao:YhnwzMJYNd8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/JYg1Zhj16ao/109266219</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109266219</guid><pubDate>Tue, 01 Jul 2008 00:00:00 -0400</pubDate><category>phusion passenger</category><category>apache</category><category>rails</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109266219</feedburner:origLink></item><item><title>Redesign Your Site in Place Using Rails Custom Mime Types</title><description>&lt;p&gt;After reading Ben Smith’s classic &lt;a href="http://www.slashdotdash.net/articles/2007/12/04/iphone-on-rails-creating-an-iphone-optimised-version-of-your-rails-site-using-iui-and-rails-2"&gt;iPhone on Rails 2 article&lt;/a&gt; and implementing our own &lt;a href="http://blog.mindbites.com/techy/five-things-we-learned-building-our-iphone-site/"&gt;iPhone site&lt;/a&gt; after the jump to Rails 2, I was struck at how easy it is to create you own custom mime types to serve up different content to different devices.&lt;/p&gt;

&lt;p&gt;When the time came to redesign the main &lt;a href="http://www.mindbites.com/"&gt;MindBites&lt;/a&gt; site, I didn’t look forward to a lot of branching and merging, no matter how easy Git makes it. Then it hit me. Why not create a custom mime type and serve up a new layout and views for beta users? Let’s take a look at how we’re doing just that.&lt;/p&gt;

&lt;h3&gt;Step 1: Register your custom mime type&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is tell Rails we’ve got a new format type to serve up:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# config/environment.rb

Mime::Type.register_alias "text/html", :iphone
Mime::Type.register_alias "text/html", :beta
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We used &lt;code&gt;beta&lt;/code&gt;, but you could use any value here not already in use.&lt;/p&gt;

&lt;h3&gt;Step 2: Detect a beta request&lt;/h3&gt;

&lt;p&gt;In order to let only the cool folks into our new beta site, we’ll need a big burly bouncer at the door keeping out the riff-raff:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# application.rb
before_filter :adjust_format_for_beta

...

def adjust_format_for_beta
  request.format = :beta if beta_request? 
end

def beta_request?
  return (request.subdomains.first == "beta" || params[:format] == "beta")
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;before_filter&lt;/code&gt; executes before each request and if ‘beta’ is the first subdomain or passed in as a &lt;code&gt;format&lt;/code&gt; param, Rails will set the format to &lt;code&gt;:beta&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Step 3: We’re in da club, now what?&lt;/h3&gt;

&lt;p&gt;Once we’ve identified our beta users, we now need to give them the new eye candy. We do this simply by dropping in a new view named &lt;code&gt;xxxxxx.beta.erb&lt;/code&gt;. The bulk of the changes for our redesign are in a new application layout which serves up a new basic layout and &lt;span class="caps"&gt;CSS&lt;/span&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;application.html.erb&lt;/strong&gt; → &lt;strong&gt;application.beta.rb&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Other views (even partials) are swapped out in a similar manner:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;show.html.erb&lt;/strong&gt; → &lt;strong&gt;show.beta.erb&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_lesson.html.erb&lt;/strong&gt; → &lt;strong&gt;_lesson.beta.erb&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;And that’s it! You don’t have to do every view in the site because Rails will fall back to &lt;strong&gt;.html.erb&lt;/strong&gt; for any views without a &lt;strong&gt;.beta.erb&lt;/strong&gt; version. Most of our redesign is accomplished in &lt;span class="caps"&gt;CSS&lt;/span&gt;. Where we have massive markup changes, we simply swap out the view templates.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;See update 2 below&lt;/p&gt;

&lt;h4&gt;A gotcha to watch out for&lt;/h4&gt;

&lt;p&gt;In a few cases, we had &lt;code&gt;respond_to&lt;/code&gt; blocks in our controllers to handle differences between the iPhone and regular versions of the site. Anywhere that you have these, you’ll need to add the beta mime type as well:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;respond_to do |format|
  format.html
  format.beta # &amp;lt;= You'll get a 406 Unprocessable entity error without this line
  format.iphone do 
    render :layout =&amp;gt; false if request.xhr?
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If an action does not already have a &lt;code&gt;respond_to&lt;/code&gt; block, you’re OK. No changes are required in your controller.&lt;/p&gt;

&lt;h4&gt;&lt;span class="caps"&gt;UPDATE&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;Thanks, Ryan Heneise, for reminding me about the last step:&lt;/p&gt;

&lt;h4&gt;Step 4: What is new is old again&lt;/h4&gt;

&lt;p&gt;When you’re ready to launch, you’ll want to fold all those beta views back into your project as the default &lt;code&gt;.html.erb&lt;/code&gt; views.  Simply rename them from &lt;code&gt;.beta.erb&lt;/code&gt; to &lt;code&gt;.html.erb&lt;/code&gt;. For us, it’s a bit easier since we don’t have to overwrite any &lt;code&gt;.html.erb&lt;/code&gt; files. We’re still using &lt;code&gt;.rhtml&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Thanks, Ryan!&lt;/p&gt;

&lt;h4&gt;&lt;span class="caps"&gt;UPDATE 2&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;Paul Canavese turns in some excellent QA work and informs me that Rails will not fall back to &lt;code&gt;.html.erb&lt;/code&gt;. Our templates are still &lt;code&gt;.rhtml&lt;/code&gt;. So it would appear you will have to touch each template or use .rhtml. Sorry!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=KfVco5wRIjs:T_ztQCEfA8A:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=KfVco5wRIjs:T_ztQCEfA8A:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=KfVco5wRIjs:T_ztQCEfA8A:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=KfVco5wRIjs:T_ztQCEfA8A:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=KfVco5wRIjs:T_ztQCEfA8A:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/KfVco5wRIjs/109268258</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109268258</guid><pubDate>Sun, 29 Jun 2008 00:00:00 -0400</pubDate><category>rails</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109268258</feedburner:origLink></item><item><title>jQuery Parallax Scrolling - Build Your Own 1980's Video Game!</title><description>&lt;p&gt;What is old is new again. Inspired by the recent crop of &lt;a href="http://weallhatequickbooks.com/"&gt;css parallax&lt;/a&gt; techniques. I thought it would be cool to take it one step further and introduce &lt;a href="http://en.wikipedia.org/wiki/Parallax_scrolling"&gt;parallax scrolling&lt;/a&gt;. Thus the &lt;a href="http://plugins.jquery.com/project/jquery-parallax"&gt;jQuery Parallax plugin&lt;/a&gt; was born.&lt;/p&gt;

&lt;h3&gt;What the heck is a &lt;em&gt;parallax&lt;/em&gt; anyway?&lt;/h3&gt;

&lt;p&gt;Paul Annett of &lt;a href="http://www.clearleft.com/"&gt;Clearleft&lt;/a&gt; does a far better job of &lt;a href="http://www.thinkvitamin.com/features/design/how-to-recreate-silverbacks-parallax"&gt;explaining the css parallax technique&lt;/a&gt; than I could. If you’re unfamiliar with this layer-stacking technique, jog over to &lt;a href="http://www.thinkvitamin.com/"&gt;Vitamin&lt;/a&gt; and get the scoop.&lt;/p&gt;

&lt;h3&gt;That’s pretty cool, but why do I have to resize my browser to enjoy it?&lt;/h3&gt;

&lt;p&gt;For no practical reason whatsoever, I thought it would be cool to animate the layers in a &lt;span class="caps"&gt;CSS&lt;/span&gt; parallax and give the scene a depth of field that you can’t convey with static layers. As with all my &lt;a href="http://www.jquery.com/"&gt;jQuery&lt;/a&gt; endeavors so far, I found this surprisingly easier than I thought.&lt;/p&gt;

&lt;h4&gt;Step 1: Build the layered scene&lt;/h4&gt;

&lt;p&gt;Just in case you’re reading this and were served one of the other parallax scenes on the site, here’s a look at the finished product.&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both;"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/27640669@N02/2592978614/" title="Locomotivation.com header by squeejee, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3039/2592978614_331f799cc3_o.png" height="144" alt="Locomotivation.com header" width="800"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both;"/&gt;&lt;/p&gt;

&lt;p&gt;For our simple western landscape, we’ll construct the scene from four layers:&lt;/p&gt;

&lt;div style="margin-left: 30px;"&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;poles.png&lt;/strong&gt; Through the magic of &lt;code&gt;repeat-x&lt;/code&gt;, a single telephone pole with power lines becomes a never-ending array of energy delivery&lt;/li&gt;
  &lt;li&gt;
&lt;strong&gt;logo.png&lt;/strong&gt; Adding the logo to the scene creates an even greater illusion of depth. The slight gradient also conveys depth as items further in the distance have more light&lt;/li&gt;
  &lt;li&gt;
&lt;strong&gt;windmill.png&lt;/strong&gt; A lone windmill is set off in the distance&lt;/li&gt;
  &lt;li&gt;
&lt;strong&gt;mountains.png&lt;/strong&gt; Just like our telephone pole, this mountain range goes to the end of the earth&lt;/li&gt;
  &lt;li&gt;
&lt;strong&gt;sky.png&lt;/strong&gt; Finally, that pretty blue sky is a tiny image only one pixel wide, repeating on the x-axis&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br style="clear: both;"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/27640669@N02/2592113063/" title="CSS Parallax diagram by squeejee, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3092/2592113063_1c1c36b026_o.png" height="419" alt="CSS Parallax diagram" width="820"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both;"/&gt;&lt;/p&gt;

&lt;h4&gt;Step 2: Add the required Javascript references&lt;/h4&gt;

&lt;p&gt;To animate our parallax we’ll use my &lt;a href="http://plugins.jquery.com/project/jquery-parallax"&gt;jQuery Parallax plugin&lt;/a&gt; which requires &lt;a href="http://www.jquery.com/"&gt;jQuery 1.2.x&lt;/a&gt; and the &lt;a href="http://plugins.jquery.com/project/Easing"&gt;jQuery Easing plugin&lt;/a&gt; (for acceleration and deceleration).&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both;"/&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;script src="/javascripts/theme/jquery-1.2.6.min.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="/javascripts/theme/jquery.easing.1.3.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="/javascripts/theme/jquery.parallax.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br style="clear: both;"/&gt;&lt;/p&gt;

&lt;h4&gt;Step 3: Wire up the parallaxes (what a fun word to say!)&lt;/h4&gt;

&lt;p&gt;Now it’s time to attach our parallax behavior to the container and each slice in our scene.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/27640669@N02/2592113077/" title="jQuery Parallax diagram by squeejee, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3139/2592113077_0d57abce9c_o.png" height="419" alt="jQuery Parallax diagram" width="820"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br style="clear: both;"/&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;jQuery(document).ready(function() {

    jQuery("#header").parallax("create");
    jQuery(".scene-1 .slice-1").parallaxSlice("create", {speed: 5});
    jQuery(".scene-1 .slice-2").parallaxSlice("create", {speed: 3});
    jQuery(".scene-1 .slice-3").parallaxSlice("create", {speed: 1});

});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br style="clear: both;"/&gt;&lt;/p&gt;

&lt;h4&gt;Step 4: Sit back and enjoy the show!&lt;/h4&gt;

&lt;p&gt;Again, this little demo has very little practical value. Maybe I should do something more constructive with it like, recreate the first level of &lt;a href="http://en.wikipedia.org/wiki/Contra_%28series%29"&gt;Contra&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=QeFysFrpkic:8afo0m26cRY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=QeFysFrpkic:8afo0m26cRY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=QeFysFrpkic:8afo0m26cRY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=QeFysFrpkic:8afo0m26cRY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=QeFysFrpkic:8afo0m26cRY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/QeFysFrpkic/109269802</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109269802</guid><pubDate>Wed, 18 Jun 2008 00:00:00 -0400</pubDate><category>jquery</category><category>javascript</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109269802</feedburner:origLink></item><item><title>ImageMagick OS X Installer</title><description>&lt;p&gt;Installing ImageMagick / RMagick on OS X does not have to be a hastle.  There is a very handy install script on RubyForge that does all of the work for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://rubyforge.org/projects/rmagick/"&gt;&lt;a href="http://rubyforge.org/projects/rmagick/"&gt;http://rubyforge.org/projects/rmagick/&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just be sure you have the following prereqs already installed on your Mac:&lt;br/&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XCode Tools&lt;/li&gt;
&lt;li&gt;X11 SDK&lt;/li&gt;
&lt;li&gt;X11.app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, even though the script is supposed to include RMagick Ruby library, that piece did not work for me.  Luckily, a simple “sudo gem install rmagick” worked like a champ for me.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=Vt0FT5ya95g:eaZbHEjHqgo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=Vt0FT5ya95g:eaZbHEjHqgo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=Vt0FT5ya95g:eaZbHEjHqgo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=Vt0FT5ya95g:eaZbHEjHqgo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=Vt0FT5ya95g:eaZbHEjHqgo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/Vt0FT5ya95g/109283417</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109283417</guid><pubDate>Thu, 20 Mar 2008 00:00:00 -0400</pubDate><category>os x</category><category>image magick</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109283417</feedburner:origLink></item><item><title>Syntax Highlighting Blog Posts</title><description>&lt;p&gt;After setting up this blog on the amazing new &lt;a href="http://www.expressionengine.com/" title="Expression Engine 1.6"&gt;Expression Engine 1.6&lt;/a&gt;, we needed a way to share code snippets on blog posts. I found a number of utilities that will preformat your code, injecting markup around keywords that you can then style to taste. However, most of these solutions were language specific and cumbersome. One of the best tools I found was &lt;a href="http://pastie.caboo.se/usage"&gt;Pastie&lt;/a&gt;. With its built-in &lt;a href="http://pastie.caboo.se/paste/579"&gt;API&lt;/a&gt;, it would have been a nice fit had I been running a &lt;a href="http://mephistoblog.com/"&gt;Rails-based&lt;/a&gt; CMS.&lt;/p&gt;

&lt;p&gt;
Most of the tools I found were Wordpress plugins which doesn’t help out an EE fan like me. I did find a great &lt;a href="http://expressionengine.com/downloads/details/pretty_php/"&gt;EE plugin for PHP code&lt;/a&gt;, but we tend to sling much more Ruby (and Rails), SQL, HTML, Javascript, and CSS on this blog. 
&lt;/p&gt;

&lt;p&gt;Perhaps we were asking too much but we simply wanted to format our code within an entry with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No special markup required&lt;/li&gt;
&lt;li&gt;Multi programming language support (including the ones I haven’t learned yet)&lt;/li&gt;
&lt;li&gt;CSS based styling&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The solution&lt;/h2&gt;



&lt;p&gt;I remember liking how Javascript Fu Master &lt;a href="http://www.danwebb.net/"&gt;Dan Webb&lt;/a&gt; formatted code within posts on his blog and was pleased to find a link to his &lt;a href="http://projects.danwebb.net/wiki/CodeHighlighter"&gt;CodeHighlighter&lt;/a&gt; project on his site. CodeHighlighter is a 100% client-side solution, using Javascript to perform configurable Regular Expression matches to inject CSS classes around keywords, constants, and other language constructs. Supported languages include Ruby, HTML, CSS, and Javascript, however you can add support for your favorite language in literally a few minutes. CodeHighlighter doesn’t offer SQL highlighting out of the box, so that’s exactly what we did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step one: set up the scripts&lt;/strong&gt; Simply copy the CodeHighlighter.js and other language specific scripts to your server and add the script tags to your page:&lt;/p&gt;

&lt;p&gt;&lt;script type="text/javascript" src="/js/code_highlighter.js"&gt;&lt;/script&gt;&lt;script type="text/javascript" src="/js/ruby.js"&gt;&lt;/script&gt;&lt;script type="text/javascript" src="/js/html.js"&gt;&lt;/script&gt;&lt;script type="text/javascript" src="/js/javascript.js"&gt;&lt;/script&gt;&lt;script type="text/javascript" src="/js/css.js"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step two: insert your code block&lt;/strong&gt; Next, add your code block to your post:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;pre&gt;&lt;code class="html"&gt;
  your code here
&lt;/code&gt;&lt;/pre&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note the CSS class on the &lt;strong&gt;code&lt;/strong&gt; element. This determines the language formatting that CodeHighlighter should apply to your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step three: set up your styles&lt;/strong&gt; The last step to syntax highlighting bliss is to add language specific CSS rules to your stylesheet:&lt;/p&gt;

&lt;p&gt;.javascript .comment, .ruby .comment, .sql .comment {
    color : #9c6;
    font-style: italic;
  }&lt;/p&gt;

&lt;p&gt;.javascript .string, .ruby .string, .sql .string {
    color : #9c6;
  }&lt;/p&gt;

&lt;p&gt;.javascript .keywords, .ruby .keywords, .sql .keywords, sql-keyword {
    color : #679ef1;
  }&lt;/p&gt;

&lt;p&gt;.javascript .global {
    color : blue;
  }&lt;/p&gt;

&lt;p&gt;.javascript .brackets, .ruby .brackets {
    color : red;
  }&lt;/p&gt;

&lt;p&gt;.css .comment {
    color : gray;
  }&lt;/p&gt;

&lt;p&gt;.css .properties {
    color : navy;
  }&lt;/p&gt;

&lt;p&gt;.css .selectors {
    color : maroon;
    font-weight : bold;
  }&lt;/p&gt;

&lt;p&gt;.css .units {
    color :red;
  }&lt;/p&gt;

&lt;p&gt;.css .urls {
    color :green;
  }&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;h2&gt;Adding support for SQL (well actually MySql)&lt;/h2&gt;

&lt;p&gt;To support SQL, I simply cloned the Javascript rules (javascript.js) as sql.js and edited the Regular Expression rules as necessary.&lt;/p&gt;

&lt;p&gt;CodeHighlighter.addStyle(“sql”,{
   comment : {
   exp  : /(—[^n]&lt;em&gt;(n|$))|(/&lt;/em&gt;[^&lt;em&gt;]&lt;/em&gt;*+([^/][^&lt;em&gt;]&lt;/em&gt;*+)&lt;em&gt;/)/
   },
   string : {
   exp  : /’[^’]&lt;/em&gt;’|”[^”]*”/
   },
   keywords : {
   exp  : /b(ADD|ALL|ALTER|ANALYZE|AND|AS|ASC|ASENSITIVE|BEFORE|&lt;snip&gt;)b/
   },
   global : {
   exp  : /b(NULL)b/
   }
  });&lt;/snip&gt;&lt;/p&gt;

&lt;p&gt;All I had to do was copy this file to the server and &lt;a href="http://www.locomotivation.com/comments/mysql_oracle_rownum10/"&gt;Jim’s first MySql post&lt;/a&gt; just got a bit more purty.&lt;/p&gt;

&lt;h2&gt;Drawbacks&lt;/h2&gt;



&lt;p&gt;While CodeHighlighter is a super easy, &lt;a href="http://www.ujs4rails.com/"&gt;unobtrusive&lt;/a&gt; way to provide syntax highlighting in a server agnostic way, it’s client-side nature does mean you may see a slight delay before your code becomes formatted as the page loads before the script initiates. Also, as you can see with my SQL example, the rules files may get quite large, 8k in our case. If any RegEx gurus are reading, let me know if you can match a set of words in upper, lower, and mixed case for case insensitive languages like SQL. That would cut the sql.js rules file in half!
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=tCoU-YAFnEM:d5ICRQTRar8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=tCoU-YAFnEM:d5ICRQTRar8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=tCoU-YAFnEM:d5ICRQTRar8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/locomotivation?a=tCoU-YAFnEM:d5ICRQTRar8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/locomotivation?i=tCoU-YAFnEM:d5ICRQTRar8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/locomotivation/~3/tCoU-YAFnEM/109272701</link><guid isPermaLink="false">http://locomotivation.squeejee.com/post/109272701</guid><pubDate>Sat, 30 Jun 2007 00:00:00 -0400</pubDate><category>javascript</category><category>css</category><feedburner:origLink>http://locomotivation.squeejee.com/post/109272701</feedburner:origLink></item></channel></rss>
