<?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:dc="http://purl.org/dc/elements/1.1/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>Elevated Code RSS Feed</title>
    <link>http://www.elevatedrails.com/rss/</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Elevated Code, providing custom software development.</description>
    
    
        <geo:lat>41.990631</geo:lat><geo:long>-87.666705</geo:long><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/ElevatedRailsRssFeed" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
          <title>Speeding up delayed job</title>
          <description>&lt;p&gt;&lt;a href="http://github.com/tobi/delayed_job"&gt;Delayed Job&lt;/a&gt; is a fantastic plugin for moving work out of the request flow. Unfortunately, it has a few performance problems when running with large numbers of jobs on MySQL. With minor changes, we can remove a large amount of the overhead.&lt;/p&gt; &lt;p&gt;
I noticed the performance problem on one of our applications when MySQL was taking large amounts of CPU. The application had been in production for about a year and had been performing well. I had just recently moved image resizing out of the main workflow and into delayed job. When I went to look at the &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/slow-query-log.html"&gt;slow query log&lt;/a&gt;, I saw this being run frequently:
&lt;/p&gt;
&lt;code class="code"&gt;
&lt;pre class="code"&gt;
# Query_time: 0  Lock_time: 0  Rows_sent: 5  Rows_examined: 67604
SELECT * FROM `delayed_jobs` WHERE ((run_at &lt;= '2009-11-04 06:58:49' AND (locked_at IS NULL OR locked_at &lt; '2009-11-04 02:58:49') OR (locked_by = 'host:example.com pid:17731')) AND failed_at IS NULL)  ORDER BY priority DESC, run_at ASC LIMIT 5;
&lt;/pre&gt;
&lt;/code&gt;
&lt;p&gt;
When reading the output of the slow query log, I look mainly at two things. The first is the query time. In this case, the query was being run in less than a second. The second thing I look at is the ratio of the number of rows examined to the number of rows sent back to the app. In this case, the database had to look at 67,604 rows just to give the app 5 rows back. When you see full table scans like this, it is usually a sign of a missing index. I looked at the delayed jobs table and saw that there was only an index on ID. That means the DB was reading every row in the table for each query.
&lt;/p&gt;
&lt;p&gt;Once I realized that I was seeing a full table scan, I knew I needed to add an index. Normally, when you add indexes to a table, you're doing it to speed up the row selection performed by the where clause. That isn't a good idea here. Delayed job removes successfully completed records by default, and we have very few updates that fail. This means that the vast majority of rows in the table will match the where clause. When this happens, you typically want to add an index to speed up the sort instead of the where clause.
&lt;/p&gt;
&lt;p&gt;
This query sorts on two fields, &lt;code&gt;priority&lt;/code&gt; and &lt;code&gt;run_at&lt;/code&gt;. We can easily add an index there, but notice the ordering on the sort. It sorts by priority in descending order. Because of the way database indexes work, they are typically only used for ascending order sorts. If you were to create an index on &lt;code&gt;delayed_jobs(priority,run_at)&lt;/code&gt; the database won't be able to use the index. (You can try it out. Add the index and &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/explain.html"&gt;explain&lt;/a&gt; the query.
&lt;/p&gt;
&lt;p&gt;
Some databases, like Oracle, allow you to create an index with a specified ordering. For example, you can create an index on &lt;code&gt;delayed_jobs(priority desc, run_at asc)&lt;/code&gt; which will then allow the query to use the index. While that syntax is supported by MySQL, the ordering is ignored. That means MySQL still won't be able to use the index.
&lt;/p&gt;
&lt;p&gt; So how did I solve the problem? Simple. I changed the semantics of priority. By default, Delayed Job expects a higher value of priority to be more important. I decided to assign lower numbers to mean more important (similar to &lt;a href="http://en.wikipedia.org/wiki/Nice_(Unix)"&gt;Unix nice values&lt;/a&gt;.) Once I changed the way I assigned priorities, I simply changed the query to order by &lt;code&gt;priority ASC, run_at ASC&lt;/code&gt; and added an index on &lt;code&gt;delayed_jobs(priority,run_at)&lt;/code&gt;. Now, when the query is run, the log looks like:
&lt;/p&gt;
&lt;code class="code"&gt;
&lt;pre class="code"&gt;
# Query_time: 0  Lock_time: 0  Rows_sent: 5  Rows_examined: 5
SELECT * FROM `delayed_jobs` WHERE ((run_at &lt;= '2009-11-04 06:58:49' AND (locked_at IS NULL OR locked_at &lt; '2009-11-04 02:58:49') OR (locked_by = 'host:example.com pid:17731')) AND failed_at IS NULL)  ORDER BY priority ASC, run_at ASC LIMIT 5;
&lt;/pre&gt;
&lt;/code&gt;
&lt;p&gt;
That quickly reduced the load on the system and improved application performance.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=YkgOqfCsbuw:wvx3m8s4lOI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=YkgOqfCsbuw:wvx3m8s4lOI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=YkgOqfCsbuw:wvx3m8s4lOI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=YkgOqfCsbuw:wvx3m8s4lOI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/YkgOqfCsbuw" height="1" width="1"/&gt;</description>
          <pubDate>Wed, 04 Nov 2009 06:44:28 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2009/11/04/speeding-up-delayed-job/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/YkgOqfCsbuw/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2009/11/04/speeding-up-delayed-job/</feedburner:origLink></item>
    
        <item>
          <title>Slides from Inman Connect 2009</title>
          <description>&lt;p&gt;As promised, I wanted to provide my slides from &lt;a href="/InmanMangino.pdf"&gt;Inman Connect 2009&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My favorite links for learning about Agile methodologies are:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.agilemanifesto.org"&gt;The Agile Manifesto&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.threeriversinstitute.org/blog/"&gt;Kent Beck&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://alistair.cockburn.us/Characterizing+people+as+non-linear%2c+first-order+components+in+software+development"&gt;Cockburn on people as non-linear, first-order components&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.poppendieck.com/publications.htm"&gt;Lean principals applied to software&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are many more, but those are some of my favorites.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=LMAT2vGrjAA:NepoFtV4jkY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=LMAT2vGrjAA:NepoFtV4jkY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=LMAT2vGrjAA:NepoFtV4jkY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=LMAT2vGrjAA:NepoFtV4jkY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/LMAT2vGrjAA" height="1" width="1"/&gt;</description>
          <pubDate>Tue, 11 Aug 2009 06:42:45 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2009/08/11/slides-from-inman-connect-2009/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/LMAT2vGrjAA/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2009/08/11/slides-from-inman-connect-2009/</feedburner:origLink></item>
    
        <item>
          <title>Standing up to be counted</title>
          <description>&lt;p&gt;Nick Sieger &lt;a href="http://blog.nicksieger.com/articles/2009/04/30/stand-and-be-counted"&gt;asked that people stand and be counted&lt;/a&gt; and I too will join in. I&amp;#8217;m disappointed with many in our community. While I think the presentation that sparked this discussion was in poor taste, it&amp;#8217;s not the presentation that bothers me. I&amp;#8217;m disappointed by how little empathy some people are showing.&lt;/p&gt;
