<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
  <title>RailsTips by John Nunemaker</title>
  
  <link href="http://railstips.org/blog/" />
  <id>4b4217d4dabe9d774f000002</id>
  <updated>2010-03-04T11:41:42-0500</updated>
 
  
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/railstips" /><feedburner:info uri="railstips" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><geo:lat>41.650672</geo:lat><geo:long>-86.160028</geo:long><feedburner:emailServiceId>railstips</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry>
      <title>Canable: The Flesh Eating Permission System</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/HmcCaCM-IrQ/" />
      <id>4b897852dabe9d0c6f000004</id>
      <updated>2010-02-28T13:12:31-0500</updated>
      <published>2010-02-27T15:00:00-0500</published>
      <category term="gems" />
      <summary type="html">&lt;p&gt;In which I release another project, this one abstracted from a few recent apps.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;A while back I wrote about how to add simple permissions to your apps. Since then, I have worked on a few applications (&lt;a href="http://harmonyapp.com"&gt;Harmony&lt;/a&gt; among them) where I have taken that concept and expanded it. Yesterday, I decided that &lt;strong&gt;I had repeated myself enough times&lt;/strong&gt; (3) and that I should abstract the shared functionality of those apps into a gem. Thus, &lt;a href="http://github.com/jnunemaker/canable/"&gt;Canable&lt;/a&gt;, the flesh eating permission system,  was born.&lt;/p&gt;
&lt;h2&gt;Can&lt;/h2&gt;
&lt;p&gt;Canable does not actually implement any permissions for you (or actually eat flesh). Instead, it provides you with all the helpers and then (&lt;strong&gt;gasp&lt;/strong&gt;) you have to do the work. The idea &lt;strong&gt;centers around running all permissions through current_user&lt;/strong&gt;. Anytime you check if a user can do something you use a can method:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;user.can_create?(article)
user.can_update?(article)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Able&lt;/h2&gt;
&lt;p&gt;Instead of having a big case statement in those can methods for each different type of object, I use the &lt;a href="http://en.wikipedia.org/wiki/Strategy_pattern#Ruby"&gt;strategy pattern&lt;/a&gt; to just ask the object if the user has permission to do the action. This is done by having a matching &amp;#8220;able&amp;#8221; method to the &amp;#8220;can&amp;#8221; method, thus canable.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Item
  def updatable_by?(user)
    creator == user
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code, for example, makes it so that only the creator of an item can update it. Obviously, you can get more in depth from there. By default, I add the following can and able methods:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:view =&amp;gt; :viewable
:create =&amp;gt; :creatable
:update =&amp;gt; :updatable
:destroy =&amp;gt; :destroyable&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Custom Actions&lt;/h2&gt;
&lt;p&gt;If you need permissions for actions other than the defaults, you can add your own quite easily:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;Canable.add(:publish, :publishable)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href="http://github.com/jnunemaker/canable/"&gt;readme&lt;/a&gt; over on Github has far more details, but I figured I would at least cover it here a bit. It might seem a bit weird at first, but once you start rolling with it, it makes for a pretty easy to implement and understand permission system.&lt;/p&gt;
&lt;p&gt;The really funny part is that it is only like 80 lines of code, as most of the methods are dynamically generated. I am perfectly fine if I am the only one who uses this and finds it helpful, but you never know, so feel free to &lt;a href="http://rubygems.org/gems/canable"&gt;install it as a gem&lt;/a&gt; or &lt;a href="http://github.com/jnunemaker/canable/"&gt;fork it on github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: No permissions were harmed in the making of this gem.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=HmcCaCM-IrQ:8atxeZ-cWss:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=HmcCaCM-IrQ:8atxeZ-cWss:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=HmcCaCM-IrQ:8atxeZ-cWss:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/HmcCaCM-IrQ" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/02/27/canable-the-flesh-eating-permission-system/</feedburner:origLink></entry>
  
    <entry>
      <title>MongoMapper 0.7: Identity Map</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/ELIAJ0ZNSKk/" />
      <id>4b8199b7dabe9d1c1c000001</id>
      <updated>2010-02-21T17:29:44-0500</updated>
      <published>2010-02-21T17:00:00-0500</published>
      <category term="mongomapper" /><category term="patterns" />
      <summary type="html">&lt;p&gt;In which I explain that a pattern learned is a query saved.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;While it isn&amp;#8217;t quite ready to shove down everyone&amp;#8217;s throats, IdentityMap is a hot new plugin I have been working on, directly due to a need that arose in   &lt;a href="http://harmonyapp.com"&gt;Harmony&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the things I started to notice in Harmony right away was an epic amount of queries, 90% of which were simple id lookups repeated over and over. I remembered hearing about the &lt;a href="http://www.martinfowler.com/eaaCatalog/identityMap.html"&gt;identity map  pattern&lt;/a&gt; before and figured I would research it a bit.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;An old proverb says that a man with two watches never knows what time it is. If two watches are confusing, you can get in an even bigger mess with loading objects from a database.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So true. Now that I had the &lt;a href="http://railstips.org/blog/archives/2010/02/21/mongomapper-07-plugins/"&gt;plugin system in place&lt;/a&gt;, I knew exactly how I would go about integrating IM (identity map) with MM. The great thing about the process was that I had a need from a real application. &lt;strong&gt;Over half of the IM plugin was created while vendored in Harmony&lt;/strong&gt;. Several test cases that are currently in MM are directly due to weird bugs that we experienced in Harmony. &lt;strong&gt;I think it is really important to note that MM features grow from real applications, not theoretical fun&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Foo is Foo is Foo&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The first thing I had to get working was to make sure that each document in the database equated to one object in Ruby&lt;/strong&gt;. This means whether I call Item.find(id) or item.parent, if they are both loading the same document, they need to be the same object id in Ruby.&lt;/p&gt;
&lt;p&gt;The easiest way I could think to do this was to have one method I always call whenever loading a document from the database. The obvious method name was load. Now, whenever an document is found in the database, &lt;a href="http://github.com/jnunemaker/mongomapper/blob/master/lib/mongo_mapper/plugins/keys.rb#L50-57"&gt;I call load&lt;/a&gt; instead of new. This seems really insignificant, but down the rabbit trail it has significant implications.&lt;/p&gt;
&lt;h3&gt;Taking Advantage of Method Lookups and Super&lt;/h3&gt;
&lt;p&gt;Nothing fancy happens in load, but where it gets cool is in the IM plugin. Because every document coming from the database runs through load, all the IM plugin has to do is override load and &lt;strong&gt;ensure that the same document in the database is the same object in Ruby&lt;/strong&gt;. It does this by storing each document in the map when loaded and then checking each document loaded against that store. &lt;strong&gt;If the document is already there, it returns the object for that document instead of creating a new one&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Each class gets an IM (just a plain old hash for now). Because each id is unique, the id is the key in the IM hash and the value is the Ruby object. Because of how &lt;a href="http://railstips.org/blog/archives/2009/08/20/lookin-on-up-to-the-east-side/"&gt;method lookups work in Ruby&lt;/a&gt;, all that I do is include the IM plugin after the plugin that defines load and I can access the original load method using super. Below is the load method in the IM plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;def load(attrs)
  document = identity_map[attrs['_id']]

  if document.nil? || identity_map_off?
    document = super
    identity_map[document._id] = document if identity_map_on?
  end

  document
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without even knowing what the other methods do, you can get a feel for what is going on. First, I set document to where I assume it will be in the identity map for the class. If it is nil, that means it doesn&amp;#8217;t exist, so I call super which calls the normal load method, and returns a new instance of the class based on the document from the database. Then, if the identity map is turned on, I add it to the map by setting the id as the key and the object as the value.&lt;/p&gt;
&lt;p&gt;At the end, I return the document no matter what has happened before. With this tiny bit of code (and a few more lines for setting up hash store), &lt;strong&gt;I ensure that no matter how or when a document is queried from the database, it is always the same object in memory&lt;/strong&gt;. Soooo sweet. Like I said, huge ramifications.&lt;/p&gt;
&lt;h2&gt;Identity Map Lookups Zap Simple Queries&lt;/h2&gt;
&lt;p&gt;Once I had this part done, all that was left was to zap the actual queries to the database if the document has already been loaded. I refactored the MM internal methods for finding documents to find_one and find_many. This means whenever you do any kind of find query in MM, eventually it hits one of those two methods.&lt;/p&gt;
&lt;p&gt;Taking the same approach as load, if you are doing everything through one or two methods, &lt;strong&gt;all you have to do to change those methods is redefine them in the plugin&lt;/strong&gt; to behave differently. &lt;strong&gt;The best part is you still have access to the originals using super&lt;/strong&gt;. find_one in the IM plugin looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;def find_one(options={})
  criteria, query_options = to_query(options)

  if simple_find?(criteria) &amp;amp;&amp;amp; identity_map.key?(criteria[:_id])
    identity_map[criteria[:_id]]
  else
    super.tap do |document|
      remove_documents_from_map(document) if selecting_fields?(query_options)
    end
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;simple_find? returns true if doing a query only by _id or _id and _type (which happens when using single collection inheritance). This method addition means we can return the document from the identity map without doing a query, if it has already been loaded.&lt;/p&gt;
&lt;h2&gt;A Simple, Yet Awesome Example&lt;/h2&gt;
&lt;p&gt;I learn best with code examples, so here is one for you. A simple item class for creating an even more simple tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, :logger =&amp;gt; Logger.new(STDOUT))
MongoMapper.database = 'testing'

