<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Elevated Rails RSS Feed</title>
    <link>http://www.elevatedrails.com/rss/</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Elevated Rails, A Chicago based software development consultancy.</description>
    
    
        <item>
          <title>Open Source Bridge</title>
          <description>&lt;p&gt;If you&amp;#8217;re looking for a great conference to attend in June, check out &lt;a href=&quot;http://opensourcebridge.org&quot;&gt;Open Source Bridge&lt;/a&gt;. Open Source Bridge is a technology agnostic conference covering all things open source. It includes several tracks, including a track on building open source businesses and a track on cool hacks. Our own &lt;a href=&quot;http://dyepot-teapot.com/&quot;&gt;Audrey Eschright&lt;/a&gt; is one of the Co-Chairs. Audrey and Selena Deckelmann have put together an incredible &lt;a href=&quot;http://opensourcebridge.org/events/2009/sessions&quot;&gt;lineup of speakers&lt;/a&gt; including &lt;a href=&quot;http://opensourcebridge.org/users/43&quot;&gt;Brian Aker&lt;/a&gt; (MySQL and Drizzle) &lt;a href=&quot;http://opensourcebridge.org/users/68&quot;&gt;Greg Kroah-Hartman&lt;/a&gt; (Linux Kernel) and &lt;a href=&quot;http://opensourcebridge.org/users/313&quot;&gt;Chris Wanstrath&lt;/a&gt; (Github)&lt;/p&gt;


	&lt;p&gt;If you&amp;#8217;re anywhere near the area, I highly recommend attending.&lt;/p&gt; </description>
          <pubDate>Wed, 20 May 2009 15:14:29 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2009/05/20/open-sourc/</guid>
          <link>http://www.elevatedrails.com/articles/2009/05/20/open-sourc/</link>
        </item>
    
        <item>
          <title>Standing up to be counted</title>
          <description>&lt;p&gt;Nick Sieger &lt;a href=&quot;http://blog.nicksieger.com/articles/2009/04/30/stand-and-be-counted&quot;&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; </description>
          <pubDate>Thu, 30 Apr 2009 12:58:57 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2009/04/30/standing-up-to-be-counted/</guid>
          <link>http://www.elevatedrails.com/articles/2009/04/30/standing-up-to-be-counted/</link>
        </item>
    
        <item>
          <title>Speeding up location based searches</title>
          <description>&lt;p&gt;
With the popularity of &lt;a href=&quot;http://maps.google.com&quot;&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=&quot;http://github.com/andre/geokit-gem/tree/master&quot;&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=&quot;http://en.wikipedia.org/wiki/Haversine_formula&quot;&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;pre class=&quot;code_ruby&quot;&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;alter table amenities add lat_lng_point point not null&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;alter table amenities engine myisam&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
&lt;/pre&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&quot;
&lt;/p&gt;

&lt;pre class=&quot;code_sql&quot;&gt;
update amenities set lat_lng_point = GeomFromText(concat('Point(',lat,' ',lng,')'))
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;create spatial index amenities_spatial_idx on amenities(lat_lng_point)&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
  
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;self.near&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;mappable&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;bound&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;GeoKit&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Bounds&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;from_point_and_radius&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;mappable&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;sql&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;
    select *,  GLength(LineStringFromWKB(LineString(AsBinary(lat_lng_point), 
    (AsBinary(GeomFromText('Point(&lt;span class=&quot;expr&quot;&gt;#{mappable.lat}&lt;/span&gt; &lt;span class=&quot;expr&quot;&gt;#{mappable.lng}&lt;/span&gt;)')))))) * 69 as distance
    from amenities where MBRWithin(lat_lng_point,
     GeomFromText('Polygon((&lt;span class=&quot;expr&quot;&gt;#{bound.ne.lat}&lt;/span&gt; &lt;span class=&quot;expr&quot;&gt;#{bound.ne.lng}&lt;/span&gt;, &lt;span class=&quot;expr&quot;&gt;#{bound.ne.lat}&lt;/span&gt; &lt;span class=&quot;expr&quot;&gt;#{bound.sw.lng}&lt;/span&gt;,&lt;span class=&quot;expr&quot;&gt;#{bound.sw.lat}&lt;/span&gt; &lt;span class=&quot;expr&quot;&gt;#{bound.sw.lng}&lt;/span&gt;,&lt;span class=&quot;expr&quot;&gt;#{bound.sw.lat}&lt;/span&gt; &lt;span class=&quot;expr&quot;&gt;#{bound.ne.lng}&lt;/span&gt;,&lt;span class=&quot;expr&quot;&gt;#{bound.ne.lat}&lt;/span&gt; &lt;span class=&quot;expr&quot;&gt;#{bound.ne.lng}&lt;/span&gt;))')) 
    order by distance asc limit 25;
