Dynamic Page Caching With Prototype

Ryan Bates recently released a Railscast on Dynamic Page Caching. This technique caches each page and then updates the page’s dynamic content using Javascript.

Although I like this technique, it requires the jQuery javascript library whereas Rails ships with the Prototype library and so there’s a high chance your application already relies on Prototype.

Here is a variation on Ryan Bate’s technique you can implement with Prototype:

Assuming you already have a controller named UsersController and a show action, add the following view to views/users/show.js.erb:

document.observe("dom:loaded", function () {
  <% if logged_in? %>
    $$('.login_required').invoke('show');  //reveals all of the hidden links
  <% end %>
  $('content_div').insert('<%= escape_javascript render(:partial => "layouts/flash_notices") %>');  // inserts the flash partial
});

and then this line in the head of your layouts:

<%= javascript_include_tag "prototype", "/users/show/current" %>

I’ve made a few assumptions here but you can change the javascript to suit your needs. The first line here ensures that the javascript is run once the page has loaded.

The second line assumes you have a helper method called logged_in?. The code within this block will only be run if there is a logged in user.

Line three uses the prototype $$ function to find all tags with class name “login_required” and sets the CSS display value to “show”. This is the part that actually displays or hides the dynamic content depending on whether there’s a logged in user.

Finally, line five assumes you have a div on page with id “content_div” and inserts the partial “layouts/flash_notices” inside it. Without this, the flash messages will not be displayed when loading cached pages. Using the insert() function you can insert html content into various objects within the DOM. This method also includes a second argument to specify whether the content should be added as the first or last child. Read the Prototype API for more info.

“flash_notices” is a partial in the views/layouts folder that looks something like this. Next, add this helper method to ApplicationHelper:

module ApplicationHelper
  def admin_links( id = nil, &block )
    concat "<div class='login_required' style='display:none;' #{ "id='#{id}'" if id }>"
    yield
    concat "</div>"
    nil
  end
end

Now, if you want to hide links from non-authorised users you simply have to stick it inside an admin_link block like so:

# content here will be visible to everyone
<% admin_link do %>
  # content here won't be visible to non-authorised users
<% end %>
# content here will be visible to everyone

This will output:

# content here will be visible to everyone
<div class='login_required' style='display:none;'>
  # content here won't be visible to non-authorised users
</div>
# content here will be visible to everyone

You can also specify an id for the div by passing this as an argument to admin_links:

# content here will be visible to everyone
<% admin_link "id_for_my_div" do %>
  # content here won't be visible to non-authorised users
<% end %>
# content here will be visible to everyone

Just make sure you have set perform_caching to true in config/environments/development.rb.

Written by

Photo of Gavin Morrice
Gavin Morrice

Software engineer based in Scotland

Connect with me