class Item
  include MongoMapper::Document
  
  key :title, String
  key :parent_id, ObjectId
  
  belongs_to :parent, :class_name =&amp;gt; 'Item'
end

root = Item.create(:title =&amp;gt; 'Root')
child = Item.create(:title =&amp;gt; 'Child', :parent =&amp;gt; root)
grand_child = Item.create(:title =&amp;gt; 'Grand Child', :parent =&amp;gt; child)

puts root.equal?(child.parent) # false
puts child.equal?(grand_child.parent) # false&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;If you run this code, you get false and false as the output, along with a few queries to find the parent of child and grand_child&lt;/strong&gt;. Below is some sample output:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;MONGODB admin.$cmd.find({:ismaster=&amp;gt;1}, {}).limit(-1)
MONGODB db.items.update({:_id=&amp;gt;4b81a114d072c40c3f000001}, {"title"=&amp;gt;"Root", "_id"=&amp;gt;4b81a114d072c40c3f000001, "parent_id"=&amp;gt;nil})
MONGODB db.items.update({:_id=&amp;gt;4b81a114d072c40c3f000002}, {"title"=&amp;gt;"Child", "_id"=&amp;gt;4b81a114d072c40c3f000002, "parent_id"=&amp;gt;4b81a114d072c40c3f000001})
MONGODB db.items.update({:_id=&amp;gt;4b81a114d072c40c3f000003}, {"title"=&amp;gt;"Grand Child", "_id"=&amp;gt;4b81a114d072c40c3f000003, "parent_id"=&amp;gt;4b81a114d072c40c3f000002})
MONGODB testing.items.find({:_id=&amp;gt;4b81a114d072c40c3f000001}, {}).limit(-1)
false
MONGODB testing.items.find({:_id=&amp;gt;4b81a114d072c40c3f000002}, {}).limit(-1)
false&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out the same script, with the addition of the identity map plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, :logger =&amp;gt; Logger.new(STDOUT))
MongoMapper.database = 'testing'

class Item
  include MongoMapper::Document
  plugin MongoMapper::Plugins::IdentityMap
  
  key :title, String
  key :parent_id, ObjectId
  
  belongs_to :parent, :class_name =&amp;gt; 'Item'
end

root = Item.create(:title =&amp;gt; 'Root')
child = Item.create(:title =&amp;gt; 'Child', :parent =&amp;gt; root)
grand_child = Item.create(:title =&amp;gt; 'Grand Child', :parent =&amp;gt; child)

puts root.equal?(child.parent) # true
puts child.equal?(grand_child.parent) # true&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note that we get true for both&lt;/strong&gt;. Also, &lt;strong&gt;we get no queries for the parents&lt;/strong&gt; of child and grand_child, as they are already in the identity map. The output looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;MONGODB admin.$cmd.find({:ismaster=&amp;gt;1}, {}).limit(-1)
MONGODB db.items.update({:_id=&amp;gt;4b81a0c9d072c40c2c000001}, {"title"=&amp;gt;"Root", "_id"=&amp;gt;4b81a0c9d072c40c2c000001, "parent_id"=&amp;gt;nil})
MONGODB db.items.update({:_id=&amp;gt;4b81a0c9d072c40c2c000002}, {"title"=&amp;gt;"Child", "_id"=&amp;gt;4b81a0c9d072c40c2c000002, "parent_id"=&amp;gt;4b81a0c9d072c40c2c000001})
MONGODB db.items.update({:_id=&amp;gt;4b81a0c9d072c40c2c000003}, {"title"=&amp;gt;"Grand Child", "_id"=&amp;gt;4b81a0c9d072c40c2c000003, "parent_id"=&amp;gt;4b81a0c9d072c40c2c000002})
true
true&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Obviously, this is a really small and simple example, but with one small addition, we saved a few queries. Imagine how much of a difference it makes in a big application making lots of requests. &lt;strong&gt;The thing I am most amazed at is how much punch this plugin adds compared to the amount of code in the implementation&lt;/strong&gt;. As of 0.7, the &lt;a href="http://github.com/jnunemaker/mongomapper/blob/master/lib/mongo_mapper/plugins/identity_map.rb"&gt;identity map plugin&lt;/a&gt; is only 122 lines of code.&lt;/p&gt;
&lt;h2&gt;Using the IM Plugin with 0.7&lt;/h2&gt;
&lt;p&gt;The IM plugin is by no means feature complete, so I am not automatically including it in every Document yet. I will say that what I have added is production ready and we have been using it in Harmony for over a month now. You can use it on a model by model basis like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Foo
  include MongoMapper::Document
  plugin MongoMapper::Plugins::IdentityMap
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or you can turn it on for all documents by dropping this in an initializer (stolen directly from Harmony):&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;module IdentityMapAddition
  def self.included(model)
    model.plugin MongoMapper::Plugins::IdentityMap
  end
end

MongoMapper::Document.append_inclusions(IdentityMapAddition)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Correct, Beautiful, Fast&lt;/h2&gt;
&lt;p&gt;The IM plugin, for me, is a great example of &lt;a href="http://railstips.org/blog/archives/2010/01/26/correct-beautiful-fast-in-that-order/"&gt;Correct, Beautiful, Fast&lt;/a&gt;. First, we built Harmony in a way that worked and was easy to read (correct and beautiful). Then, when we needed to make it fast, all we had to do was override the implementation in a few spots and we cut our queries in half (or more) over night. &lt;strong&gt;It is far easier to find where you need to optimize when your code is correct and beautiful&lt;/strong&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=ELIAJ0ZNSKk:I78AuS9TzBA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=ELIAJ0ZNSKk:I78AuS9TzBA:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=ELIAJ0ZNSKk:I78AuS9TzBA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/ELIAJ0ZNSKk" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/02/21/mongomapper-07-identity-map/</feedburner:origLink></entry>
  
    <entry>
      <title>MongoMapper 0.7: Plugins</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/-a7F2rsSXuE/" />
      <id>4b81951cdabe9d1c18000002</id>
      <updated>2010-02-22T08:31:32-0500</updated>
      <published>2010-02-21T15:00:00-0500</published>
      <category term="mongomapper" />
      <summary type="html">&lt;p&gt;In which I show off and explain how to enhance MongoMapper on your own.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;Whew, been a bit of a whirlwind around here. Lots to do lately, so I have been slow talking about MongoMapper&amp;#8217;s 0.7 release from a week ago. I&amp;#8217;ll take a crack now. Let&amp;#8217;s start with how to extend MongoMapper with plugins and some other fun functionality.&lt;/p&gt;
&lt;h2&gt;Separation Is Good&lt;/h2&gt;
&lt;p&gt;Originally, because Document and EmbeddedDocument shared so much, in my head it made sense to just include EmbeddedDocument in Document. &lt;strong&gt;What this led to was confusion in the code&lt;/strong&gt;. Lots of things sharing, some that should not be and some that should. Yes, I wanted to share the functionality between the two modules, but including one in the other is not the best way.&lt;/p&gt;
&lt;p&gt;So I sat down and thought about it. I figured the best way would be to split out each bit of functionality into separate modules and then include each of those modules that were shared into both Document and EmbeddedDocument. With this in mind, I went to down. I broke all the refactoring rules and just started ripping stuff out and going to down. What I ended up with is a few tests that, despite all odds, I could not get to pass. &lt;strong&gt;My problem was that I took on too much at once&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Thankfully, several hours in, I realized what I had done and stopped. I blew away all my changes and started over. &lt;strong&gt;This time, I focused on small changes and made sure to run all the tests after each change&lt;/strong&gt;. Attacking the code base with this mindset allowed me to slowly and gently remove bits and pieces into modules and begin the process of separating the conjoined twins known as Document and EmbeddedDocument.&lt;/p&gt;
&lt;h2&gt;Plugins Are Born&lt;/h2&gt;
&lt;p&gt;As I was doing this, I realized that I was doing the same things over and over. Including a module here, extending one there and occasionally dropping down to class_eval when brute force was needed. That is when it hit me that some sort of system that abstracted this process out would be nice. Thus was born MongoMapper::Plugins. MM::Plugins is the tiniest bit of code, but &lt;strong&gt;it led to sweeping changes that make MM far more readable and separated&lt;/strong&gt;. It is so tiny, in fact, that I will paste it below.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;module MongoMapper
  module Plugins
    def plugins
      @plugins ||= []
    end

    def plugin(mod)
      extend mod::ClassMethods     if mod.const_defined?(:ClassMethods)
      include mod::InstanceMethods if mod.const_defined?(:InstanceMethods)
      mod.configure(self)          if mod.respond_to?(:configure)
      plugins &amp;lt;&amp;lt; mod
    end
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This small addition made separating out all the shared functionality really easy to complete and really easy to read. For example, when you create a MongoMapper::Document, you include that module. This calls the Ruby included hook, which now looks like this in Document:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;def self.included(model)
  model.class_eval do
    include InstanceMethods
    extend  Support::Find
    extend  ClassMethods
    extend  Plugins

    plugin Plugins::Associations
    plugin Plugins::Clone
    plugin Plugins::Descendants
    plugin Plugins::Equality
    plugin Plugins::Inspect
    plugin Plugins::Keys
    plugin Plugins::Dirty # for now dirty needs to be after keys
    plugin Plugins::Logger
    plugin Plugins::Pagination
    plugin Plugins::Protected
    plugin Plugins::Rails
    plugin Plugins::Serialization
    plugin Plugins::Validations
    plugin Plugins::Callbacks # for now callbacks needs to be after validations

    extend Plugins::Validations::DocumentMacros
  end

  super
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, all I do is declare a bunch of plugins. It is really easy to see what is going on without even looking at those plugins. &lt;strong&gt;This little change was huge mentally for me&lt;/strong&gt;. It has made the project a joy to maintain once again, as I do not have to think about how to implement each new bit of functionality. The new feature just goes into a new or existing plugin.&lt;/p&gt;
&lt;h2&gt;An Example Plugin&lt;/h2&gt;
&lt;p&gt;So what does a plugin look like? Below is the logger plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;module MongoMapper
  module Plugins
    module Logger
      module ClassMethods
        def logger
          MongoMapper.logger
        end
      end

      module InstanceMethods
        def logger
          self.class.logger
        end
      end
    end
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This plugin adds simple methods for accessing MongoMapper&amp;#8217;s logging functionality on any Document or EmbeddedDocument. It means if you have a Foo model, you get Foo.logger and Foo.new.logger. Very simple and very readable. Most of the &lt;a href="http://github.com/jnunemaker/mongomapper/tree/master/lib/mongo_mapper/plugins"&gt;plugins&lt;/a&gt; are tiny like this.&lt;/p&gt;
&lt;h2&gt;Extending All Models&lt;/h2&gt;
&lt;p&gt;The other cool thing, an idea taken from DataMapper, is append_inclusions and append_extensions. These two methods are added to both Document and EmbeddedDocument and &lt;strong&gt;allow you to add any modules to be included or extended on every Document or EmbeddedDocument&lt;/strong&gt;. Examples work best for me, so check this out:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Item
  include MongoMapper::Document
