Pre-Caching a page in Rails

I recently had to figure out how to basically freeze a page in a rails app so that any changes to the dynamic data would not change the front facing view.

The app has a “web page” that is created dynamically and is then “published” to the web on a weekly basis. As this is always the same view, I needed to figure out how to save the view from each week. StackOverflow led me to the best option being to cache the page.

Easy enough, but what happens if nobody visits the page for one week? It is never cached. So after some googling I found that I could curl the page from the app and that cur (curl is a client to get documents/files from or send documents to a server, using any of the supported protocols (HTTP, HTTPS, FTP, GOPHER, DICT, TELNET, LDAP or FILE). The command is designed to work without user interaction or any kind of interactivity.) l would be sufficient enough to make the page cache.

I set the controller to caches_page on the show action. Then in the controller that performs the “publishing” I set it to clear the cached page. Nothing happened. I manually cleared the cache both in the project as well as in the browser and the page still displayed!

I finally found that the cached page was in my public folder and not the tmp folder. At first I thought that it was something that I set up incorrectly but then realized that it was how the page was setup in the first place. As this is a front facing page requiring no authentication it was named spaced in the controllers/public directory with the views in the /public directory. So that is why the cached pages were going to the public directory and not to the tmp directory as is the norm.

Now I had to manually find the cached copy and remove it.

As there really is not a need for the page to be instantly cached and no need to the user to wait for it, I moved it into a background job using Sidekiq.

So first I created a helper method to do some of the initial processing.  To the controller that does the publishing I need to add a couple of things:

[code language="ruby"]

include CacheHelper ## this tells it to use the helper
update_web_cache ## this is called in the action to publish

[/code]

So when the user publishes it will call the CacheHelper:

[code language="ruby"]
## file cache_helper.rb
module CacheHelper

def update_web_cache
FileUtils.rm_rf(Dir["#{Rails.root}/public/path_to_cached_view.html"])
url = "#{ENV['HOSTNAME']}" + path_to_the_view_needed_to_be_cached
CacheWorker.perform_async(url)
end
end
[/code]

First I had to manually remove the cached view and I did so using FileUtils. Then as this is in the helper I have access to my model and therefor I can get the path to the view based on the model. Last, I tell Sidekiq to do it's thing.

OK, so to this point we have removed the old cache but now we need to create the new cache. After a lot more googling the only way I could figure out how to curl the page was with a rake task. Further down the rabbit hole...

In the Sidekiq worker, I added the code to run the rake task. There were a few options to do this but I found the execute command to be the easiest and it worked.

[code language="ruby"]
## file cache_worker.rb
class CacheWorker
include Sidekiq::Worker

def perform(url)
%x(bundle exec rake curl["#{url}"])
end
end
[/code]

This then takes the url that it is passed and runs the rake task:

[code language="ruby"]
## file lib/tasks/curl.rake
desc "curl"
task :curl, [:url] => :environment do |task, args|
system "curl #{args.url}"
end
[/code]

Bottom line, created a background job that calls a rake task to curl a page.
Now, if there is any easier way to do this I sure would love to know.

comments powered by Disqus