&lt;p&gt;Whether you agree with somebody or not, I think it&amp;#8217;s important to try to see things from their perspective. It may not change your mind, but it will hopefully at least give you a better understanding of how your actions affect others.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=hyrrZjKTxwc:h2-811i28Mw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=hyrrZjKTxwc:h2-811i28Mw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=hyrrZjKTxwc:h2-811i28Mw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=hyrrZjKTxwc:h2-811i28Mw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/hyrrZjKTxwc" height="1" width="1"/&gt;</description>
          <pubDate>Thu, 30 Apr 2009 09:58:57 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2009/04/30/standing-up-to-be-counted/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/hyrrZjKTxwc/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2009/04/30/standing-up-to-be-counted/</feedburner:origLink></item>
    
        <item>
          <title>Speeding up location based searches</title>
          <description>&lt;p&gt;
With the popularity of &lt;a href="http://maps.google.com"&gt;Google Maps&lt;/a&gt;, more and more web applications are becoming location aware. Rails already has some great tools for working with geodata, such as &lt;a href="http://github.com/andre/geokit-gem/tree/master"&gt;GeoKit&lt;/a&gt;. We've been happily using these tools for years. While GeoKit has some basic optimizations, we recently found a case where lookups were taking close to 8 seconds. We needed a way to speed this up. Read on to learn how we did it.
&lt;/p&gt; &lt;p&gt;
 First, let me explain our domain. We are working on a site that shows the 25 nearest amenities to a location. The application is focused on San Francisco and the surrounding areas. We store the amenities in a table with columns for latitude and longitude. All told, we search about 800,000 amenity records.
&lt;/p&gt;
&lt;p&gt;
To find the nearest 25 amenities, our database has to do a lot of work. For each listing in the database, we need to &lt;a href="http://en.wikipedia.org/wiki/Haversine_formula"&gt;calculate the distance&lt;/a&gt; from our origin. Performing that calculation would be unbearably slow, so geokit speeds up the query by including a square bounds. First, the list of locations is limited by a square 5 miles on a side (we use a big window to make sure we get amenities in more rural areas.) Once we've limited  our dataset, the results are sorted by distance. Unfortunately, due to the amenity density in San Francisco, we still have to go through 10,000 records on average.
&lt;/p&gt;
&lt;p&gt;
This calculation and sort is just slow. In version 5.1, MySQL includes some geospatial extensions. These extensions include datatypes for locations and spatial indexes to make spatial searches much faster. Unfortunately, ActiveRecord doesn't support these extensions. That means we'll have to do some things manually.
&lt;/p&gt;
&lt;p&gt;

First, let's look at the three primitives we'll use. The first is a @Point@. Points represent a location in a 2D space. We'll use Points to represent the location of an amenity by it's latitude and longitude coordinates. In MySQL, you can create a point using the SQL: @GeomFromText('Point(38.567 -122.576)')@ 
&lt;/p&gt;
&lt;p&gt;

The second primitive is a @Polygon@. Polygons include a list of coordinates that make up the vertices. Polygons must be closed. For example, we could create the polygon @GeomFromText('Polygon((0 0,0 1,1 1,1 0,0 0))')@. We'll use a Polygon to represent the boundary inside which we want to search.
&lt;/p&gt;
&lt;p&gt;

The final primitive is a @Line@. We use a Line to calculate the distance between two points. According to the OpenGIS specification, we should be able to use the distance function for this. Unfortunately, MySQL doesn't implement Distance.
&lt;/p&gt;
&lt;p&gt;

We'll also need to create a spatial index. A spatial index is an r-tree index that is optimized for fast distance calculations. Currently, only MyISAM tables support spatial indexes. They also require that the column be defined as NOT NULL. We'll have to keep that in mind as we go along.
&lt;/p&gt;
&lt;p&gt;

Let's start by adding a Point column to our amenities table. Because ActiveRecord doesn't support the GIS extensions, we'll need to use the execute method inside our migration. This looks like:
&lt;/p&gt;

&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;alter table amenities add lat_lng_point point not null&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;

Because we want to use Spatial indexes, we'll also need to convert our amenities table to MyISAM. 
&lt;/p&gt;

&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;alter table amenities engine myisam&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;

With that in place, we'll need to convert our existing data to include the @lat_lng_point@ column. From what I can tell, Points can only be created using the GeomFromText method. That means we'll need to generate the text representation of a Point during our conversion. This can be done with the following SQL"
&lt;/p&gt;

&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
&lt;span style="color:#080;font-weight:bold"&gt;update&lt;/span&gt; &lt;span style=""&gt;amenities&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;set&lt;/span&gt; &lt;span style=""&gt;lat_lng_point&lt;/span&gt; = &lt;span style=""&gt;GeomFromText&lt;/span&gt;(&lt;span style=""&gt;concat&lt;/span&gt;(&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;Point(&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span style=""&gt;lat&lt;/span&gt;,&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt; &lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span style=""&gt;lng&lt;/span&gt;,&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;)&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;))
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;

At this point, we now have a table with the location data being stored as a Point. Since all of our records have a Point, we now meet the criteria for adding our spatial index:
&lt;/p&gt;

&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;create spatial index amenities_spatial_idx on amenities(lat_lng_point)&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;

With that in place, we can need a method that does a few thing. First, we need to compute a bounds to limit our distance calculation. We'll do that using @GeoKit::Bounds@. Once we have that, we need to write a query that uses the @MBRWithin@ function (MBR stands for Minimum Bounding Rectangle) to find the list of points inside our Bounds. Once we have that, we can use a Line to get the distance between our origin and each point and order by that. The end result is a little ugly:
&lt;/p&gt;