end

module Fooer
  def foo
    puts 'Foo'
  end
end

MongoMapper::Document.append_extensions(Fooer)

class Sandle
  include MongoMapper::Document
end

Item.foo # puts 'Foo'
Sandle.foo # puts 'Foo'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that it does not matter whether you append_extensions before or after the model is defined. Either way, anything that includes MongoMapper::Document gets the extension. &lt;strong&gt;Behind the scenes, this calls &lt;code class="ruby"&gt;extend Fooer&lt;/code&gt; on each model&lt;/strong&gt;. append_inclusions behaves the same except that instead of calling extend with the module, it calls include. See below:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Item
  include MongoMapper::Document
end

module Fooer
  def foo
    puts 'Foo'
  end
end

MongoMapper::Document.append_inclusions(Fooer)

class Sandle
  include MongoMapper::Document
end

Item.new.foo # puts 'Foo'
Sandle.new.foo # puts 'Foo'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note on this example that we are calling new and that foo is now an instance method instead of a class method (&lt;a href="http://railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/"&gt;include vs extend&lt;/a&gt;). Also, append inclusions and extensions are added to EmbeddedDocument so you can apply functionality specfically to it if needed.&lt;/p&gt;
&lt;h2&gt;Adding A Plugin To All Documents&lt;/h2&gt;
&lt;p&gt;The cool thing about this is if you want to add a plugin to every Document, you can just do something like this, instead of manually calling &lt;code&gt;plugin&lt;/code&gt; in each model:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Item
  include MongoMapper::Document
end

module FooPlugin
  module ClassMethods
    def foo
      puts 'Foo'
    end
  end

  module InstanceMethods
    def foo
      self.class.foo
    end
  end
end

module FooPluginAddition
  def self.included(model)
    model.plugin FooPlugin
  end
end
MongoMapper::Document.append_inclusions(FooPluginAddition)

class Sandle
  include MongoMapper::Document
end

Item.foo        # puts 'Foo'
Sandle.foo      # puts 'Foo'
Item.new.foo    # puts 'Foo'
Sandle.new.foo  # puts 'Foo'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We create the FooPlugin, then we create a module that uses Ruby&amp;#8217;s included hook to call plugin on the model. Because append_inclusions is called on each Document, &lt;strong&gt;all our models get the new plugin&lt;/strong&gt;. Fun stuff.&lt;/p&gt;
&lt;p&gt;Thanks to plugins and the append extensions/inclusions stuff there is a standardized way of extending MongoMapper models. In fact, people have already hopped on the bandwagon and started adding features as plugins instead of directly into Document or EmbeddedDocument. The first such plugin to be merged in is the &lt;a href="http://github.com/jnunemaker/mongomapper/blob/master/lib/mongo_mapper/plugins/protected.rb"&gt;protected attributes plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These additions are huge for MongoMapper in general, but I have found them handy for organizing code on &lt;a href="http://harmonyapp.com"&gt;Harmony&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;There is still more work to do in regards to organizing the MM code base, as some plugins are almost catch alls, but this has really cleaned things up in the code, especially for people new to MM.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=-a7F2rsSXuE:bhU_WgNB9kw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=-a7F2rsSXuE:bhU_WgNB9kw:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=-a7F2rsSXuE:bhU_WgNB9kw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/-a7F2rsSXuE" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/02/21/mongomapper-07-plugins/</feedburner:origLink></entry>
  
    <entry>
      <title>MongoTips: All Mongo, All The Time</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/qkQ6l78IZ-0/" />
      <id>4b7c4d6cdabe9d2b6f000002</id>
      <updated>2010-02-17T15:17:52-0500</updated>
      <published>2010-02-17T15:00:00-0500</published>
      <category term="mongodb" /><category term="site news" />
      <summary type="html">&lt;p&gt;In which I announce a new blog dedicated to getting everyone on MongoDB.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;So last night I realized that I really love MongoDB. I love it so much that I want everyone to be using it whenever it makes sense (which is quite often). I thought about what I could do to help get the word out more and starting a new site made the most sense (instead of flooding RailsTips with Mongo stuff).&lt;/p&gt;
&lt;p&gt;&lt;a href="http://mongotips.com"&gt;MongoTips&lt;/a&gt; is a new blog dedicated to MongoDB news and information. I&amp;#8217;ll be posting things there that we have learned building Harmony and anything I can find from the community.&lt;/p&gt;
&lt;p&gt;If you are interested in Mongo, be sure to &lt;a href="http://feeds.feedburner.com/mongotips"&gt;subscribe to the feed&lt;/a&gt; or &lt;a href="http://mongotips.com"&gt;follow on twitter&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The Process&lt;/h2&gt;
&lt;p&gt;What I love about the process of making this site is how smooth it was with Harmony. I registered the domain, setup the &lt;span class="caps"&gt;DNS&lt;/span&gt;, added the site in Harmony, duplicated the RailsTips theme and a few minutes later the site was ready to go. All that was needed was a few posts and it was ready to launch.&lt;/p&gt;
&lt;p&gt;That is really what I want Harmony to bring to the table. I want it to take away all the pain so all you are left with is authoring your site. Lovely.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=qkQ6l78IZ-0:DVVPOGLASk8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=qkQ6l78IZ-0:DVVPOGLASk8:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=qkQ6l78IZ-0:DVVPOGLASk8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/qkQ6l78IZ-0" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/02/17/mongotips-all-mongo-all-the-time/</feedburner:origLink></entry>
  
    <entry>
      <title>Great Lakes Ruby Bash</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/3seQJicx_io/" />
      <id>4b7c40cddabe9d247b000007</id>
      <updated>2010-02-17T14:28:38-0500</updated>
      <published>2010-02-17T14:00:00-0500</published>
      <category term="speaking" /><category term="conferences" />
      <summary type="html">&lt;p&gt;In which I mention a conference you should definitely go to.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;&lt;img src="http://static.railstips.org/images/articles/glrb.jpg" alt="Logo" class="image right" /&gt;&lt;br /&gt;
April 17th of this year, a lot of good people I know are throwing a conference for the Great Lakes region at Michigan State University (Go Irish!). They currently have a &lt;a href="http://greatlakesrubybash.com/"&gt;call for proposals&lt;/a&gt; open until February 28th.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I&amp;#8217;m already on the docket&lt;/strong&gt; and I have something awesome planned, so whether you want to talk or just attend, I would highly recommend keeping this conference on your radar.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=3seQJicx_io:qTg1d-cZTV8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=3seQJicx_io:qTg1d-cZTV8:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=3seQJicx_io:qTg1d-cZTV8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/3seQJicx_io" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/02/17/great-lakes-ruby-bash-1/</feedburner:origLink></entry>
  
    <entry>
      <title>Correct, Beautiful, Fast (In That Order)</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/NIWavJjp2hs/" />
      <id>4b5d1b13dabe9d2425000009</id>
      <updated>2010-03-04T11:41:42-0500</updated>
      <published>2010-01-26T13:00:00-0500</published>
      <category term="refactoring" />
      <summary type="html">&lt;p&gt;In which I post some thoughts on the steps to beautiful code.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;One of the books I am occasionally grazing on is &lt;a href="http://www.amazon.com/Beautiful-Code-Leading-Programmers-Practice/dp/0596510047"&gt;Beautiful Code&lt;/a&gt;. The title of this post is the title of Chapter 6. I&amp;#8217;ll be honest, I only skimmed the chapter as one can only digest so much Java, but the title is spot on.&lt;/p&gt;