&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;SQL&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;find_by_sql&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;EOT&lt;/span&gt;&lt;span class=&quot;string&quot;&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 class=&quot;constant&quot;&gt;EOT&lt;/span&gt;
&lt;/pre&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=&quot;http://artriggers.rubyforge.org/&quot;&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;pre class=&quot;code_ruby&quot;&gt;
  &lt;span class=&quot;comment&quot;&gt;# Remove lat_lng_point from the list of attributes&lt;/span&gt;
  &lt;span class=&quot;comment&quot;&gt;# otherwise, AR writes it as NULL which keeps the trigger from firing&lt;/span&gt;
  &lt;span class=&quot;comment&quot;&gt;# and setting the value correctly  &lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;before_save&lt;/span&gt;
    &lt;span class=&quot;attribute&quot;&gt;@attributes&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;lat_lng_point&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;)&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;comment&quot;&gt;# Our entire migration&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;ConvertAmenitiesToUseSpatials&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Migration&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;self.up&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;alter table amenities add lat_lng_point point not null&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;alter table amenities engine myisam&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;update amenities set lat_lng_point = GeomFromText(concat(&lt;span class=&quot;escape&quot;&gt;\'&lt;/span&gt;Point(&lt;span class=&quot;escape&quot;&gt;\'&lt;/span&gt;,lat,&lt;span class=&quot;escape&quot;&gt;\'&lt;/span&gt; &lt;span class=&quot;escape&quot;&gt;\'&lt;/span&gt;,lng,&lt;span class=&quot;escape&quot;&gt;\'&lt;/span&gt;)&lt;span class=&quot;escape&quot;&gt;\'&lt;/span&gt;))&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;create spatial index amenities_spatial_idx on amenities(lat_lng_point)&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&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 class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;self.down&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;execute&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;alter table amenities drop lat_lng_point&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;

&lt;/pre&gt;

</description>
          <pubDate>Fri, 06 Mar 2009 09:35:51 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2009/03/06/speeding-up-location-based-searches/</guid>
          <link>http://www.elevatedrails.com/articles/2009/03/06/speeding-up-location-based-searches/</link>
        </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=&quot;http://acts-as-solr.rubyforge.org/&quot;&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=&quot;http://blog.aisleten.com/2008/01/26/optimizing-solr-and-rails-index-in-the-background/&quot;&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;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;MyIndexedModel&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;acts_as_solr&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:fields=&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;[&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:auto_commit=&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&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;pre class=&quot;code_xml&quot;&gt;
...
  &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;updateHandler&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;solr.DirectUpdateHandler2&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&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 class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;autoCommit&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;maxDocs&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;10000&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;maxDocs&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;maxTime&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;60000&lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;maxTime&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;autoCommit&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
...
&lt;/pre&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;</description>
          <pubDate>Wed, 14 Jan 2009 15:01:01 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2009/01/14/speeding-up-solr-indexing/</guid>
          <link>http://www.elevatedrails.com/articles/2009/01/14/speeding-up-solr-indexing/</link>
        </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=&quot;http://www.pragprog.com/screencasts/v-mmfacer/rails-development-for-the-facebook-platform&quot;&gt;the screencast&lt;/a&gt; or &lt;a href=&quot;http://www.pragprog.com/titles/mmfacer&quot;&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;pre class=&quot;code_sh&quot;&gt;
  script/generate xd_receiver
&lt;/pre&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 &quot;connect&quot; 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;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;punct&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;-//W3C//DTD XHTML 1.0 Strict//EN&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;http://www.w3.org/1999/xhtml&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:fb=&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;http://www.facebook.com/2008/fbml&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; javascript_include_tag :defaults%&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;My Great Application&amp;lt;/h1&amp;gt;
&amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;fb_connect_javascript_tag&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;
&amp;lt;%= init_fb_connect &amp;quot;XFBML&amp;quot;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; fb_login_button%&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;span class=&quot;normal&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&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=&quot;/images/fb_connect_1.png&quot; /&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=&quot;http://wiki.developers.facebook.com/index.php/XFBML&quot;&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=&quot;/images/fb_connect_1.png&quot; /&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 &quot;connect&quot; 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;pre class=&quot;code_ruby&quot;&gt;
  &lt;span class=&quot;ident&quot;&gt;before_filter&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:set_facebook_session&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;helper_method&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:facebook_session&lt;/span&gt;
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;facebook_session&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;
  &amp;lt;h2&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;You&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;logged&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; facebook_session.user.name %&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;% else %&amp;gt;
  &amp;lt;h2&amp;gt;You are not logged in!&amp;lt;/h2&amp;gt;