&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
  
  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#038;font-weight:bold"&gt;self&lt;/span&gt;.near(mappable)
    bound = &lt;span style="color:#036;font-weight:bold"&gt;GeoKit&lt;/span&gt;::&lt;span style="color:#036;font-weight:bold"&gt;Bounds&lt;/span&gt;.from_point_and_radius(mappable,&lt;span style="color:#00D;font-weight:bold"&gt;5&lt;/span&gt;)
    sql = &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;lt;&amp;lt;SQL&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style=""&gt;
    select *,  GLength(LineStringFromWKB(LineString(AsBinary(lat_lng_point), 
    (AsBinary(GeomFromText('Point(&lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;mappable.lat&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt; &lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;mappable.lng&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;)')))))) * 69 as distance
    from amenities where MBRWithin(lat_lng_point,
     GeomFromText('Polygon((&lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.ne.lat&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt; &lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.ne.lng&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;, &lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.ne.lat&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt; &lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.sw.lng&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;,&lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.sw.lat&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt; &lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.sw.lng&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;,&lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.sw.lat&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt; &lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.ne.lng&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;,&lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.ne.lat&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt; &lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;bound.ne.lng&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;))')) 
    order by distance asc limit 25;&lt;/span&gt;&lt;span style="color:#710"&gt;
SQL&lt;/span&gt;&lt;/span&gt;
    find_by_sql(sql)
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;

It's ugly, but it is blazingly fast! Our query went from 8s to 0.01s. That's definitely worth a little code ugliness. 
&lt;/p&gt;
&lt;p&gt;

While we've handled the inital data conversion and querying, we can't add data to our table. We need to provide a Point for each record, and there's no way to do that using ActiveRecord. Instead, we'll need to use a MySQL trigger.
&lt;/p&gt;

&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;lt;&amp;lt;EOT&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style=""&gt;
      create trigger set_lat_lng_point 
      before insert on amenities for each row 
      set  new.lat_lng_point = GeomFromText(concat('Point(',new.lat,' ',new.lng,')'));&lt;/span&gt;&lt;span style="color:#710"&gt;
EOT&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;

That code will create a Point from the lat and lng when a record is inserted. If you need to be able to update locations, you could make the trigger fire on update as well. You'll want to install the &lt;a href="http://artriggers.rubyforge.org/"&gt;Trigger Happy&lt;/a&gt; plugin so that your triggers are dumped and included in your test database.
&lt;/p&gt;
&lt;p&gt;

After all that, can you believe we're still not quite done? Due to a bug in MySQL, inserting a row where @lat_lng_point@ is NULL will give an error. It turns out that MySQL checks the not null columns before triggers are run. Luckily, there is a workaround. If you don't explicitly set the column value to NULL, the trigger will be run before the value is checked. To get ActiveRecord to not explicitly set the column to NULL, we'll use a bit of a hack.
&lt;/p&gt;