&lt;h2&gt;Step 1: Correct (With Tests)&lt;/h2&gt;
&lt;p&gt;I have been thinking exactly this as of late, though I will admit in no way as succinctly. First, you get it working. It does not matter if it is dirty. Rather, &lt;strong&gt;what matters is that it functions and is well tested&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If it works and it is not tested, you can never make it beautiful with confidence, so testing is very important to this step.&lt;/p&gt;
&lt;h2&gt;Step 2: Beautiful&lt;/h2&gt;
&lt;p&gt;Once your program is working and testing, make it beautiful. Leave the tests alone and head back into the code. &lt;strong&gt;Branch out and try new idioms&lt;/strong&gt; that you have not tried before. Ensure that someone with no prior knowledge could jump in and know what is going on (without ridiculous comments all over).&lt;/p&gt;
&lt;p&gt;Note that this step is not optional. Once your code is functional and well tested, &lt;strong&gt;you still have an obligation to make it beautiful&lt;/strong&gt;. I think a lot of people skip this step. They think once it works and is tested, that is the end. Wrong.&lt;/p&gt;
&lt;p&gt;Not only is this step the most fun, it also forces you to think through the code more. I often find edge cases during this step that I would never find if I stopped and correct and tested.&lt;/p&gt;
&lt;h2&gt;Step 3: Fast (optional)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;If you pass these first two steps and you run into slowness&lt;/strong&gt; at an unacceptable level, make it fast. The author puts it best in the summary at the end of the chapter, so I will quote them:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If there&amp;#8217;s a moral to this story, it is this: do not let performance considerations stop you from doing what is right. &lt;strong&gt;You can always make the code faster with a little cleverness. You can rarely recover so easily from a bad design&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So true. I have never focused on speed in MongoMapper. No benchmarks to wow the noobs. No dramatic statements about performance. I have focused on cleaning up the code and making it easier for myself to maintain and others to contribute.&lt;/p&gt;
&lt;p&gt;Not long ago, one of those contributors sent me a tiny patch (~30 lines) that improved overall performance by at least two, maybe three times what it was in 0.6.10. &lt;strong&gt;I do not think that it would have been that simple to to find and fix the performance issues if the code had been a mess&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Correct&lt;/strong&gt;: make it work and test it.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Beautiful&lt;/strong&gt;: refactor original code in a manner that others (and yourself down the road) can easily understand.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt;: often the first two steps leave code fast enough, but if they do not, make it faster.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=NIWavJjp2hs:4IImDKQgY5Q:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=NIWavJjp2hs:4IImDKQgY5Q:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=NIWavJjp2hs:4IImDKQgY5Q:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/NIWavJjp2hs" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/01/26/correct-beautiful-fast-in-that-order/</feedburner:origLink></entry>
  
    <entry>
      <title>Just In Time, Not Just In Case</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/CLNLcXB_1lU/" />
      <id>4b4e3ab7dabe9d1091000003</id>
      <updated>2010-01-24T23:32:55-0500</updated>
      <published>2010-01-24T01:30:00-0500</published>
      <category term="refactoring" />
      <summary type="html">&lt;p&gt;In which I discuss coding only for what is needed today, not what &lt;em&gt;might&lt;/em&gt; be needed down the road.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;Something that has finally become a habit for me is adding code when it is needed, not in case it is needed. Often times we &amp;#8220;think&amp;#8221; we are going to need something, so we add the code to support it. &lt;strong&gt;What happens most often with that code is it sits and rots&lt;/strong&gt;. It adds bloat and weight to your program that is not needed.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;I like to see examples first, so I will start with one. In Harmony, sites have users. I had a method in the site model named add_user.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Site
  def add_user(user)
    user_id = user.is_a?(User) ? user.id : user
    # add user code here, removed for clarity
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some might look at that and think it is a perfectly fine method, but I would disagree. A quick search through my code &lt;strong&gt;revealed that I was never passing a user id&lt;/strong&gt;. Every time I called the method, I passed in a user as an argument. That was the first smell.&lt;/p&gt;
&lt;p&gt;The second, which may be for another article is that I used &lt;code&gt;is_a?&lt;/code&gt;. We are programming in Ruby folks. It is &lt;strong&gt;always better to duck type&lt;/strong&gt; and ask &lt;code&gt;respond_to?&lt;/code&gt; than &lt;code&gt;is_a?&lt;/code&gt;. The difference is asking an object what it is, instead of asking an object what it can do.&lt;/p&gt;
&lt;p&gt;Having &lt;code&gt;add_user&lt;/code&gt; take either a user or a user id &lt;strong&gt;meant twice as many tests and an extra line of code&lt;/strong&gt;. I killed the &lt;code&gt;is_a?&lt;/code&gt; ternary line and the accompanying tests and went on my merry way.&lt;/p&gt;
&lt;h2&gt;How to Avoid Just in Case&lt;/h2&gt;
&lt;p&gt;The best way that I have found to avoid &amp;#8220;just in case&amp;#8221; &lt;strong&gt;is to test first&lt;/strong&gt;. When I write my tests first and start with the most basic test I can think of and go from there, my code tends to only be &lt;strong&gt;as complex as the test requires&lt;/strong&gt;. Whenever I begin with program code instead of test code, I end up with &amp;#8220;just in case&amp;#8221; features like the one above.&lt;/p&gt;
&lt;p&gt;This might seem small as it was only one line and it was well tested, but this was only one small example. &lt;strong&gt;Cases like this multiply&lt;/strong&gt; as the program grows and &lt;strong&gt;the &amp;#8220;real&amp;#8221; code begins to get obfuscated&lt;/strong&gt; behind the cases that &lt;em&gt;might&lt;/em&gt; happen someday.&lt;/p&gt;
&lt;p&gt;The moral of the story is a line removed is &lt;strong&gt;a line that will never have bugs down the road&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;*To give credit where I believe credit is due, I am 99% positive that &lt;a href="http://weblog.jamisbuck.org/"&gt;Jamis Buck&lt;/a&gt; said &amp;#8220;Just in time, not just in case.&amp;#8221; at a conference. Could not find a reference though.&lt;/small&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=CLNLcXB_1lU:3PUJrdEDCyI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=CLNLcXB_1lU:3PUJrdEDCyI:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=CLNLcXB_1lU:3PUJrdEDCyI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/CLNLcXB_1lU" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/01/24/just-in-time-not-just-in-case/</feedburner:origLink></entry>
  
    <entry>
      <title>Multiple Domain Page Caching</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/AbNAyoBfGQk/" />
      <id>4b57ccf4dabe9d1163000003</id>
      <updated>2010-02-02T11:48:37-0500</updated>
      <published>2010-01-22T11:00:00-0500</published>
      <category term="caching" /><category term="harmony" />
      <summary type="html">&lt;p&gt;In which I show the apache/rails trickery we do to support full page caching in Harmony.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;The other day Brandon Wright emailed me about &lt;a href="http://twitter.com/jnunemaker/status/7762829630"&gt;the following tweet&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Just deployed full page caching on Harmony. Our log file stopped spinning by which made me happy and sad.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2&gt;Routing&lt;/h2&gt;
&lt;p&gt;It might seem like black magic, but it isn&amp;#8217;t all that hard. The front side for Harmony is not the same as a typical Rails app as we have multiple domains pointed at Harmony and the paths are not known up front so they don&amp;#8217;t go in the routes file. In order to get everything headed to a controller, the last route in our file is this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;map.dispatch '*path', :controller =&amp;gt; 'the', :action =&amp;gt; 'dispatch'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses &lt;a href="http://guides.rubyonrails.org/routing.html#route-globbing"&gt;Rails route globbing&lt;/a&gt; to send every path to an action named dispatch in a controller dubiously named &amp;#8220;the&amp;#8221; (because it made us laugh). From there, we determine if it we can find the site and if the site has an item (page, link, blog, post, etc.) that matches the path.&lt;/p&gt;
&lt;h2&gt;Caching&lt;/h2&gt;
&lt;p&gt;Somewhere down the rabbit hole we render that item based on it&amp;#8217;s liquid template, immediately after which we call something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;cache_item(@item, contents)

# which looks kind of like this
def cache_item(item, contents)
  # gone for brevity
  
  FileUtils.mkdir_p(File.dirname(item.page_cache_path))
  File.open(item.page_cache_path, 'w+') { |f| f.puts(contents) }
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;small&gt;&amp;#42;We could have used caches_page in Rails, but we are already using that without including the http host for asset and theme file caching, so it was easier to just roll our own.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;All cache_item does is ensure that the directory exists and then write the contents of what we are about to send back to the browser into a file. Really nothing fancy. So what does item.page_cache_path look like? For a site like railstips.org and a path of /dude/, we end up with the following cache path:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#{RAILS_ROOT}/public/cache/railstips.org/dude/index.html&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note the use of the domain in the cache path&lt;/strong&gt;. Since we have that, we can use apache rewrites along with conditions to &lt;strong&gt;tell apache to check if a cached file exists based on the host&lt;/strong&gt;. If it does, we server that file and if it doesn&amp;#8217;t, we just hit rails, cache the file, and return the response. We use &lt;a href="http://github.com/railsmachine/moonshine"&gt;Moonshine&lt;/a&gt; for our deployments so all we need to do is set the Passenger page cache directory like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:passenger:
  :page_cache_directory: '/cache/%{HTTP_HOST}'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we deploy, this sets up the following Apache rewrite rules:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Rewrite to check for Rails non-html cached pages (i.e. xml, json, atom, etc)
RewriteCond  %{THE_REQUEST} ^(GET|HEAD)
RewriteCond  %{DOCUMENT_ROOT}/cache/%{HTTP_HOST}%{REQUEST_URI} -f
RewriteRule  ^(.*)$ /cache/%{HTTP_HOST}$1 [QSA,L]

