ProblemOne of the pages of your application contains several sections that are generated dynamically. You want to control performance by caching certain sections while leaving others dynamic. SolutionRails provides fragment caching to let you control which sections of a page are to be cached and which are to remain truly dynamic. You can even cache several sections of a page individually and have different criteria for how each section's cache is expired. To specify the type of fragment store you want Rails to use: config/environment.rb: ActionController::Base.fragment_cache_store = :file_store, %W( #{RAILS_ROOT}/public/frags ) This tells Rails to store individual fragments in the public/frags directory. Fragment caching make the most sense when you have an expensive query that's used to produce some rendered output. To demonstrate fragment caching, let the following get_time class method of the Invoice model play the part of a custom query that may take some time to execute: app/models/invoice.rb: class Invoice < ActiveRecord::Base def self.get_time find_by_sql("select now() as time;")[0].time end end The following view template displays three different versions of the output of Invoice#get_time, which is made available to the show.rhtml template via the @report instance variable: app/views/reports/show.rhtml: <h1>Reports</h1> <%= link_to "show", :action => "show" %> | <%= link_to "expire_one", :action => "expire_one" %> | <%= link_to "expire_all", :action => "expire_all" %> <br /><hr /> <%= @report %><br /> <% cache(:action => "show", :id => "report_one") do %> <%= @report %><br /> <% end %> <% cache(:action => "show", :id => "report_two") do %> <%= @report %><br /> <% end %> The first occurrence of @report is displayed without any caching. The second two occurrences are each wrapped in a block and passed to the cache view helper. The cache helper stores each fragment in a file identified by the url_for style option hash that you pass it. In this example, the two fragments created are distinguished by their unique values of the id key. The Reports Controller defines the following actions that demonstrate how you can expire each fragment on a page individually: app/controllers/reports_controller.rb: class ReportsController < ApplicationController def show @report = Invoice.get_time end def expire_one @report = Invoice.get_time expire_fragment(:action => "show", :id => "report_one") redirect_to :action => "show" end def expire_all @report = Invoice.get_time expire_fragment(%r{show/.*}) redirect_to :action => "show" end end The show action populates the @report instance variable and renders the show.rhtml template. The first time the template is rendered, each call to the cache helper generates a cached version of the block it surrounds. The expire_one action demonstrates how you can expire a specific fragment by referencing it with the same url_for options hash that was used to create the cache fragment. The expire_all action shows how to remove all fragments that match a regular expression. DiscussionFragment caching is slower than page caching, but you trade some performance for the control of caching specific portions of a page while leaving others dynamic. The solution stores two cache files in the cache directory on the file system specified by #{RAILS_ROOT}/public/frags. The command shows these files: $ ls public/frags/localhost.3000/reports/show report_one.cache report_two.cache Notice the subdirectory that is created is named after the host and port number of the server. This can help distinguish fragments that may differ only by the subdomain name (e.g., rob.tupleshop.com/reports/show, tim.tupleshop.com/reports/show would create two distinct cache files). Like Rails session data storage, you have several options to store cached fragments. The solution demonstrates storing fragments on your filesystem in the directory specified. Four storage options are listed; select one based on the specifics of your deployment setup or whichever proves to be fastest:
See Also
|