FightinJoe : Aaron Wheeler

Merb caching DataMapper

Monday, 05 January 2009

Strange the evolution of Merb: copy what Rails does; reinvent how Rails does it; assimilate into Rails. Hopefully Rails will become as easy to hack as Merb is currently.

Case in point: Merb::Cache. It’s first iteration was nearly a direct clone of Rails caching, complete with fragment, action, and page caching. Then the core team had to get clever and reinvent the wheel. While a little half baked, it provides a foundation for constructing almost any type of cache, and is ripe for documentation!

There are several hurdles to get over to start using Merb::Cache:

  1. It isn’t intuitive. Or at least not for someone who is used to the Rails way. Fortunately, all of it doesn’t have to make sense in order be useful.
  2. It doesn’t play well with DataMapper. Or rather DataMapper doesn’t play nice, unless dm-core edge is used.
  3. It doesn’t work out of the box. The file cache, in particular, doesn’t do anything to serialize what is being cached, and no strategy is provided to do the work.

Preparation:

Working backwards, these hurdles are easy to clear. First, create a new strategy to serialize what is being cached. Using the gzip_store as a model, it’s easy to create a marshal_store that uses Marshal.dump and Marshal.load to serialize and deserialize any data that is being cached. This file can be placed in the lib folder for safe-keeping.

Setup:

Merb::Cache should be included in the Merb stack. If not, make sure that there is a dependency set for merb-cache. At the same time, add a dependency for dm-serializer (from dm-more); this is required to serialize DataMapper objects.

[code] # somewhere in the init.rb or dependencies.rb file dependency merb-cache dependency dm-serializer [/code]

It is easiest to configure Merb::Cache in an after_app_loads block in each environment file, since the caching schemes across environments will differ. Production will use a file cache store, while the other environments will use an adhoc store which does nothing.

[code] # in config/environments/production.rb Merb::BootLoader.after_app_loads do require 'lib/marshal_store' Merb::Cache.setup do # FYI, you can check whether a cache store has been setup with # Merb::Cache.stores.has_key?(:file_cache) register(:base_cache, Merb::Cache::FileStore, :dir => "tmp") register(:file_cache, Merb::Cache::MarshalStore[:base_cache]) end end [/code]

[code] Merb::BootLoader.after_app_loads do Merb::Cache.setup do register(:file_cache, Merb::Cache::AdhocStore.new) end end [/code]

Caching can now easily be used wherever needed:

[code] # for example, in a controller or model cache = Merb::Cache[:file_cache] keys = ['users',params] if cache.exists?( *keys ) @users = cache.read( *keys ) else cache.write( keys.first, @users = User.all, keys.last ) end [/code]

NOTE: the cache will have to be expired manually, since the file cache does not support time-based expiration.

[code] Merb::Cache[:file_cache].delete( *keys ) [/code]

Conclusion:

This solution should keep you warm and toasty for the next 6 months until Merb 2 / Rails 3 comes out. In the meantime, let’s get behind a Franken-source project we can all believe in: the integrity of ActiveRecord and the concept of DataMapper (ActiveDataRecordMapper? ActivataRecapper?)

Comments