Caching JSON calls in Rails with Memcached and Dalli

I found lots of examples on how to use low level rails caching but they were basic, and it didn’t explain what config lines to change or how to implement it. Hopefully here’s a fuller example of caching some remote JSON. The requirement was that footer links were set in one place (our main site), then other secondary sites to use the same footer links from one source. We had our main django/wagtail site produce some JSON for our footer links, but obviously if our other sites just consume that JSON on every page request that’s going to be a lot of traffic. Hence the need for caching, if the secondary sites just grab the JSON once a day, then that’s going to be preferable.

Thanks to Ryan Tyler and Neil Williams for some of the code and their advice.

First of install Memcached, there are plenty of tutorials online for this, we won’t go into it here. We assume you’re running memcached with rails, not on a separate server.

Next install the Dalli Gem, add a line like gem “dalli”, “2.7.0” to your gemfile, we locked to a specific version that worked with our rails 3.2 version. Then bundle install, Dalli will allow us to interact with Memcached.

Add these lines to your production.rb config file, to test in development run in production mode, I’ve been told it’s best not to have caching stuff in the development environment config :

config.action_controller.perform_caching = true
config.cache_store = :dalli_store

Next setup some code in your application_controller.rb (Add some exception notification if required)
At the beginning add the code below, especially the before_filter to call the JSON caching code:

require ‘net/http’
require ‘json’

before_filter :main_footer

then the bit that does all the work:

def main_footer
Rails.cache.fetch("main_footer_json", :expires_in => 24.hours) do
begin
json = []
url = "http://www.yourothersite.com/some.json"
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(url)
response = http.request(request)
json = JSON.parse(response.body)
json.to_json
rescue
json = []
json.to_json
end
end
@main_footer = Rails.cache.read("main_footer_json")

end

If it can’t find the remote JSON, it returns an empty JSON string. The JSON is kept in an instance variable that can be accessed in our footer template below.

Then in our _footer.html.erb we add this code to display the JSON as footer links :

<ul class="nav navbar-nav">
<% JSON.parse(@main_footer).each do |foot_main_link| %>
<li><a href="<%= foot_main_link[‘link’] %>">
<%= foot_main_link[‘title’] %></a></li>
<% end %>
</ul>