In preparation for a Ruby project I wanted to play around with Rails’ page caching a little over the weekend. Naturally, I wanted to find a way to ensure that my controller was writing files correctly so that Apache (or any other HTTP server) can then serve the static content directly (without hitting my Rails processes). Since I’m also digging RSpec at the moment, I wanted to find a way of expressing it nicely in my specifications. This is what I ended up with.
Here’s the relevant controller’s specification:
describe UsersController, "caching" do include CachingExampleHelper it "should cache new users page" do requesting {get :new}.should be_cached end it "should not cache the activation page" do requesting {get :activate, :activation_code => 'blah'}.should_not be_cached endend
The key part is the requesting
line. That’s my extension that encapsulates the request that should be cached, so that we can both make the request and check that it ended up with a file being written to the filesystem.
module CachingExampleHelper ActionController::Base.public_class_method :page_cache_path ActionController::Base.perform_caching = true def requesting(&request) url = request.call.request.path ActionController::Base.expire_page(url) request.call end module ResponseHelper def cached? File.exists? ActionController::Base.page_cache_path(request.path) end end ActionController::TestResponse.send(:include, ResponseHelper)end
Note that we have to expose the pagecachepath method so we can calculate the location for where the cached files will get written, and then also explicitly enable caching. When we call requesting
giving a block to the request we want to make (such as get :index
to invoke the index
action), we capture the block as a Proc, invoke it and then read the request’s path from the generated TestResponse
object. This lets us then first expire the page (in case it was cached previously), and then make the request. Finally, we then check whether the cached file exists by mixing in a cached?
method to the Response.
Unfortunately, the code above will make 2 requests per assertion. But, I’d rather have it read nicely for the time being. Any suggestions on how you could tidy this up further would be well received!
I’m not sure I’d want to run these tests all the time, maybe something a little lighter to ensure that we tell Rails to caches_page
for relevant actions in our controllers, but as a definite re-assurance it seems ok.