# Rewrite to check for Rails cached html page
RewriteCond  %{THE_REQUEST} ^(GET|HEAD)
RewriteCond  %{DOCUMENT_ROOT}/cache/%{HTTP_HOST}%{REQUEST_URI}index.html -f
RewriteRule  ^(.*)$ /cache/%{HTTP_HOST}$1index.html [QSA,L]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that in the RewriteRule, we include the HTTP_HOST, which when visiting railstips.org, would be railstips.org.&lt;/p&gt;
&lt;h2&gt;One &lt;span class="caps"&gt;URL&lt;/span&gt; to Rule Them All&lt;/h2&gt;
&lt;p&gt;The key to this being effective is only having one true url for each page. We do this right now by redirecting www to no-www and ensuring that each page has a trailing slash. First, no-www.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# no www
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ http://%1$1 [R=301,L]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we ensure that there is always a trailing slash when needed. This means that /foo redirects to /foo/ and foo.json just stays as foo.json.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RewriteCond  %{THE_REQUEST} ^(GET|HEAD)
RewriteCond %{REQUEST_URI} !^/admin/
RewriteRule ^(.*/[^/\.]+)$ $1/ [R]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensuring that each page has one &lt;span class="caps"&gt;URL&lt;/span&gt; is better for search engines and analytics. You don&amp;#8217;t end up with split page rank for the same page (with and without slash) and the same thing is true for pageviews.&lt;/p&gt;
&lt;h2&gt;Cache Clearing&lt;/h2&gt;
&lt;p&gt;Now that I&amp;#8217;ve explained a bit how we do the caching, I&amp;#8217;ll mention quickly how we clear it. As they say, cache expiration and naming are the two hardest things to do in programming. We opted for the most simple solution that would work for now.&lt;/p&gt;
&lt;p&gt;I made a simple site cache clearer module that I include in any model that can affect a site on the front side. It looks something like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module SiteCacheClearer
  def self.included(model)
    model.after_save    :clear_item_cache
    model.after_destroy :clear_item_cache
  end
  
  def clear_item_cache
    site.clear_item_cache if site.present?
  end
end

# To use
class Item
  include MongoMapper::Document
  include SiteCacheClearer
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All it does is remove the entire site&amp;#8217;s cache whenever the model is updated or destroyed. Like I said, nothing fancy. Doesn&amp;#8217;t check if the thing is published. Doesn&amp;#8217;t check what pages it is actually shown on and only removes them. It just blows away cache when things change.&lt;/p&gt;
&lt;p&gt;Someday we&amp;#8217;ll definitely do something more advanced like a reference-based cache where only the pages that need to be blown away are, but this is working great for now. Hope this is helpful to someone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The main thing to remember is to use the host and make sure there is only one way to get to the resource&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So what does this all mean to our read heavy application? Well, we end up with &lt;a href="http://scoutapp.com/"&gt;Scout&lt;/a&gt; graphs like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://static.railstips.org/images/articles/harmony_page_caching.jpg" alt="Harmony Page Caching" class="image full" /&gt;&lt;/p&gt;
&lt;p&gt;The blue is apache requests and the orange is Rails requests. Notice that as our apache requests go up, our Rails requests stay pretty steady.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=AbNAyoBfGQk:YDv_hjOrGTo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=AbNAyoBfGQk:YDv_hjOrGTo:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=AbNAyoBfGQk:YDv_hjOrGTo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/AbNAyoBfGQk" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/01/22/multiple-domain-page-caching/</feedburner:origLink></entry>
  
    <entry>
      <title>I Have No Talent</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/Q_x5vRDFEBw/" />
      <id>4b4b5ab5dabe9d4d64000003</id>
      <updated>2010-02-25T22:57:03-0500</updated>
      <published>2010-01-12T13:00:00-0500</published>
      <category term="thoughts" />
      <summary type="html">&lt;p&gt;In which I discuss my lack of talent and intelligence.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;The other day someone sent me an IM and thanked me for my open source contributions. They then said something about &lt;strong&gt;wishing they had my gem/code creation talents&lt;/strong&gt;. I didn&amp;#8217;t miss a beat and &lt;strong&gt;informed them that I have no talent&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It is true. I have no talent. &lt;strong&gt;What I do have is a lot of practice&lt;/strong&gt;. And I am not talking about occasionally dabbling in Ruby on the weekends. I am talking about the kind of practice where I beat code that isn&amp;#8217;t working into submission (though often times the code wins).&lt;/p&gt;
&lt;p&gt;The kind of practice where all of a sudden I realize that it is 2am and I&amp;#8217;m exhausted physically so I should go to bed, but mentally I feel on fire so I let the code have me for another hour or two (I imagine this state to be like a marathon runner or ironman near the end of their race).&lt;/p&gt;
&lt;p&gt;The kind of practice that leads to a &lt;a href="http://github.com/jnunemaker"&gt;GitHub profile&lt;/a&gt; stuffed with code I regret (and am embarrassed about, but don&amp;#8217;t delete to remind me of where I once was) and code I am proud of (not near as much as I am embarrassed about though).&lt;/p&gt;
&lt;h2&gt;Intelligence&lt;/h2&gt;
&lt;p&gt;I am also not very smart. I have a good memory (though my wife will tell you it has some missing pieces) and I work really hard. &lt;strong&gt;Really hard&lt;/strong&gt;. I get that from my dad. He is also not very smart (his words, not mine), with a good memory and works really hard. :)&lt;/p&gt;
&lt;p&gt;I am sick of hearing people say, &amp;#8220;Oh, I love your code, &lt;strong&gt;I wish I could do that&lt;/strong&gt;.&amp;#8221; &lt;strong&gt;You can&lt;/strong&gt;. The only reason you can&amp;#8217;t &lt;strong&gt;is because you don&amp;#8217;t practice enough&lt;/strong&gt;. I used to think that I wasn&amp;#8217;t smart enough. I was jealous of those that did crazy code stuff that I couldn&amp;#8217;t even comprehend. Then, one day, I ran into something I did not understand and instead of giving up, I pushed through. I sat there in front of my computer for hours and wrestled with &lt;a href="http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/"&gt;class and class instance variables&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That day was a turning point for me. It was &lt;strong&gt;the last time&lt;/strong&gt; I thought that whether or not I was successful &lt;strong&gt;depended on my talent or intelligence&lt;/strong&gt;. It really comes down to hard work people. Ever since then, I have attacked each thing that I do not understand until I understand it.&lt;/p&gt;
&lt;p&gt;I will close with this. &lt;strong&gt;I still suck&lt;/strong&gt;. There are still so many people out there who are far better than I am, but that does not stop me anymore. I do not measure myself against the programming greats, but against those projects on my Github profile from years ago.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=Q_x5vRDFEBw:0g4F7dttvIQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=Q_x5vRDFEBw:0g4F7dttvIQ:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=Q_x5vRDFEBw:0g4F7dttvIQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/Q_x5vRDFEBw" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/01/12/i-have-no-talent/</feedburner:origLink></entry>
  
    <entry>
      <title>Live From Harmony</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/VVH1VOi10t4/" />
      <id>4b442b2fdabe9d79e8000004</id>
      <updated>2010-01-08T04:09:37-0500</updated>
      <published>2010-01-06T01:00:00-0500</published>
      <category term="mongodb" /><category term="harmony" />
      <summary type="html">&lt;p&gt;In which I announce that Rails Tips is now powered by Harmony, the website management tool that we have built at Ordered List.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;After more than a year and a half of &lt;strong&gt;self-funded&lt;/strong&gt; work on and off, &lt;a href="http://harmonyapp.com"&gt;Harmony&lt;/a&gt; is almost ready. It is so close in fact, that we (Steve and I) are beginning to eat our own dog food by moving our own sites into it. Rails Tips is the first to go live (if you can see this post, you are a witness) and I&amp;#8217;m not going to lie, it feels good!&lt;/p&gt;