&amp;lt;% end %&amp;gt;&lt;span class=&quot;normal&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&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=&quot;http://www.inklingmarkets.com&quot;&gt;Inkling Markets&lt;/a&gt; who provided a great overview of how Facebook Connect works! Happy New Year everyone!
&lt;/p
</description>
          <pubDate>Fri, 02 Jan 2009 12:51:20 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2009/01/02/announcing-facebooker-support-for-facebook-connect/</guid>
          <link>http://www.elevatedrails.com/articles/2009/01/02/announcing-facebooker-support-for-facebook-connect/</link>
        </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=&quot;http://tammersaleh.com/system/assets/angels_and_daemons.pdf&quot;&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;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;comment&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class=&quot;ident&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;rubygems&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;ident&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;daemons&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;constant&quot;&gt;ROOT&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)+'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;/../&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;')&lt;/span&gt;
&lt;span class=&quot;constant&quot;&gt;Daemons&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;run_proc&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;('&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;',&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:dir_mode&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:normal&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:dir=&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{ROOT}&lt;/span&gt;/log/&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;,&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:log_output&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:backtrace=&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{ROOT}&lt;/span&gt;/config/environment&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;comment&quot;&gt;# Do work here&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&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 &quot;/&quot;. 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;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;ident&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:restart_watcher&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{current_path}&lt;/span&gt;/script/watcher restart&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;

</description>
          <pubDate>Tue, 02 Dec 2008 15:02:54 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2008/12/02/daemonizing-rails/</guid>
          <link>http://www.elevatedrails.com/articles/2008/12/02/daemonizing-rails/</link>
        </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=&quot;http://www.pragprog.com/titles/mmfacer&quot;&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=&quot;/unboxing_1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;


	&lt;p&gt;A nice note on top of the box.&lt;/p&gt;


	&lt;p&gt;&lt;img src=&quot;/unboxing_2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;


	&lt;p&gt;Carefully wrapped&lt;/p&gt;


	&lt;p&gt;&lt;img src=&quot;/unboxing_3.jpg&quot; alt=&quot;&quot; /&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=&quot;http://pragdave.pragprog.com/pragdave/2007/03/sywtwab_1_so_yo.html&quot;&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;</description>
          <pubDate>Wed, 22 Oct 2008 11:11:51 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2008/10/22/it-feels-good-to-see-your-name-in-print/</guid>
          <link>http://www.elevatedrails.com/articles/2008/10/22/it-feels-good-to-see-your-name-in-print/</link>
        </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;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:assignments&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:grades&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:through&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:assignments&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:absences&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@teacher&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;students&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;
    &amp;lt;tr&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; student.name %&amp;gt; &amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;absences&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; &amp;lt;/td&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&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 class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:score&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; &amp;lt;/td&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;tr&amp;gt;
  &amp;lt;% end %&amp;gt;&lt;span class=&quot;normal&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
  &lt;span class=&quot;punct&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@teacher&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;students&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;
    &amp;lt;tr&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; student.name %&amp;gt; &amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;times_absent&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; &amp;lt;/td&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;string&quot;&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 class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;average_score&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; &amp;lt;/td&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;punct&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;tr&amp;gt;
  &amp;lt;% end %&amp;gt;
&lt;span class=&quot;normal&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
  Our implementation of those methods will look familiar:
&lt;/p&gt;
&lt;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:assignments&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:grades&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:through&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:assignments&lt;/span&gt;
  &lt;span class=&quot;ident&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:absences&lt;/span&gt;
  
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;times_absent&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;absences&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;count&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;percent_assignments_on_time&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;assignments&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;on_time?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;assignments&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_f&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;average_score&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:score&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;count&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; 

&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;average_score&lt;/span&gt;
    &lt;span class=&quot;attribute&quot;&gt;@average_score&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:score&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)/&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;count&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt; 
