Dynamic content distinguishes the web as an interactive medium, but dynamic content can also mean huge strains on web servers. Thankfully, caching can be used to relieve this stress by eliminating redundancies among the generation of identical response content. To this end, the Merb plugin merb-cache
is included in the Merb stack and has been designed to simplify the common challenges of integrating caching in an application.
Though caching is included within the Merb stack, you need to configure it to put it to use.
There are two fundamental cache stores, FileStore
and MemcachedStore
. You can use multiples of each of these stores in your application if necessary.
• FileStore
—an implementation of caching using files. It stores these files in the directory dir
, which, if not specified, defaults to Merb.root_path(:tmp / :cache)
. File stores have the best performance for fragment and page caching but not for object caching.
• MemcachedStore
—an implementation of caching using memcached, a caching daemon used by nearly all the web’s largest sites. By virtue of its incredible versatility, memcached allows you to distribute caching across multiple servers and suits all forms of efficient caching. The two options namespace
and servers
accept a string for the memcached application namespace and an array for server addresses, respectively. The defaults of these two configuration options are nil and ["127.0.0.1:11211"]
.
To set up a cache store within Merb, you can pass a block of cache registrations into Merb::Cache
. This is best done either in config/init.rb
or through one of the environment files should you need to tailor caching to the environment.
Above we have set up a single-file store cache as well as a secondary memcached cache named memcached
. Once registered, all data stores are accessible via Merb::Cache
. Treated like an array, Merb::Cache
by default retrieves the default store and otherwise pulls up the store we specify by name:
Strategy stores are layered stores on top of the two fundemental stores. You can wrap them on top of both file and memcached stores or even on top of themselves.
• ActionStore
—stores responses for cached controller actions that may have responses conditioned upon request parameters
• AdhocStore
—wraps a list of multiple stores together using the first one that will work
• GzipStore
—gzips cached data and is thus suitable for large files
• PageStore
—sends out cached responses for simple actions, avoiding full controller dispatch
• SHA1Store
—hashes parameters to form a key for each cache and is best used when numerous parameters are involved
Below we wrap the stores we previously created with two of the strategy stores from above.
Merb application development does not typically involve getting your hands dirty with low-level caching methods. However, understanding how to get down to caching basics adds both breadth to your understanding of the plugin as well as a few essentials to your development arsenal. Therefore, let’s take a look at the basic methods used to interact with cache stores.
There are two methods related to writing data to a cache. These are writable?
and write
. The first checks to see if a particular cache key is writable or not. Between the two fundamental data stores, write
makes a call to writable?
in order to get the go-ahead to cache data. Therefore, as an application developer you need to be aware of only the method write
. Here we use it to store a value into the cache store:
The first line is the easier of the two, simply setting data to be associated with a value. The second, however, has two extra method parameters, the first being a hash of additionally identifying parameters and the second being a condition that applies only to memcache stores.
In order to contrast the two methods and know how they work, let’s take a look at the two implementations of write
, first for FileStore
and second for MemcachedStore
:
As previously mentioned, both make use of the writable?
method, but the file store is more complicated. It creates a path for the file and then writes to it.
Reading, like writing, is handled by two methods, exists?
and read
. You may end up finding some use for exists?
as an application developer, but for the most part read
is sufficient since the first thing it does is seek the approval of exists?
.
Above we have pulled the data that we previously persisted in the two data stores.
Note that the read
method of FileStore
uses exists?
, as opposed to Data-Store#read
, which rescues not-found errors, ignoring them and returning nil.
The fetching of records is essentially shorthand for the retrieval or otherwise writing of a cached value. Below we use a block to return the potential value of a cache element that may not exist.
The fetch methods for both of the fundamental stores is the same:
In its highly condensed statement, fetch
reads from or otherwise attempts to write to a cached element.
You can delete cached data using the method delete
and passing in the cached element key along with any identifying parameters needed. Observe that the code behind the delete
method differs between the two fundamentals, but no more than we’ve seen before:
Various helpers exist to ease the use of caching away from the primitive methods we have so far seen. We’ll take a glimpse at all of these in this section.
In order to better facilitate the page and action stores, a number of methods are mixed into controllers at both an instance and a class level. The simpliest of these is cache!
, which caches all actions. Alternatively, the method cache
accepts a list of actions to cache. More precisely, we can specify that a particular action should be cached only under certain conditions with cache_action
:
All of these work by adding in before filters:
Do note that the position of the cache class methods can have an effect on the ultimate request, since they may be part of a complex filter chain. Authentication filters, for instance, should certainly appear above cache method lines.
A variant of action caching that is better suited for pages where the most current information is mildly less important than the speed of the response is eager caching. Making use of after filters instead of before filters, eager caching updates stale versions of the cache based upon the evoking of a triggering action. It does so in a run_later
block after the triggering action is evoked, making eager caching perfect for actions with expensively constructed responses. However, that’s the only problem eager caching solves, since it also effectively marks as stale all old caches. This versatility can be seen in the example below, where we employ the method eager_cache
.
The two methods build_url
and build_request
are helper methods used by the eager_cache
method to construct URLs and internally dispatch requests, respectively. In the first case above we don’t pass in a block and let eager_cache
do its own magic. The second is more explicit.
Here’s the code that sets up the after filters:
But far more interestingly, here’s the code that dispatches the eager requests to be cached. Pay special attention to the variety of forms in which requests can be created.
Fragment caching through the method fetch_fragment
allows application developers to store small sections of a view template. This is perfect if only a region of a template needs caching.
Looking into the source, we see how it magically identifies fragments by file and line number:
The method fetch_partial
can act as a replacement for partial
when the partial needs to be cached. As we saw with fetch
, the cached element is generally either read or otherwise written. However, unlike fetch
, the fact that we’re referring to a template means there is no need for passing in a block.
fetch_partial 'users/stats', :user => @user
Here’s the source behind fetch_partial
; note how it takes a possible third parameter of conditions that will be passed along to the actual fetch
method.
Caching in Merb can be handled in a number of ways. The best ways, however, are those that interfere the least with application code. The code behind action and eager caching exhibits the Merb controller’s ease in layering these mechanisms. Our look at the various caching stores available within Merb should make it clear that no one storage mechanism fits all, and that caching must make sense for the particular data being cached. Nonetheless, the caching store strategies provide a number of extensions on the abstract stores and when needed can be used to create composite user-defined stores as well.
3.14.134.188