&lt;p&gt;As I mentioned previously, we recently switched &lt;a href="http://railstips.org/blog/archives/2009/12/18/why-i-think-mongo-is-to-databases-what-rails-was-to-frameworks"&gt;Harmony to MongoDB&lt;/a&gt;. A few have asked what issues we ran into and so far it has only been mental (free your mind matrix-style stuff). Now we will start getting into the nitty gritty. Expect some good posts soon.&lt;/p&gt;
&lt;p&gt;Rails Tips is all moved and our company site, &lt;a href="http://orderedlist.com"&gt;OrderedList.com&lt;/a&gt;, is in the final stages of the redesign process and should be going live in Harmony later this week.&lt;/p&gt;
&lt;h2&gt;Redirects&lt;/h2&gt;
&lt;p&gt;Harmony has redirect support (including regex&amp;#8217;s and back references) so I&amp;#8217;ve put a few in place to fix the URLs, but there are bound to be some broken things around here. If you run into anything that isn&amp;#8217;t working, just let me know with a comment or something.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://static.railstips.org/images/articles/redirects.jpg" alt="Redirects" class="image full" /&gt;&lt;/p&gt;
&lt;h2&gt;Technical Details To Come&lt;/h2&gt;
&lt;p&gt;I am really excited about the future of Harmony and I&amp;#8217;ll be posting more on the technical details soon, but for now this lame post will have to suffice. :)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=VVH1VOi10t4:B6gZcWtCQMg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=VVH1VOi10t4:B6gZcWtCQMg:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=VVH1VOi10t4:B6gZcWtCQMg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/VVH1VOi10t4" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2010/01/06/live-from-harmony/</feedburner:origLink></entry>
  
    <entry>
      <title>The Balance of Convention and Innovation</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/U0zmw7tHT5o/" />
      <id>4b4218f6dabe9d782b000009</id>
      <updated>2010-01-18T22:53:32-0500</updated>
      <published>2009-12-29T00:00:00-0500</published>
      <category term="quotes" />
      <summary type="html">&lt;p&gt;In which I post an enjoyable quote and some thoughts on standards versus innovation.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;&lt;a href="http://adam.blog.heroku.com/past/2009/12/27/crystalized_innovation/"&gt;Adam Wiggins&lt;/a&gt; just posted a few paragraphs by Tim Lind from his article titled &lt;a href="http://universalprogrammer.wordpress.com/2009/12/08/innovation-in-database-technology/"&gt;Innovation in Database Technology&lt;/a&gt;. Rarely do I just post direct quotes here, but I really thought the paragraphs were insightful, so I&amp;#8217;ll share them for those that haven&amp;#8217;t had the pleasure yet.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;That, is why we have really moved away from sql, it is not for any specific approach to scalability or data storage, &lt;strong&gt;but rather just the ability to free ourselves from the standardized ideas&lt;/strong&gt; encapsulated in the standard query language.&lt;/p&gt;&lt;p&gt;I’m going to say it again, &lt;strong&gt;moving away from sql allows us to innovate&lt;/strong&gt;. I’m sure no one will have ill feeling towards the notion of innovation, and &lt;strong&gt;standardization is almost the exact opposite, it is the crystallization of previous innovation&lt;/strong&gt;, so of course it would be what we stand against.&lt;/p&gt;&lt;p&gt;We do not necessarily stand against any specific idea encapsulated by the sql standardization, rather we just choose &lt;strong&gt;to open ourselves up&lt;/strong&gt; to investigating the elements of the system for the sake of making design decisions which provide innovative solutions.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;You can&amp;#8217;t innovate in a box of standards, thus the &amp;#8220;think outside the box&amp;#8221; saying. For whatever reason, I had not thought about the step back from &lt;span class="caps"&gt;SQL&lt;/span&gt; from this perspective.&lt;/p&gt;
&lt;p&gt;Several people have asked me what issues I ran into switching to MongoDB. The biggest issue I ran into was freeing my mind from the standards and conventions that swaddled me in bed at night. &lt;strong&gt;Conventions and standards are great, but they do seem to be at odds with innovation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We definitely do not want to be in a constant state of innovation, as I am sure that would lead to chaos. On the other hand, if we always stick to conventions and standards, we will never push forward. There has to be balance. After years of the same with regards to databases, it &lt;strong&gt;great to see the new ideas&lt;/strong&gt; over the past year or so.&lt;/p&gt;
&lt;p&gt;Thanks to Tim for the observations and Adam for putting them on my radar.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=U0zmw7tHT5o:ZSdgG_bMnnk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=U0zmw7tHT5o:ZSdgG_bMnnk:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=U0zmw7tHT5o:ZSdgG_bMnnk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/U0zmw7tHT5o" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2009/12/29/the-balance-of-convention-and-innovation/</feedburner:origLink></entry>
  
    <entry>
      <title>Getting A Grip on GridFS</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/LD4k8fO8VaY/" />
      <id>4b4218f7dabe9d782b00001b</id>
      <updated>2010-01-06T00:37:29-0500</updated>
      <published>2009-12-23T09:30:00-0500</published>
      <category term="mongodb" /><category term="plugin" /><category term="mongomapper" /><category term="gridfs" />
      <summary type="html">&lt;p&gt;In which I announce some tweaks I made to a fun plugin which makes storing files in GridFS as easy as 1, 2, 3.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;I know it is almost Christmas and your minds are beginning to turn to gifts and family, but hang with me for a few more minutes. My buddy &lt;a href="http://jonmagic.com/"&gt;Jon&lt;/a&gt; pointed me to &lt;a href="http://github.com/twoism/grip"&gt;Grip&lt;/a&gt; yesterday. I liked the idea but not all of the implementation. I&amp;#8217;ve been doing some GridFS stuff lately so I decided to take some time and tweak it to be more what I need.&lt;/p&gt;
&lt;p&gt;You can get my &lt;a href="http://github.com/jnunemaker/grip"&gt;fork of Grip&lt;/a&gt; on Github. Below is a simple example of how it works (you could also &lt;a href="http://github.com/jnunemaker/grip/blob/master/test/test_grip.rb"&gt;check out the tests&lt;/a&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Foo
  include MongoMapper::Document
  include Grip
  
  has_grid_attachment :image
  has_grid_attachment :pdf
end

image = File.open('foo.png', 'r')
pdf = File.open('foo.pdf', 'r')
foo = Foo.create(:image =&amp;gt; image, :pdf =&amp;gt; pdf)

foo.image # contents read from gridfs for serving from rack/metal/controller
foo.image_name # foo.jpg
foo.image_content_type # image/png
foo.image_size # File.size of the file
foo.image_path # path in grid fs foo/image/:id where :id is foo.id&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main changes I made were to store name, size and content_type along with the path to the file. I also made it &lt;strong&gt;so those are assigned when the file is set so that they can be used in validations&lt;/strong&gt;. Some other ideas I have for the plugin are adding image resizing with MojoMagick and common validations (much like most of the file upload plugins out there).&lt;/p&gt;
&lt;p&gt;What I really like about Grip is its simplicity. A single, small file, an include, a call to has_grid_attachment, and you can start storing files. The great thing about working on this is &lt;strong&gt;it gave me some wonderful ideas for how to standardize the process of creating and declaring MongoMapper plugins&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll be adding those to ideas to MongoMapper over the next few weeks as I get time and I think people are going to be stoked about what I&amp;#8217;ve come up with (thanks to &lt;a href="http://opensoul.org/"&gt;Brandon&lt;/a&gt; for brainstorming the plugin &lt;span class="caps"&gt;API&lt;/span&gt; with me).&lt;/p&gt;
&lt;h2&gt;Behind the Scenes&lt;/h2&gt;
&lt;p&gt;As I said, behind the scenes, Grip uses &lt;a href="http://www.mongodb.org/display/DOCS/GridFS+Specification"&gt;GridFS&lt;/a&gt; (more on &lt;a href="http://blog.mongodb.org/post/183689081/storing-large-objects-and-files-in-mongodb"&gt;GridFS&lt;/a&gt;), Mongo&amp;#8217;s specification for storing files in Mongo.&lt;/p&gt;
&lt;p&gt;The good news is that the &lt;span class="caps"&gt;API&lt;/span&gt; for storing files in GridFS using Ruby is nearly identical to using Ruby&amp;#8217;s File class. Unfortunately, that is also the bad news, in my opinion, as I find Ruby&amp;#8217;s File open, read and close a bit awkward. Here are a few examples pulled almost directly from Grip:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;# write foo.jpg to grid fs
file = File.read('foo.jpg')
GridFS::GridStore.open(database, 'foo.jpg', 'w', :content_type =&amp;gt; 'image/jpg') do |f|
  f.write(file)
end

# read foo.jpg from grid fs
GridFS::GridStore.read(database, 'foo.jpg')

# delete foo.jpg from grid fs
GridFS::GridStore.unlink(database, 'foo.jpg')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not horrible but not beautiful. There is a bunch of &lt;a href="http://github.com/search?langOverride=&amp;amp;q=gridfs&amp;amp;repo=&amp;amp;start_value=1&amp;amp;type=Repositories"&gt;GridFS related code on Github&lt;/a&gt;. To highlight a few interesting ones:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://github.com/mdirolf/nginx-gridfs/"&gt;nginx GridFS&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://github.com/skinandbones/rack-gridfs/"&gt;Rack GridFS&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://github.com/mikejs/gridfs-fuse/"&gt;GridFS Fuse&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://github.com/twoism/metal_grid_fs/"&gt;Rails Metal for serving from GridFS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is all for now. Enjoy the GridFS goodness and the future hope of an awesome MongoMapper plugin interface.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=LD4k8fO8VaY:LSqq4pExmcw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=LD4k8fO8VaY:LSqq4pExmcw:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=LD4k8fO8VaY:LSqq4pExmcw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/LD4k8fO8VaY" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2009/12/23/getting-a-grip-on-gridfs/</feedburner:origLink></entry>
  
    <entry>
      <title>Twizzle Your Deplizzles</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/PwWv298iVDY/" />
      <id>4b4218f7dabe9d782b000022</id>
      <updated>2010-01-13T19:52:42-0500</updated>
      <published>2009-12-21T21:30:00-0500</published>
      <category term="twitter" /><category term="capistrano" />
      <summary type="html">&lt;p&gt;In which I show how to easily update Twitter with deploy notices that include the environment and revision.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;Steve and I have a Twitter account that we send all our commits to. It is all handled by Github and both of us find it really handy. Rain or shine, we get commit updates on our phone which is great for staying in the loop.&lt;/p&gt;
&lt;p&gt;For a little while now, I&amp;#8217;ve been wanting to add deploy notices to this twitter account and tonight I finally got around to it. It was pretty easy, but I&amp;#8217;ll post the code here to save others time. I went the no dependencies route (even though I created the &lt;a href="http://github.com/jnunemaker/twitter/"&gt;Twitter gem&lt;/a&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;set :twitter_username, 'username'
set :twitter_password, 'password'

namespace :twitter do
  task :update do
    require 'open-uri'
    require 'net/http'
    
    url = URI.parse('http://twitter.com/statuses/update.xml')
    request = Net::HTTP::Post.new(url.path)
    request.basic_auth twitter_username, twitter_password
    request.set_form_data('status' =&amp;gt; "Deployed #{current_revision[0..6]} to #{rails_env}")
    
    begin
      response = Net::HTTP.new(url.host, url.port).start do |http|
        http.open_timeout = 10
        http.request(request)
      end
      puts 'Deploy notice sent to twitter'
    rescue Timeout::Error =&amp;gt; e
      puts "Timeout after 10s: Seems like Twitter is down."
      puts "Use \"cap twitter:update\" to update Twitter status later w/o deploying"
    end
  end
end

after "deploy", "twitter:update"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just drop this in your deploy file and update the username and password. Notice that I even included the git revision and rails environment so you can easily see what was last deployed and where to. You end up with a nice little message like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Deployed 09ea23f to staging&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Very handy!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=PwWv298iVDY:AJ2mrSMG24w:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=PwWv298iVDY:AJ2mrSMG24w:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=PwWv298iVDY:AJ2mrSMG24w:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/PwWv298iVDY" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2009/12/21/twizzle-your-deplizzles/</feedburner:origLink></entry>
  
    <entry>
      <title>Why I think Mongo is to Databases what Rails was to Frameworks</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/y5_UdXPFA2c/" />
      <id>4b4218f8dabe9d782b00006a</id>
      <updated>2010-01-04T11:36:07-0500</updated>
      <published>2009-12-18T16:47:00-0500</published>
      <category term="mongodb" /><category term="mongomapper" /><category term="harmony" />
      <summary type="html">&lt;p&gt;In which I divulge the awesomeness of MongoDB that we have used to build Harmony.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;Strong statement, eh? The more I work with Mongo the more I am coming around to this way of thinking. I tell no lie when I say that I now approach Mongo with the same kind of excitement I &lt;a href="http://addictedtonew.com/archives/77/ruby-on-rails-first-impressions/"&gt;first felt using Rails&lt;/a&gt;. For some, that may be enough, but for others, you probably require more than a feeling to check out a new technology.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Below are 7 Mongo and MongoMapper&lt;/strong&gt; related features that I have found to be really awesome while working on switching &lt;a href="http://harmonyapp.com"&gt;Harmony&lt;/a&gt;, a new website management system by my company, &lt;a href="http://orderedlist.com"&gt;Ordered List&lt;/a&gt;, to Mongo from MySQL.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://harmonyapp.com"&gt;&lt;img src="http://static.railstips.org/images/articles/harmony.png" alt="Harmony" class="image full" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;1. Migrations are Dead&lt;/h2&gt;
&lt;p&gt;Remember the first time you created and ran a migration in Rails. Can you? Think back to the exuberance of the moment when you realized tempting fate on a production server was a thing of the past. Well I have news for you Walter Cronkite, &lt;strong&gt;migrations are so last year&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Yep, you don&amp;#8217;t migrate when you want to add or remove columns with Mongo. Heck, you don&amp;#8217;t even add or remove columns. Need a new piece of data? &lt;strong&gt;Throw a new key into any model and you can start adding data to it&lt;/strong&gt;. No need to bring your app to a screeching halt, migrate and then head back to normal land. Just add a key and start collecting data.&lt;/p&gt;
&lt;h2&gt;2. Single Collection Inheritance Gone Wild&lt;/h2&gt;
&lt;p&gt;There are times when inheritance is sweet. Let&amp;#8217;s take Harmony for example. Harmony is all about managing websites. Websites have content. &lt;strong&gt;Content does not equal pages&lt;/strong&gt;. Most website management tools are called content management systems and all that means is that &lt;strong&gt;you get a title field and a content field&lt;/strong&gt;. There, you can now manage content. &lt;strong&gt;Wrong!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pages are made up of content. Each piece of content could be as tiny as a number or as large a massive &lt;span class="caps"&gt;PDF&lt;/span&gt;. &lt;strong&gt;Also, different types of pages behave differently&lt;/strong&gt;. Technically a blog and a page are both pages, but a page has children that are most likely ordered intentionally, whereas a blog has children that are ordered by publish date.&lt;/p&gt;
&lt;p&gt;So how did Mongo help us with this? Well, we created a base Item model. Sites have many items. Items have many custom pieces of data. So, we have an Item model that acts as the base for our Page, Blog, Link, BlogPost and such models. Then each of those defines specific keys and behaviors that they do not have in common the other items.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By using inheritance, they all share the same base keys, validations, callbacks and collection&lt;/strong&gt;. Then for behaviors and keys that are shared by some, but not all, &lt;strong&gt;we are creating modules and including them&lt;/strong&gt;. One such module is SortableItem. This gets included in Page, Blog and Link as those can all be sorted and have previous and next items. The SortableItem module defines a position key and keeps the position order in check when creating and destroying items that include it. Think of it as acts_as_list.&lt;/p&gt;
&lt;p&gt;This has been so handy. Steve was building the &lt;a href="http://docs.harmonyapp.com"&gt;doc site&lt;/a&gt; and said he wished he had a link type, something that shows up in the navigation, but cross links to another section or another site. &lt;strong&gt;I was like, so make it&lt;/strong&gt;! Here it is in all its glory.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Link &amp;lt; Item
  include SortableItem
  
  key :url, String, :required =&amp;gt; true, :allow_blank =&amp;gt; false
  
  def permalink
    Harmony.escape_url(title)
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yep, barely any code. We inherit from item, include the sortable attributes, define a new key named url (where the link should go to) and make sure the permalink is always set to the title. Nothing to it. &lt;strong&gt;This kind of flexibility is huge when you get new feature ideas&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;All these completely different documents are stored in the same collection&lt;/strong&gt; and follow a lot of the same rules but none of them has any more data stored with it than is absolutely needed. &lt;strong&gt;No creating a column for any key that could be in any row. Just define the keys that go with specific document types&lt;/strong&gt;. Sweet!&lt;/p&gt;
&lt;h2&gt;3. Array Keys&lt;/h2&gt;
&lt;p&gt;Harmony has sites and users. Users are unique across all of Harmony. One username and password and you can access any specific site or all sites of a particular account. Normally this would require a join table, maybe even some polymorphism. What we decided to do is very simple. &lt;strong&gt;Mongo natively understands arrays&lt;/strong&gt;. Our site model has an array key named authorizations and our Account model has one named memberships. These two array keys store arrays of user ids. We could de-normalize even more and just have a sites array key on user, but we decided not to.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Site
  include MongoMapper::Document
  key :authorizations, Array, :index =&amp;gt; true
  
  # def add_user, remove_user, authorized?
end

class Account
  include MongoMapper::Document
  key :memberships, Array, :index =&amp;gt; true
  
  # def add_user, remove_user, member?
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is cool about this is that it is still simple to get all the users for a given site.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Site
  def users
    user_ids = [authorizations, memberships].flatten.compact.uniq
    User.all(:id =&amp;gt; user_ids, :order =&amp;gt; 'last_name, first_name')
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sweet thing about this is that not only does Mongo know how to store arrays in documents, but &lt;strong&gt;you can even index the values and perform efficient queries on arrays&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Eventually, I want to roll array key stuff like this into MongoMapper supported associations, but I just haven&amp;#8217;t had a chance to abstract them yet. Look for that on the horizon.&lt;/p&gt;
&lt;h2&gt;4. Hash Keys&lt;/h2&gt;
&lt;p&gt;As if array keys were not enough, hash keys are just as awesome. Harmony has a really intelligent activity stream. Lets face it, &lt;strong&gt;most activity streams out there suck&lt;/strong&gt;. Take Github&amp;#8217;s for example. I will pick on them because I know the guys and they are awesome. They are so successful, they can take it. :)&lt;/p&gt;
&lt;p&gt;It may be handy that I can see &lt;strong&gt;every single user&lt;/strong&gt; who follows or forks MongoMapper, but personally I would find it way more helpful if their activity stream just put in one entry that was more like this.&lt;/p&gt;
&lt;p&gt;&amp;#8220;14 users started watching MongoMapper today and another 3 forked it. Oh, and you had 400 pageviews.&amp;#8221;&lt;/p&gt;
&lt;p&gt;Am I right? Maybe I have too many projects, but &lt;strong&gt;their feed is overwhelming for me at times&lt;/strong&gt;. What we did to remedy this in Harmony is make the activity stream intelligent. When actions happen, it checks if the same action has happened recently and just increments a count. What you end up with are things in the activity stream like:&lt;/p&gt;
&lt;p&gt;&amp;#8220;Mongo is to Databases what Rails was to Frameworks was updated 24 times today by John Nunemaker.&amp;#8221;&lt;/p&gt;
&lt;p&gt;On top of that, we use a hash key named source to store all of the attributes from the original object right in the activity stream collection. This means we do 0, yes 0, extra queries to show each activity. Our activity model looks something like this (obviously this is really pared down):&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Activity
  include MongoMapper::Document
  key :source, Hash
  key :action, String
  key :count, Integer, :default =&amp;gt; 1
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we define an &lt;span class="caps"&gt;API&lt;/span&gt; in that model to normalize the different attributes that could be there. For example, here is the title method:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Activity
  def title
    source['title'] || source['name'] || source['filename']
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to determine if a new action is already in the stream and just needs to be incremented, we can then use Mongo&amp;#8217;s ability to query inside hashes with something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;Activity.first({
  'source._id'   =&amp;gt; id, 
  :action        =&amp;gt; 'updated', 
  :created_at.gt =&amp;gt; Time.zone.now.beginning_of_day.utc
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How fricken sweet is that? Major. Epic.&lt;/p&gt;
&lt;h2&gt;5. Embedding Custom Objects&lt;/h2&gt;
&lt;p&gt;What is that you say? Arrays and hashes just aren&amp;#8217;t enough for you. Well go fly a kite&amp;#8230;or just use an embedded object. When Harmony was powered by MySQL (back a few months ago), we had an Item model and an ItemAttributes (key, value, item_id) model.&lt;/p&gt;
&lt;p&gt;Item was the base for all the different types of content and item attributes were how we allowed for custom data on items. This meant every time we queried for an item, we also had to get its attributes. This isn&amp;#8217;t a big deal when you know all the items up front as you can eager load some stuff, but we don&amp;#8217;t always know all the items that are going to be shown on a page until it happens.&lt;/p&gt;
&lt;p&gt;That pretty much killed the effectiveness of eager loading. It also meant that we just always got all of an item&amp;#8217;s attributes each time we got an item (and performed the queries to get the info), just in case one of those attributes was being used (title and path are on all items).&lt;/p&gt;
&lt;p&gt;With Mongo, however, we just embed custom data right with the item. Anytime we get an item, all the custom data comes with it. &lt;strong&gt;This is great as there is never a time where we would get an attribute without the item it is related to&lt;/strong&gt;. For example, here is part of an item document with some custom data in it:&lt;/p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;{
  "_id"   =&amp;gt;..., 
  "_type" =&amp;gt;"Page", 
  "title" =&amp;gt;"Our Writing", 
  "path"  =&amp;gt;"/our-writing/", 
  "data"  =&amp;gt;[
    {"_id" =&amp;gt;..., "file_upload"=&amp;gt;false, "value"=&amp;gt;"", "key"=&amp;gt;"content"}, 
    {"_id" =&amp;gt;..., "file_upload"=&amp;gt;true, "value"=&amp;gt;"", "key"=&amp;gt;"pic"}
  ], 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now anytime we get an item, we already have the data. No need to query for it. &lt;strong&gt;This alone will help performance so much in the future&lt;/strong&gt;, that it alone had the weight to convince us to switch to Mongo, despite being almost 90% done in MySQL.&lt;/p&gt;
&lt;p&gt;The great part is embedded objects are just arrays of hashes in Mongo, but MongoMapper automatically turns them into pure ruby objects.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;class Item
  include MongoMapper::Document
  
  many :data do
    def [](key)
      detect { |d| d.key == key.to_s }
    end
  end
end

class Datum
  include MongoMapper::EmbeddedDocument
  
  key :key, String
  key :value
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like that, each piece of custom data gets embedded in the item on save and converted to a Datum object when fetched from the database. The association extension on data even allows for getting data by its key quite easily like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;Item.first.data['foo'] # return datum instance if foo key present&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. Incrementing and Decrementing&lt;/h2&gt;
&lt;p&gt;A decision we made the moment we switched to Mongo was to take advantage of its awesome parts as much as we could. One way we do that is storing published post counts on year, month and day archive items and label items. Anytime a post is published, unpublished, etc. we use Mongo&amp;#8217;s increment modifier to bump the count up or down. This means that there is &lt;strong&gt;no query at all needed to get the number of posts published in a given year, month or day or of a certain label&lt;/strong&gt; if we already have that document.&lt;/p&gt;
&lt;p&gt;We have several callbacks related to a post&amp;#8217;s publish status that call methods that perform stuff like this under the hood:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;# ids is array of item ids
conditions = {:_id =&amp;gt; {'$in' =&amp;gt; ids}}

# amount is either 1 or -1 for increment and decrement
increments = {'$inc' =&amp;gt; {:post_count =&amp;gt; amount}}

collection.update(conditions, increments, :multi =&amp;gt; true)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For now, we drop down the ruby driver (collection.update), but I have tickets (&lt;a href="http://github.com/jnunemaker/mongomapper/issues#issue/121"&gt;inc&lt;/a&gt;, the &lt;a href="http://github.com/jnunemaker/mongomapper/issues#issue/122"&gt;rest&lt;/a&gt;) to abstract this out of Harmony and into MongoMapper. Modifiers like this are super handy for us and &lt;strong&gt;will be even more handy when we roll out statistics in Harmony&lt;/strong&gt; as we&amp;#8217;ll use increments to keep track of pageviews and such.&lt;/p&gt;
&lt;h2&gt;7. Files, aka GridFS&lt;/h2&gt;
&lt;p&gt;Man, with all the awesome I&amp;#8217;ve mentioned above, some of you may be tired, but I need you to hang with me for one more topic. Mongo actually has a really cool GridFS specification that is implemented for all the drivers to allow storing files right in the database. I remember when storing files in the database was a horrible idea, but &lt;strong&gt;with Mongo this is really neat&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We currently store all theme files and assets right in Mongo. This was handy when in development for passing data around and was nice for building everything in stage before our move to production. When we were ready to move to production, we literally just dumped stage and restored it on production. Just like that all data and files were up and running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No need for S3 or separate file backup processes&lt;/strong&gt;. Just store the files in Mongo and serve them right out of there. We then &lt;strong&gt;heavily use etags and &lt;span class="caps"&gt;HTTP&lt;/span&gt; caching&lt;/strong&gt; and intend on doing more in the future to make sure that serving these files stays performant, but that is for another day. :) As of now, it is plenty fast and sooooo convenient.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We have been amazed at how much code we cut out of Harmony with the switch from MySQL to Mongo. We&amp;#8217;re also really excited about the features mentioned above and how they are going to help us grow our first product, &lt;a href="http://harmonyapp.com"&gt;Harmony&lt;/a&gt;. I can&amp;#8217;t imagine building some of the flexibility we&amp;#8217;ve built into Harmony or some of the ideas we have planned for the future with a relational database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I am truly as excited about the future of Mongo as I once was (and still am) about the future of Rails.&lt;/strong&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=y5_UdXPFA2c:TpOjEYOquhM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=y5_UdXPFA2c:TpOjEYOquhM:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=y5_UdXPFA2c:TpOjEYOquhM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/y5_UdXPFA2c" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2009/12/18/why-i-think-mongo-is-to-databases-what-rails-was-to-frameworks/</feedburner:origLink></entry>
  
    <entry>
      <title>Config So Simple Your Mama Could Use It</title>
      <link href="http://feedproxy.google.com/~r/railstips/~3/DlFoSL0_mp4/" />
      <id>4b4218f8dabe9d782b00007b</id>
      <updated>2010-01-04T11:36:08-0500</updated>
      <published>2009-11-10T06:00:00-0500</published>
      <category term="clogging" />
      <summary type="html">&lt;p&gt;In which I clog a bit of code for simple application configuration.&lt;/p&gt;</summary>
      <content type="html">&lt;p&gt;Tonight, &lt;a href="http://metaatem.net/"&gt;Kastner&lt;/a&gt; asked me if I had anything to do some simple configuration for something he was working on. I&amp;#8217;ve got a simple module and yaml file that I&amp;#8217;ve been using so I &lt;a href="http://gist.github.com/230531"&gt;gist&amp;#8217;d&lt;/a&gt; it. It then occurred to me that I might as well share it here too.&lt;/p&gt;
&lt;h2&gt;The Yaml&lt;/h2&gt;
&lt;p&gt;Below is an example of the yaml file. Basically, I setup some defaults and then customize each environment as needed.&lt;/p&gt;
&lt;pre&gt;&lt;code class="yaml"&gt;DEFAULTS: &amp;amp;DEFAULTS
  email: no-reply@harmonyapp.com
  email_signature: |
    Regards,
    The Harmony Team
 
development:
  domain: harmonyapp.local
  &amp;lt;&amp;lt;: *DEFAULTS
  
test:
  domain: harmonyapp.com
  &amp;lt;&amp;lt;: *DEFAULTS
 
production:
  domain: harmonyapp.com
  &amp;lt;&amp;lt;: *DEFAULTS&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Module&lt;/h2&gt;
&lt;p&gt;The module can read and write to the config and even loads the Yaml file the first time you try to read a configuration key.&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;module Harmony
  # Allows accessing config variables from harmony.yml like so:
  # Harmony[:domain] =&amp;gt; harmonyapp.com
  def self.[](key)
    unless @config
      raw_config = File.read(RAILS_ROOT + "/config/harmony.yml")
      @config = YAML.load(raw_config)[RAILS_ENV].symbolize_keys
    end
    @config[key]
  end
  
  def self.[]=(key, value)
    @config[key.to_sym] = value
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I wanted to get the domain, I would do the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class="ruby"&gt;Harmony[:domain]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing fancy, but it gets the job done. I just drop the yaml file in config/ and the module in lib/. Obviously, you would rename the module and yaml file to whatever constant you want, such as App or something related to your application&amp;#8217;s name. I know there are gems and plugins to do configuration, but when something this simple gets the job done, I figure why bother.&lt;/p&gt;
&lt;p&gt;What do you all use for app configuration? What do you like about what you use?&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/railstips?a=DlFoSL0_mp4:rFz3cQ-6tIM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=DlFoSL0_mp4:rFz3cQ-6tIM:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/railstips?a=DlFoSL0_mp4:rFz3cQ-6tIM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/railstips?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/railstips/~4/DlFoSL0_mp4" height="1" width="1"/&gt;</content>
      <author>
        <name>John Nunemaker</name>
      </author>
    <feedburner:origLink>http://railstips.org/blog/archives/2009/11/10/config-so-simple-your-mama-could-use-it/</feedburner:origLink></entry>
  
</feed>