&lt;/pre&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;pre class=&quot;code_ruby&quot;&gt;
  &lt;span class=&quot;ident&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:average_score&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:percent_assignments_on_time&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;self.load_assignment_analytics!&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;students&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;sql&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;string&quot;&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 class=&quot;constant&quot;&gt;    SQL&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;assignment_info&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;find_by_sql&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)])&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;user_map&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;assignment_info&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;index_by&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;amp;&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;students&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;ident&quot;&gt;rec&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;user_map&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; 
      &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;rec&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;nil?&lt;/span&gt;
        &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;percent_assignments_on_time&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;average_score&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;percent_assignments_on_time&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;rec&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;cached_on_time_percent&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_f&lt;/span&gt;
        &lt;span class=&quot;ident&quot;&gt;student&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;average_score&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;rec&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;cached_average_score&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_f&lt;/span&gt;
      &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
    
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&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 &quot;None&quot;. (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;</description>
          <pubDate>Wed, 08 Oct 2008 10:55:21 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2008/10/08/preloading-summary-data-using-activerecord/</guid>
          <link>http://www.elevatedrails.com/articles/2008/10/08/preloading-summary-data-using-activerecord/</link>
        </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=&quot;http://www.pragprog.com/titles/mmfacer&quot;&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=&quot;caps&quot;&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=&quot;http://apps.new.facebook.com/karate_poke&quot;&gt;on Facebook.&lt;/a&gt; You can also see the code on &lt;a href=&quot;http://github.com/mmangino/karate_poke/tree/master&quot;&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=&quot;http://github.com/mmangino/facebooker_authentication/tree/master&quot;&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=&quot;http://www.indyhall.org/&quot;&gt;Indy Hall&lt;/a&gt; and really dig it. I&amp;#8217;ve also been volunteering with a group called &lt;a href=&quot;http://www.backonmyfeet.org&quot;&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=&quot;http://www.active.com/donate/raceforbackonmyfeet/mangino&quot;&gt;Check out my story and make a donation!&lt;/a&gt;&lt;/p&gt;</description>
          <pubDate>Thu, 25 Sep 2008 10:04:45 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2008/09/25/developing-facebook-platform-applications-with-rails-is-done/</guid>
          <link>http://www.elevatedrails.com/articles/2008/09/25/developing-facebook-platform-applications-with-rails-is-done/</link>
        </item>
    
        <item>
          <title>Painless monitoring with Scout</title>
          <description>&lt;p&gt;
I write about monitoring from time to time. We host about 15 applications for our clients, so monitoring our servers is a high priority for us. We've experimented with a large number of tools including &lt;a href=&quot;http://www.tildeslash.com/monit/&quot;&gt;monit&lt;/a&gt; and &lt;a href=&quot;http://god.rubyforge.org/&quot;&gt;god&lt;/a&gt;. Both work great as process monitors, but they aren't built for some of our most important tasks. Enter &lt;a href=&quot;http://www.scoutapp.com&quot;&gt;Scout&lt;/a&gt;. In less then a week I've been able to port all of our ad-hoc monitors to Scout. See inside for more information.
&lt;/p&gt;

 &lt;p&gt;
 What makes Scout great is its configuration and plugin architecture. When a host runs it's monitors, it talks to Scout to download an executiong plan. This plan includes a list of plugins to execute. If a plugin has been added, the client will automatically download the required code. This makes it trivial to change monitors via the web and have your clients update automatically.
&lt;/p&gt;
&lt;p&gt;
A Scout plugin is just a simple Ruby class that either returns data, sends and alert or raises an error. It provides a very simple architecture for adding new plugins. This means that unlike with monit, you can create monitors that are tailored to your environment.
&lt;/p&gt;
&lt;p&gt;
  Let's look at an example. Weare heavy users of &lt;a href=&quot;http://github.com/advany/starling/tree/master&quot;&gt;Starling&lt;/a&gt;. We want to be able to monitor certain queues to make sure that our processors are running. Doing this under monit was a hack. I wrote a ruby program to read the queues and touch a watchdog file when the queue depth is below a threshold. I configured monit to raise an error when a file's timestamp was older than a set interval. This worked in practice, but it was complicated to set up and required modifying multiple pieces any time a change was required.
&lt;/p&gt;
&lt;p&gt;
 I was able to replace my cobbled together monitor in just a few minutes with Scout. Here is the full code to my new plugin:
&lt;/p&gt;
&lt;pre class=&quot;code_ruby&quot;&gt;
&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;MissingLibrary&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;StarlingMonitor&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Scout&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Plugin&lt;/span&gt;

  
  &lt;span class=&quot;ident&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:connection&lt;/span&gt;
  
  
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;setup_starling&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;begin&lt;/span&gt;
      &lt;span class=&quot;ident&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;starling&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;LoadError&lt;/span&gt;
      &lt;span class=&quot;keyword&quot;&gt;begin&lt;/span&gt;
        &lt;span class=&quot;ident&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;rubygems&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;ident&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;starling&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
      &lt;span class=&quot;keyword&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;LoadError&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;MissingLibrary&lt;/span&gt;
      &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;constant&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Starling&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{option(:host)}&lt;/span&gt;:&lt;span class=&quot;expr&quot;&gt;#{option(:port)}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;)&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
  
    
  
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;build_report&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;begin&lt;/span&gt;
      &lt;span class=&quot;ident&quot;&gt;setup_starling&lt;/span&gt;
      &lt;span class=&quot;ident&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;queue_name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;item_count&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;ident&quot;&gt;check_queue&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;queue_name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;item_count&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;should_check_queue?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;queue_name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;ident&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;attribute&quot;&gt;@report&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;rescue&lt;/span&gt;  &lt;span class=&quot;constant&quot;&gt;MissingLibrary&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;ident&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Could not load all required libraries&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;,&lt;/span&gt;
            &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;I failed to load the starling library. Please make sure it is installed.&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;ident&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Got unexpected error: &lt;span class=&quot;expr&quot;&gt;#{e}&lt;/span&gt; &lt;span class=&quot;expr&quot;&gt;#{e.class}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;should_check_queue?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:queue_re&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;regex&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{option(:queue_re)}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;check_queue&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;depth&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;ident&quot;&gt;q_depth&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;depth&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_i&lt;/span&gt;
    &lt;span class=&quot;attribute&quot;&gt;@report&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;attribute&quot;&gt;@report&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;q_depth&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;q_depth&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;option&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;symbol&quot;&gt;:max_depth&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_i&lt;/span&gt;
      &lt;span class=&quot;ident&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Max Queue Depth for &lt;span class=&quot;expr&quot;&gt;#{name}&lt;/span&gt; exceeded&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;expr&quot;&gt;#{q_depth}&lt;/span&gt; items is more than the max allowed &lt;span class=&quot;expr&quot;&gt;#{option(:max_depth)}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
  There's a bit of code, but it is pretty simple. Scout starts our plugin by calling the &lt;code&gt;build_report&lt;/code&gt; method. In &lt;code&gt;build_report&lt;/code&gt;, I do a little setup to make sure  can load all of the required libraries. After requiring in Starling, I make a connection. The plugin uses the &lt;code&gt;option&lt;/code&gt; method to pull configuration information from the environment. Scout provides a simple configuration interface that allows plugins to be configured via the web.
&lt;/p&gt;
&lt;p&gt;
 After loading up the environment, I walk the list of matching queues and send any necessary alerts. After looking at all queues, I return a report that maps the name of each queue to its depth. Along with providing an alert infrastructure, Scout also stores your reported data and can plot it on a graph. For example, here is a graph of the depth of our starling queues on one server:
&lt;/p&gt;
&lt;img src=&quot;/queue_depth.png&quot; /&gt;
&lt;p&gt;
 This simple interface to reporting and alerting is incredibly powerful. What makes it even more powerful is the ease at which you can install plugins. If you want to install my starling plugin, you can simply enter the URL to the Ruby file from github (http://github.com/mmangino/er-scout-plugins/tree/master%2Fstarling_monitor%2Fstarling_monitor.rb?raw=true)
&lt;/p&gt;
&lt;p&gt;
  As I said, I've moved all of my ad-hoc monitoring to Scout. I had to create a few plugins to make that work. They're available on Github at &lt;a href=&quot;http://github.com/mmangino/er-scout-plugins/tree/master&quot;&gt;http://github.com/mmangino/er-scout-plugins/tree/master&lt;/a&gt;. Feel free to fork them and submit enhancements!
&lt;/p&gt;
&lt;p&gt;
 So far, using Scout has been a real joy. That's something I never thought I would say about a monitoring tool.
&lt;/p&gt;</description>
          <pubDate>Wed, 06 Aug 2008 10:43:34 GMT</pubDate>
          <guid>http://www.elevatedrails.com/articles/2008/08/06/painless-monitoring-with-scout/</guid>
          <link>http://www.elevatedrails.com/articles/2008/08/06/painless-monitoring-with-scout/</link>
        </item>
    
    
  </channel>
</rss>

