Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 373
- Log:
Initial import of Radiant 0.9.1, which is now packaged as a gem. This is an
import of the tagged 0.9.1 source checked out from GitHub, which isn't quite
the same as the gem distribution - but it doesn't seem to be available in an
archived form and the installed gem already has modifications, so this is
the closest I can get.
- Author:
- rool
- Date:
- Mon Mar 21 13:40:05 +0000 2011
- Size:
- 7039 Bytes
1 | require 'fileutils' |
2 | require 'uri' |
3 | |
4 | module ActionController #:nodoc: |
5 | module Caching |
6 | # Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server |
7 | # can serve without going through Action Pack. This is the fastest way to cache your content as opposed to going dynamically |
8 | # through the process of generating the content. Unfortunately, this incredible speed-up is only available to stateless pages |
9 | # where all visitors are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are |
10 | # a great fit for this approach, but account-based systems where people log in and manipulate their own data are often less |
11 | # likely candidates. |
12 | # |
13 | # Specifying which actions to cache is done through the <tt>caches_page</tt> class method: |
14 | # |
15 | # class WeblogController < ActionController::Base |
16 | # caches_page :show, :new |
17 | # end |
18 | # |
19 | # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, |
20 | # which match the URLs used to trigger the dynamic generation. This is how the web server is able |
21 | # pick up a cache file when it exists and otherwise let the request pass on to Action Pack to generate it. |
22 | # |
23 | # Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache |
24 | # is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends: |
25 | # |
26 | # class WeblogController < ActionController::Base |
27 | # def update |
28 | # List.update(params[:list][:id], params[:list]) |
29 | # expire_page :action => "show", :id => params[:list][:id] |
30 | # redirect_to :action => "show", :id => params[:list][:id] |
31 | # end |
32 | # end |
33 | # |
34 | # Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be |
35 | # expired. |
36 | module Pages |
37 | def self.included(base) #:nodoc: |
38 | base.extend(ClassMethods) |
39 | base.class_eval do |
40 | @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : "" |
41 | ## |
42 | # :singleton-method: |
43 | # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>. |
44 | # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing |
45 | # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your |
46 | # web server to look in the new location for cached files. |
47 | cattr_accessor :page_cache_directory |
48 | |
49 | @@page_cache_extension = '.html' |
50 | ## |
51 | # :singleton-method: |
52 | # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in |
53 | # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>. |
54 | # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an |
55 | # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps. |
56 | cattr_accessor :page_cache_extension |
57 | end |
58 | end |
59 | |
60 | module ClassMethods |
61 | # Expires the page that was cached with the +path+ as a key. Example: |
62 | # expire_page "/lists/show" |
63 | def expire_page(path) |
64 | return unless perform_caching |
65 | |
66 | benchmark "Expired page: #{page_cache_file(path)}" do |
67 | File.delete(page_cache_path(path)) if File.exist?(page_cache_path(path)) |
68 | end |
69 | end |
70 | |
71 | # Manually cache the +content+ in the key determined by +path+. Example: |
72 | # cache_page "I'm the cached content", "/lists/show" |
73 | def cache_page(content, path) |
74 | return unless perform_caching |
75 | |
76 | benchmark "Cached page: #{page_cache_file(path)}" do |
77 | FileUtils.makedirs(File.dirname(page_cache_path(path))) |
78 | File.open(page_cache_path(path), "wb+") { |f| f.write(content) } |
79 | end |
80 | end |
81 | |
82 | # Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that |
83 | # matches the triggering url. |
84 | # |
85 | # Usage: |
86 | # |
87 | # # cache the index action |
88 | # caches_page :index |
89 | # |
90 | # # cache the index action except for JSON requests |
91 | # caches_page :index, :if => Proc.new { |c| !c.request.format.json? } |
92 | def caches_page(*actions) |
93 | return unless perform_caching |
94 | options = actions.extract_options! |
95 | after_filter({:only => actions}.merge(options)) { |c| c.cache_page } |
96 | end |
97 | |
98 | private |
99 | def page_cache_file(path) |
100 | name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/')) |
101 | name << page_cache_extension unless (name.split('/').last || name).include? '.' |
102 | return name |
103 | end |
104 | |
105 | def page_cache_path(path) |
106 | page_cache_directory + page_cache_file(path) |
107 | end |
108 | end |
109 | |
110 | # Expires the page that was cached with the +options+ as a key. Example: |
111 | # expire_page :controller => "lists", :action => "show" |
112 | def expire_page(options = {}) |
113 | return unless perform_caching |
114 | |
115 | if options.is_a?(Hash) |
116 | if options[:action].is_a?(Array) |
117 | options[:action].dup.each do |action| |
118 | self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :action => action))) |
119 | end |
120 | else |
121 | self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true))) |
122 | end |
123 | else |
124 | self.class.expire_page(options) |
125 | end |
126 | end |
127 | |
128 | # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used |
129 | # If no options are provided, the requested url is used. Example: |
130 | # cache_page "I'm the cached content", :controller => "lists", :action => "show" |
131 | def cache_page(content = nil, options = nil) |
132 | return unless perform_caching && caching_allowed |
133 | |
134 | path = case options |
135 | when Hash |
136 | url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format])) |
137 | when String |
138 | options |
139 | else |
140 | request.path |
141 | end |
142 | |
143 | self.class.cache_page(content || response.body, path) |
144 | end |
145 | |
146 | private |
147 | def caching_allowed |
148 | request.get? && response.status.to_i == 200 |
149 | end |
150 | end |
151 | end |
152 | end |