&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
  &lt;span style="color:#888"&gt;# Remove lat_lng_point from the list of attributes&lt;/span&gt;
  &lt;span style="color:#888"&gt;# otherwise, AR writes it as NULL which keeps the trigger from firing&lt;/span&gt;
  &lt;span style="color:#888"&gt;# and setting the value correctly  &lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;before_save&lt;/span&gt;
    &lt;span style="color:#33B"&gt;@attributes&lt;/span&gt;.delete(&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style=""&gt;lat_lng_point&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;

That code will remove the column for AR's list of attributes. 
&lt;/p&gt;
&lt;p&gt;

Obviously, this is somewhat complicated. If your searches are running okay using just normal SQL, I wouldn't recommend changing. If you need high performance and are searching large amounts of data, this little bit of code can make a huge difference.
&lt;/p&gt;

&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
&lt;span style="color:#888"&gt;# Our entire migration&lt;/span&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;ConvertAmenitiesToUseSpatials&lt;/span&gt; &amp;lt; &lt;span style="color:#036;font-weight:bold"&gt;ActiveRecord&lt;/span&gt;::&lt;span style="color:#036;font-weight:bold"&gt;Migration&lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#038;font-weight:bold"&gt;self&lt;/span&gt;.up
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;alter table amenities add lat_lng_point point not null&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;alter table amenities engine myisam&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;update amenities set lat_lng_point = GeomFromText(concat(&lt;/span&gt;&lt;span style="color:#b0b"&gt;\'&lt;/span&gt;&lt;span style=""&gt;Point(&lt;/span&gt;&lt;span style="color:#b0b"&gt;\'&lt;/span&gt;&lt;span style=""&gt;,lat,&lt;/span&gt;&lt;span style="color:#b0b"&gt;\'&lt;/span&gt;&lt;span style=""&gt; &lt;/span&gt;&lt;span style="color:#b0b"&gt;\'&lt;/span&gt;&lt;span style=""&gt;,lng,&lt;/span&gt;&lt;span style="color:#b0b"&gt;\'&lt;/span&gt;&lt;span style=""&gt;)&lt;/span&gt;&lt;span style="color:#b0b"&gt;\'&lt;/span&gt;&lt;span style=""&gt;))&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;create spatial index amenities_spatial_idx on amenities(lat_lng_point)&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style=""&gt;create trigger set_lat_lng_point before insert on amenities for each row set  new.lat_lng_point = GeomFromText(concat('Point(',new.lat,' ',new.lng,')'));&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;

  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#038;font-weight:bold"&gt;self&lt;/span&gt;.down
    execute &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;alter table amenities drop lat_lng_point&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=GQPgF_UjhGo:voqncbHIkrY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=GQPgF_UjhGo:voqncbHIkrY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=GQPgF_UjhGo:voqncbHIkrY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=GQPgF_UjhGo:voqncbHIkrY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/GQPgF_UjhGo" height="1" width="1"/&gt;</description>
          <pubDate>Fri, 06 Mar 2009 06:35:51 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2009/03/06/speeding-up-location-based-searches/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/GQPgF_UjhGo/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2009/03/06/speeding-up-location-based-searches/</feedburner:origLink></item>
    
        <item>
          <title>Speeding up SOLR indexing</title>
          <description>&lt;p&gt; We're finishing up a project that uses SOLR and &lt;a href="http://acts-as-solr.rubyforge.org/"&gt;acts_as_solr&lt;/a&gt; extensively. Our dataset isn't large, but it's non-trivial as well. We're managing a search index with a little over 100,000 documents in it. Each document has indexes on 12 fields. In general, our search performance is outstanding. We get results almost instantly. Our indexing performance was another story. I'm not a big fan of premature optimization, but when a full reindex took 4 days, I decided something needed to change&lt;/p&gt; &lt;p&gt;
  There's some &lt;a href="http://blog.aisleten.com/2008/01/26/optimizing-solr-and-rails-index-in-the-background/"&gt;good advice&lt;/a&gt; on the web for pulling indexing out of a request flow. Unfortunately for us, this wasn't going to help. Our updates would take several days to process. This delay makes testing changes to search incredibly painful.
&lt;/p&gt;
&lt;p&gt;After much googling, I found a number of people that recommended allowing SOLR to manage its own commits. It took me a few tries to get this working. In the end, I was making it much harder than it had to be. Disabling autcommit really only takes two steps.
&lt;/p&gt;
&lt;p&gt;
 First, update your call to &lt;code&gt;acts_as_solr&lt;/code&gt; to disable autcommit:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;MyIndexedModel&lt;/span&gt; &amp;lt; &lt;span style="color:#036;font-weight:bold"&gt;ActiveRecord&lt;/span&gt;::&lt;span style="color:#036;font-weight:bold"&gt;Base&lt;/span&gt;
  acts_as_solr &lt;span style="color:#A60"&gt;:fields&lt;/span&gt;=&amp;gt;[&lt;span style="color:#A60"&gt;:name&lt;/span&gt;,&lt;span style="color:#A60"&gt;:body&lt;/span&gt;], &lt;span style="color:#A60"&gt;:auto_commit&lt;/span&gt;=&amp;gt;&lt;span style="color:#038;font-weight:bold"&gt;false&lt;/span&gt;
&lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  With just that change our index performance went from 2 seconds per record to 50 records per second. A 100x speed up. Unfortunately, our changes weren't showing up in the index. SOLR doesn't add indexed records to the database until a commit is done. SOLR does provide an easy way to tell it to manage the commits itself. To enable this, edit &lt;code&gt;vendor/plugins/acts_as_solr/solr/solr/conf/solrconfig.xml&lt;/code&gt;. There is commented out configuration for &lt;code&gt;autoCommit&lt;/code&gt;. Remove the XML comments around this area. We use the &lt;code&gt;maxTime&lt;/code&gt; parameter to have SOLR update the index every minute. Our configuration looks like:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
...
  &lt;span style="color:#070"&gt;&amp;lt;updateHandler&lt;/span&gt; &lt;span style="color:#007"&gt;class&lt;/span&gt;=&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style=""&gt;solr.DirectUpdateHandler2&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#070"&gt;&amp;gt;&lt;/span&gt;

    &lt;span style="color:#888"&gt;&amp;lt;!-- A prefix of &amp;quot;solr.&amp;quot; for class names is an alias that
         causes solr to search appropriate packages, including
         org.apache.solr.(search|update|request|core|analysis)
     --&amp;gt;&lt;/span&gt;

    &lt;span style="color:#070"&gt;&amp;lt;autoCommit&amp;gt;&lt;/span&gt;
      &lt;span style="color:#070"&gt;&amp;lt;maxDocs&amp;gt;&lt;/span&gt;10000&lt;span style="color:#070"&gt;&amp;lt;/maxDocs&amp;gt;&lt;/span&gt;
      &lt;span style="color:#070"&gt;&amp;lt;maxTime&amp;gt;&lt;/span&gt;60000&lt;span style="color:#070"&gt;&amp;lt;/maxTime&amp;gt;&lt;/span&gt;
    &lt;span style="color:#070"&gt;&amp;lt;/autoCommit&amp;gt;&lt;/span&gt;
...
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
 That's all there is to it! We get 100x faster indexing and our updates still show up within a minute. Suddenly I don't dread making changes to our indexed models!
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=GljimMvtAsc:DJkOuSlbEYI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=GljimMvtAsc:DJkOuSlbEYI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=GljimMvtAsc:DJkOuSlbEYI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=GljimMvtAsc:DJkOuSlbEYI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/GljimMvtAsc" height="1" width="1"/&gt;</description>
          <pubDate>Wed, 14 Jan 2009 12:01:01 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2009/01/14/speeding-up-solr-indexing/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/GljimMvtAsc/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2009/01/14/speeding-up-solr-indexing/</feedburner:origLink></item>
    
        <item>
          <title>Announcing Facebooker support for Facebook Connect</title>
          <description>&lt;p&gt;
  Over the last few months I've received quite a few requests for Facebook Connect support in Facebooker. Thanks to Kevin Lochner, I'm proud to announce Facebook Connect support in Facebooker. Read on to learn how to create a simple Facebook Connect application.
&lt;/p&gt;
 &lt;p&gt;
  
&lt;/p&gt;
&lt;p&gt;
  The setup of a Facebook Connect application is very similar to that of a Facebook Platform Application. You'll need to create an application using the Facebook Developer tool and then build a Rails application and install the Facebooker plugin. If you need help with this, check out &lt;a href="http://www.pragprog.com/screencasts/v-mmfacer/rails-development-for-the-facebook-platform"&gt;the screencast&lt;/a&gt; or &lt;a href="http://www.pragprog.com/titles/mmfacer"&gt;My Book&lt;/a&gt;. You'll need to set up your facebooker.yml and configure a callback URL on Facebook.
&lt;/p&gt;
&lt;p&gt;
  Once you've got a basic application set up and running, we'll need to generate an &lt;code&gt;xd_receiver&lt;/code&gt; file. The xd_receiver file allows Facebook to perform cross domain javascript requests. You can generate this file by running:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
  script/generate xd_receiver
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  A file named &lt;code&gt;xd_receiver.html&lt;/code&gt; will be generated and placed in your &lt;code&gt;public&lt;/code&gt; directory. Make sure this file is added to your source code control system. It must exist for Facebook Connect to work.
&lt;/p&gt;
&lt;p&gt;
  With that setup done, it's time to try out Facebook Connect. Before we can do anything else, we'll need to ask our user to login. Let's create a controller and an action for testing. I called my controller "connect" and just used the index action because I'm lazy. Inside the view for action, we'll need to include a javascript file and do a little setup. Add this to your view:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
&lt;span style="color:#34b"&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Strict//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span style="color:#070"&gt;&amp;lt;html&lt;/span&gt; &lt;span style="color:#007"&gt;xmlns&lt;/span&gt;=&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style=""&gt;http://www.w3.org/1999/xhtml&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#007"&gt;xmlns:fb&lt;/span&gt;=&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style=""&gt;http://www.facebook.com/2008/fbml&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#070"&gt;&amp;gt;&lt;/span&gt;
&lt;span style="color:#070"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;&amp;lt;%=&lt;/span&gt; javascript_include_tag &lt;span style="color:#A60"&gt;:defaults&lt;/span&gt;&lt;span style="font-weight:bold;color:#777"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color:#070"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span style="color:#070"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span style="color:#070"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;My Great Application&lt;span style="color:#070"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;&amp;lt;%=&lt;/span&gt; fb_connect_javascript_tag &lt;span style="font-weight:bold;color:#777"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;&amp;lt;%=&lt;/span&gt; init_fb_connect &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style=""&gt;XFBML&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight:bold;color:#777"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;&amp;lt;%=&lt;/span&gt; fb_login_button&lt;span style="font-weight:bold;color:#777"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;

&lt;span style="color:#070"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span style="color:#070"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  Facebook connect requires an XHTML doctype declaration along with a namespace on your HTML element. It also works most reliably if your HTML is valid. If you're getting strange results, it's worth validating your HTML. The three calls to helpers are the actual meat of the view. The first call includes the Facebook Connect javascript. The second line runs a little setup and the final line renders an &amp;lt;fb:login-button&amp;gt; tag. 
&lt;/p&gt;
&lt;p&gt;
  If all goes well, you should see something that looks like:
 &lt;img src="/images/fb_connect_1.png" /&gt;
&lt;/p&gt;
&lt;p&gt;
  If you don't see that, you'll need to verify that things are configured just right. There's a lot of magic going on and it tends to be brittle. Make sure that your callback URL is correctly set and that you are accessing your site using the callback URL. Make sure that your &lt;code&gt;public/xd_receiver.html&lt;/code&gt; file exists. Finally, make sure that you have correctly set up your facebooker.yml file.
&lt;/p&gt;
&lt;p&gt;
 Take a look at the source of your page. Doesn't that &amp;lt;fb:login-button&amp;gt; tag look strange? To render &lt;a href="http://wiki.developers.facebook.com/index.php/XFBML"&gt;FBML&lt;/a&gt; inside your page, Facebook is using into the HTML equivalents. The login button is just one of many tags we can use. For example, we can include a user's profile photo by calling &lt;code&gt;fb_profile_pic&lt;/code&gt;. Add a call to show your own profile. Once you've done that, you should see something like:
 &lt;img src="/images/fb_connect_1.png" /&gt;
Notice that Facebook isn't showing your picture. That's because of privacy restrictions. Before we can see information about a Facebook user, we'll need to login. Click the "connect" button and you should see a popup prompting you to log in. After logging in, your profile picture will now be shown.
&lt;/p&gt;
&lt;p&gt;
  Getting access to FBML outside of the Facebook canvas is cool, but it's just a start to what we can do. When we login using Facebook Connect, Facebook sets several cookies that our application can use to access a Facebook session. Facebooker knows how to use these cookies to set up a facebook session. Add the following code to your controller:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
  before_filter &lt;span style="color:#A60"&gt;:set_facebook_session&lt;/span&gt;
  helper_method &lt;span style="color:#A60"&gt;:facebook_session&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
 That code sets up the users Facebook session and makes it available to the view. With it, you have access to the normal REST API as you can see in the following code.
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;&amp;lt;%&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; facebook_session &lt;span style="font-weight:bold;color:#777"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span style="color:#070"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;You are logged in as &lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;&amp;lt;%=&lt;/span&gt; facebook_session.user.name &lt;span style="font-weight:bold;color:#777"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#070"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;&amp;lt;%&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="font-weight:bold;color:#777"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span style="color:#070"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;You are not logged in!&lt;span style="color:#070"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;&amp;lt;%&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt; &lt;span style="font-weight:bold;color:#777"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
This means you can ask for data about a user like their name and their friends. You can also post stories to their feed! You have most of the power of a Facebook Platform application inside your existing code!
&lt;/p&gt;
&lt;p&gt;
  This support is still very new, so make sure you join the Facebooker mailing list on Rubyforge if you decide to use it. Let us know what you build with it!
&lt;/p&gt;
&lt;p&gt;
  Thanks again to Kevin Lochner who provided the patch to support authentication using cookies. Thanks also to Nate Kontny of &lt;a href="http://www.inklingmarkets.com"&gt;Inkling Markets&lt;/a&gt; who provided a great overview of how Facebook Connect works! Happy New Year everyone!
&lt;/p&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=LJcQnNIyII4:77rjQx5QUvc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=LJcQnNIyII4:77rjQx5QUvc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=LJcQnNIyII4:77rjQx5QUvc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=LJcQnNIyII4:77rjQx5QUvc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/LJcQnNIyII4" height="1" width="1"/&gt;</description>
          <pubDate>Fri, 02 Jan 2009 09:51:20 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2009/01/02/announcing-facebooker-support-for-facebook-connect/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/LJcQnNIyII4/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2009/01/02/announcing-facebooker-support-for-facebook-connect/</feedburner:origLink></item>
    
        <item>
          <title>Daemonizing Rails</title>
          <description>&lt;p&gt;No, this isn't a post about us converting from Linux to BSD. Instead, I'm talk about starting long running processes that use the Rails environment. If you've tried to do this, you've probably seen some obscure errors like:
&lt;/p&gt;
&lt;pre&gt;
closed stream 
activesupport-2.1.0/lib/active_support/buffered_logger.rb:105:in `write'
activesupport-2.1.0/lib/active_support/buffered_logger.rb:105:in `flush'
activesupport-2.1.0/lib/active_support/buffered_logger.rb:118:in `auto_flush'
activesupport-2.1.0/lib/active_support/buffered_logger.rb:70:in `add'
activesupport-2.1.0/lib/active_support/buffered_logger.rb:76: ...
&lt;/pre&gt;
&lt;p&gt;
Inside, I'll show you how to avoid this
&lt;/p&gt; &lt;p&gt;
Daemons in UNIX are a wonderful, magical thing. Even though it seems like starting up a background job should be simple, there are a lot of steps involved. You can see them in detail in &lt;a href="http://tammersaleh.com/system/assets/angels_and_daemons.pdf"&gt;Tammer Saleh's Angels and Daemons talk&lt;/a&gt;. The really interesting issue for us is that processes close all open IO streams when they daemonize. That may not cause a problem for a normal Ruby Application, but it violates some assumptions that Rails makes.
&lt;/p&gt;
&lt;p&gt;
 When Rails boots, it opens a few different file descriptors. First, it opens a log file. This is your production.log or similar. It also makes a connection to the database. Rails gets unhappy when these files are closed out from under it. You could write a lot of code to handle these conditions or you can take the simple way out: Delay booting Rails until after you daemonize. Here's a simple script that does that:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
&lt;span style="color:#34b"&gt;#!/usr/bin/env ruby&lt;/span&gt;
require &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;rubygems&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
require &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;daemons&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span style="color:#036;font-weight:bold"&gt;ROOT&lt;/span&gt; = &lt;span style="color:#036;font-weight:bold"&gt;File&lt;/span&gt;.expand_path(&lt;span style="color:#036;font-weight:bold"&gt;File&lt;/span&gt;.dirname(&lt;span style="color:#038;font-weight:bold"&gt;__FILE__&lt;/span&gt;)+&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;/../&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span style="color:#036;font-weight:bold"&gt;Daemons&lt;/span&gt;.run_proc(&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style=""&gt;watcher&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#A60"&gt;:dir_mode&lt;/span&gt; =&amp;gt; &lt;span style="color:#A60"&gt;:normal&lt;/span&gt;, &lt;span style="color:#A60"&gt;:dir&lt;/span&gt;=&amp;gt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;&lt;span style="color:#036;font-weight:bold"&gt;ROOT&lt;/span&gt;&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;/log/&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;span style="color:#A60"&gt;:log_output&lt;/span&gt; =&amp;gt;&lt;span style="color:#038;font-weight:bold"&gt;true&lt;/span&gt;, &lt;span style="color:#A60"&gt;:backtrace&lt;/span&gt;=&amp;gt;&lt;span style="color:#038;font-weight:bold"&gt;true&lt;/span&gt;) &lt;span style="color:#080;font-weight:bold"&gt;do&lt;/span&gt;
  require &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;&lt;span style="color:#036;font-weight:bold"&gt;ROOT&lt;/span&gt;&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;/config/environment&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span style="color:#888"&gt;# Do work here&lt;/span&gt;
&lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
This code is simple, but there are a couple of subtleties. The first is that we need to use &lt;code&gt;File.expand_path&lt;/code&gt; to turn the Rails Root path into an absolute path. When an application daemonizes, it changes its working directory to "/". If we use a relative path for our RAILS_ROOT it will no longer point to our Rails installation.
&lt;/p&gt;
&lt;p&gt;
Once we have the absolute RAILS_ROOT path, we can daemonize and then boot Rails. Rails will now start as usual and all files will be opened. That's all there is to it. Now you can easily start long running processes using Rails. You can even use a cap task to easily restart them on deploy:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
task &lt;span style="color:#A60"&gt;:restart_watcher&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;do&lt;/span&gt;
  run &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="background:#eee;color:black"&gt;&lt;span style="font-weight:bold;color:#777"&gt;#{&lt;/span&gt;current_path&lt;span style="font-weight:bold;color:#777"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span style=""&gt;/script/watcher restart&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=2etQc-Xs0Bs:EQR-FJUknRg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=2etQc-Xs0Bs:EQR-FJUknRg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=2etQc-Xs0Bs:EQR-FJUknRg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=2etQc-Xs0Bs:EQR-FJUknRg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/2etQc-Xs0Bs" height="1" width="1"/&gt;</description>
          <pubDate>Tue, 02 Dec 2008 12:02:54 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2008/12/02/daemonizing-rails/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/2etQc-Xs0Bs/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2008/12/02/daemonizing-rails/</feedburner:origLink></item>
    
        <item>
          <title>It feels good to see your name in print</title>
          <description>&lt;p&gt;It was almost a full year ago that I started writing a &lt;a href="http://www.pragprog.com/titles/mmfacer"&gt;book on Facebook development.&lt;/a&gt; This past Friday I got a very exciting box. Unboxing pictures inside.&lt;/p&gt; &lt;p&gt;&lt;img src="/unboxing_1.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;A nice note on top of the box.&lt;/p&gt;
&lt;p&gt;&lt;img src="/unboxing_2.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Carefully wrapped&lt;/p&gt;
&lt;p&gt;&lt;img src="/unboxing_3.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;There it is.&lt;/p&gt;
&lt;p&gt;I really underestimated the amount of work it would take to write a book. I had read Dave Thomas&amp;#8217; &lt;a href="http://pragdave.pragprog.com/pragdave/2007/03/sywtwab_1_so_yo.html"&gt;So You Want to Write a Book&lt;/a&gt; so I should have realized what an undertaking it was. I had just completed my first marathon at that point and figured it couldn&amp;#8217;t be harder than running 26.2 miles. Boy was I ever wrong. I&amp;#8217;m proud of the book. I know it is something I would have loved to have when starting to develop Facebook applications.&lt;/p&gt;
&lt;p&gt;Now I&amp;#8217;m going to get back to training for the Philly Marathon on 11/23. Training seems much easier now.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=efRrAtECUWY:YWe3TioWhHQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=efRrAtECUWY:YWe3TioWhHQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=efRrAtECUWY:YWe3TioWhHQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=efRrAtECUWY:YWe3TioWhHQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/efRrAtECUWY" height="1" width="1"/&gt;</description>
          <pubDate>Wed, 22 Oct 2008 08:11:51 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2008/10/22/it-feels-good-to-see-your-name-in-print/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/efRrAtECUWY/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2008/10/22/it-feels-good-to-see-your-name-in-print/</feedburner:origLink></item>
    
        <item>
          <title>Preloading summary data using ActiveRecord</title>
          <description>&lt;p&gt;It seems like every application we build has at least one screen that needs to access a lot of data. For example, if we were building a web application for a school we would likely have a page for teachers that shows all of their students including their current grage, number of assignments turned in on time and attendance. Inside, we'll look at how we can make this type of page run quickly without adding much complexity.
&lt;/p&gt;  &lt;p&gt; Let's set up a couple of example models to demonstrate this case. We'll have a student model. Our student will have a list of absences, a list of assignments and a list of grades. Our model could look something like:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;Student&lt;/span&gt; &amp;lt; &lt;span style="color:#036;font-weight:bold"&gt;ActiveRecord&lt;/span&gt;::&lt;span style="color:#036;font-weight:bold"&gt;Base&lt;/span&gt;
  has_many &lt;span style="color:#A60"&gt;:assignments&lt;/span&gt;
  has_many &lt;span style="color:#A60"&gt;:grades&lt;/span&gt;, &lt;span style="color:#A60"&gt;:through&lt;/span&gt; =&amp;gt; &lt;span style="color:#A60"&gt;:assignments&lt;/span&gt;
  has_many &lt;span style="color:#A60"&gt;:absences&lt;/span&gt;
&lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
In our output, we'll want to show a tabular view of information for each student. A first cut would probably look something like:
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
  &amp;lt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;% &lt;/span&gt;&lt;span style=""&gt;for&lt;/span&gt;&lt;span style="color:#710"&gt; &lt;/span&gt;&lt;/span&gt;student &lt;span style="color:#080;font-weight:bold"&gt;in&lt;/span&gt; &lt;span style="color:#33B"&gt;@teacher&lt;/span&gt;.students &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%&amp;gt;&lt;/span&gt;&lt;span style=""&gt;
    &amp;lt;tr&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &amp;lt;td&amp;gt;&amp;lt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%=&lt;/span&gt;&lt;span style=""&gt; student.name %&amp;gt; &amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%&lt;/span&gt;&lt;span style="color:#710"&gt;=&lt;/span&gt;&lt;/span&gt; student.absences.count &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%&amp;gt;&lt;/span&gt;&lt;span style=""&gt; &amp;lt;/td&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &amp;lt;td&amp;gt;&amp;lt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%=&lt;/span&gt;&lt;span style=""&gt; student.assignments.select {|a| a.on_time?}.size / student.assignments.size.to_f rescue nil %&amp;gt; &amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%&lt;/span&gt;&lt;span style="color:#710"&gt;=&lt;/span&gt;&lt;/span&gt; student.grades.sum(&lt;span style="color:#A60"&gt;:score&lt;/span&gt;)/student.grades.count &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%&amp;gt;&lt;/span&gt;&lt;span style=""&gt; &amp;lt;/td&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
   &amp;lt;&lt;span style="background-color:#fff0ff"&gt;&lt;span style="color:#404"&gt;/&lt;/span&gt;&lt;span style="color:#808"&gt;tr&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  That code works, but there are a couple of obvious problems with it. First, we have a lot of logic in our views. Second, we'll run 4 queries for each user: 1 to count absences, 1 to get the number of on_time assignements, 1 to get the total score and one to count the number of grades. This is okay for viewing 10 students, but performance will become a serious problem when looking at a couple of hundred students.
&lt;/p&gt;
&lt;p&gt;
  So how do we clean this up? Let's start by cleaning up the logic in the views. Hopefully that will make a solution more apparent. The first step is to create methods on student for each of those items. Instead of calling &lt;code&gt;student.absences.count&lt;/code&gt; in the view, we can add a &lt;code&gt;student.times_absent&lt;/code&gt; method. We can do the same for the percent of on time assignments and for the average score. Our code then becomes:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
  &amp;lt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;% &lt;/span&gt;&lt;span style=""&gt;for&lt;/span&gt;&lt;span style="color:#710"&gt; &lt;/span&gt;&lt;/span&gt;student &lt;span style="color:#080;font-weight:bold"&gt;in&lt;/span&gt; &lt;span style="color:#33B"&gt;@teacher&lt;/span&gt;.students &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%&amp;gt;&lt;/span&gt;&lt;span style=""&gt;
    &amp;lt;tr&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &amp;lt;td&amp;gt;&amp;lt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%=&lt;/span&gt;&lt;span style=""&gt; student.name %&amp;gt; &amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%&lt;/span&gt;&lt;span style="color:#710"&gt;=&lt;/span&gt;&lt;/span&gt; student.times_absent &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%&amp;gt;&lt;/span&gt;&lt;span style=""&gt; &amp;lt;/td&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &amp;lt;td&amp;gt;&amp;lt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%=&lt;/span&gt;&lt;span style=""&gt; student.percent_assignments_on_time %&amp;gt; &amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%&lt;/span&gt;&lt;span style="color:#710"&gt;=&lt;/span&gt;&lt;/span&gt; student.average_score &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;%&amp;gt;&lt;/span&gt;&lt;span style=""&gt; &amp;lt;/td&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
   &amp;lt;&lt;span style="background-color:#fff0ff"&gt;&lt;span style="color:#404"&gt;/&lt;/span&gt;&lt;span style="color:#808"&gt;tr&amp;gt;
  &amp;lt;% end %&amp;gt;

&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  Our implementation of those methods will look familiar:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;Student&lt;/span&gt; &amp;lt; &lt;span style="color:#036;font-weight:bold"&gt;ActiveRecord&lt;/span&gt;::&lt;span style="color:#036;font-weight:bold"&gt;Base&lt;/span&gt;
  has_many &lt;span style="color:#A60"&gt;:assignments&lt;/span&gt;
  has_many &lt;span style="color:#A60"&gt;:grades&lt;/span&gt;, &lt;span style="color:#A60"&gt;:through&lt;/span&gt; =&amp;gt; &lt;span style="color:#A60"&gt;:assignments&lt;/span&gt;
  has_many &lt;span style="color:#A60"&gt;:absences&lt;/span&gt;
  
  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;times_absent&lt;/span&gt;
    absences.count
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
  
  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;percent_assignments_on_time&lt;/span&gt;
    student.assignments.select {|a| a.on_time?}.size / student.assignments.size.to_f &lt;span style="color:#080;font-weight:bold"&gt;rescue&lt;/span&gt; &lt;span style="color:#038;font-weight:bold"&gt;nil&lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;

  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;average_score&lt;/span&gt;
    student.grades.sum(&lt;span style="color:#A60"&gt;:score&lt;/span&gt;)/student.grades.count
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt; 

&lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  That cleaned up our code, but it didn't do anything for our performance. We could try to use &lt;code&gt;:include&lt;/code&gt; to speed up our query. If we had 200 students each with 200 assignments, this would involve us running a find that returns 200*200 rows. Another solution to our performance problem is to use a raw sql query. We can write a sql query that will give us the percent_assignments_on_time and the average score for our whole list of users at once. I normally avoid doing this because then I can't lose the abstraction that active record provides. What if we could keep the same interface but use a sql query to preload the data? That would be the best of both worlds.
&lt;/p&gt;
&lt;p&gt; We can do this using memoization. To implement this, we'll start by adding memoization to our methods:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;average_score&lt;/span&gt;
    &lt;span style="color:#33B"&gt;@average_score&lt;/span&gt; ||= student.grades.sum(&lt;span style="color:#A60"&gt;:score&lt;/span&gt;)/student.grades.count
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  Next, we'll create a method that will retrieve and set the average score for each user that we care about. It will look something like:
&lt;/p&gt;
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;
  attr_accessor &lt;span style="color:#A60"&gt;:average_score&lt;/span&gt;, &lt;span style="color:#A60"&gt;:percent_assignments_on_time&lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;def&lt;/span&gt; &lt;span style="color:#038;font-weight:bold"&gt;self&lt;/span&gt;.load_assignment_analytics!(students)
    sql =&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;lt;&amp;lt;-SQL&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style=""&gt;
      select u.id, 
             avg(score) as cached_average_score,
             sum(on_time)/count(*) as cached_on_time_percent
      from students u
       join assignments a on u.id = a.user_id
       join grades g on g.assignment_id = a.id
      where u.id in (?)
      group by u.id&lt;/span&gt;&lt;span style="color:#710"&gt;
    SQL&lt;/span&gt;&lt;/span&gt;
    assignment_info = &lt;span style="color:#036;font-weight:bold"&gt;Student&lt;/span&gt;.find_by_sql([sql,&lt;span style="color:#038;font-weight:bold"&gt;false&lt;/span&gt;,users.map(&amp;amp;&lt;span style="color:#A60"&gt;:id&lt;/span&gt;)])
    user_map = assignment_info.index_by(&amp;amp;&lt;span style="color:#A60"&gt;:id&lt;/span&gt;)
    students.each &lt;span style="color:#080;font-weight:bold"&gt;do&lt;/span&gt; |student|
      rec = user_map[student.id] 
      &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; rec.nil?
        student.percent_assignments_on_time = &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style=""&gt;None&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
        student.average_score = &lt;span style="background-color:#fff0f0;color:#D20"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style=""&gt;None&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span style="color:#080;font-weight:bold"&gt;else&lt;/span&gt;
        student.percent_assignments_on_time = rec. cached_on_time_percent.to_f
        student.average_score = rec. cached_average_score.to_f
      &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
    
  &lt;span style="color:#080;font-weight:bold"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;
  That methods runs our query for all users that we pass in. For each user, it finds the result from our query. If no result exists (because they don't have any assignments or grades) it will set the values to "None". (If we set them to nil or false our query would be re-run in the interface because of the &lt;code&gt;||=&lt;/code&gt; line. If there are results for a user, we set the value on the object.
&lt;/p&gt;
&lt;p&gt;
  Now, when our view iterates over the list of users, it will use the previously stored data. The interface remains the same but our performance improves just by adding a single call to &lt;code&gt;Student.load_assignment_analytics!(teacher.students)&lt;/code&gt; in our controller. If we forget to call this method, the view will still work, it will just run more queries. This is definitely less DRY than our original code, as we implement the logic for our counts in two ways. We could redefine our average score method to always call load_assignment_info!, but that might be overkill. There are also a couple of gotchas. Because we use Student.find_by_sql to fetch our data, we need to make sure that we name our calculated constants something that isn't a method name on student. If you don't, the method will be called.
&lt;/p&gt;
&lt;p&gt;
  All in all, I think this is a relatively elegant and simple solution to a complex problem.
&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=NJ8NDQQdIbc:80ohSL9hXqI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=NJ8NDQQdIbc:80ohSL9hXqI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=NJ8NDQQdIbc:80ohSL9hXqI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=NJ8NDQQdIbc:80ohSL9hXqI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/NJ8NDQQdIbc" height="1" width="1"/&gt;</description>
          <pubDate>Wed, 08 Oct 2008 07:55:21 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2008/10/08/preloading-summary-data-using-activerecord/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/NJ8NDQQdIbc/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2008/10/08/preloading-summary-data-using-activerecord/</feedburner:origLink></item>
    
        <item>
          <title>Developing Facebook Platform Applications with Rails is done!</title>
          <description>&lt;p&gt;I've been busy recently, as you may have guessed from the lack of posts. It's time to share the items I've been working on!&lt;/p&gt; &lt;p&gt;First, I&amp;#8217;m thrilled to announce that &lt;a href="http://www.pragprog.com/titles/mmfacer"&gt;Developing Facebook Platform Applications with Rails&lt;/a&gt; is being printed. It should be shipping withing the next couple of weeks. If you haven&amp;#8217;t ordered your copy, pick one up now. All told, it took about 10 months to get the book out. That&amp;#8217;s definitely longer than I hoped, but there is a good reason. Facebook released a massive overhaul of their platform about 3 months ago. The shipping version of the book has been completely updated to reflect this new &lt;span class="caps"&gt;API&lt;/span&gt;. It took a lot of work, but I&amp;#8217;m happy with how it turned out. You can check out the application we build &lt;a href="http://apps.new.facebook.com/karate_poke"&gt;on Facebook.&lt;/a&gt; You can also see the code on &lt;a href="http://github.com/mmangino/karate_poke/tree/master"&gt;github.&lt;/a&gt; I&amp;#8217;d love for some intrepid readers to fork the code and send me patches to make the application prettier!&lt;/p&gt;
&lt;p&gt;In other Facebook news, I released &lt;a href="http://github.com/mmangino/facebooker_authentication/tree/master"&gt;a plugin called facebooker_authentication&lt;/a&gt; that encapsulates a lot of the code that we use on Facebook applications. It covers user creation, user authentication and some helpers for handling users. I&amp;#8217;ve used something similar on every application. In fact, I use this plugin on two forthcoming screencasts for the Pragmatic Programmers. Expect another announcement here when that happens.&lt;/p&gt;
&lt;p&gt;Finally, I&amp;#8217;m still getting settled into Philadelphia. It&amp;#8217;s a great city with some fun people. I&amp;#8217;ve spent a couple of days at &lt;a href="http://www.indyhall.org/"&gt;Indy Hall&lt;/a&gt; and really dig it. I&amp;#8217;ve also been volunteering with a group called &lt;a href="http://www.backonmyfeet.org"&gt;Back On My Feet&lt;/a&gt; that supports the homeless population in Chicago. I&amp;#8217;m running the Philly marathon on their behalf. &lt;a href="http://www.active.com/donate/raceforbackonmyfeet/mangino"&gt;Check out my story and make a donation!&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=mu5Yc-Y0iaU:D5RorwdKnCw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=mu5Yc-Y0iaU:D5RorwdKnCw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?i=mu5Yc-Y0iaU:D5RorwdKnCw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?a=mu5Yc-Y0iaU:D5RorwdKnCw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ElevatedRailsRssFeed?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ElevatedRailsRssFeed/~4/mu5Yc-Y0iaU" height="1" width="1"/&gt;</description>
          <pubDate>Thu, 25 Sep 2008 07:04:45 GMT</pubDate>
          <guid isPermaLink="false">http://www.elevatedrails.com/articles/2008/09/25/developing-facebook-platform-applications-with-rails-is-done/</guid>
          <link>http://feedproxy.google.com/~r/ElevatedRailsRssFeed/~3/mu5Yc-Y0iaU/</link>
        <feedburner:origLink>http://www.elevatedrails.com/articles/2008/09/25/developing-facebook-platform-applications-with-rails-is-done/</feedburner:origLink></item>
    
    
  </channel>
</rss>
