openstreetmap-actionpack-page_caching 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs = ["test"]
7
+ t.pattern = "test/**/*_test.rb"
8
+ t.ruby_opts = ["-w"]
9
+ end
10
+
11
+ task default: :test
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = "openstreetmap-actionpack-page_caching"
3
+ gem.version = "1.1.2"
4
+ gem.author = "David Heinemeier Hansson"
5
+ gem.email = "tom@compton.nu"
6
+ gem.description = "Static page caching for Action Pack (removed from core in Rails 4.0)"
7
+ gem.summary = "Static page caching for Action Pack (removed from core in Rails 4.0)"
8
+ gem.homepage = "https://github.com/rails/actionpack-page_caching"
9
+ gem.license = "MIT"
10
+
11
+ gem.required_ruby_version = '>= 2.4.6'
12
+ gem.files = `git ls-files`.split($/)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.require_paths = ["lib"]
16
+ gem.license = 'MIT'
17
+
18
+ gem.add_dependency "actionpack", ">= 5.0.0"
19
+
20
+ gem.add_development_dependency "mocha"
21
+ end
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rails", github: "rails/rails", branch: "5-0-stable"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rails", github: "rails/rails", branch: "5-1-stable"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rails", github: "rails/rails", branch: "5-2-stable"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rails", github: "rails/rails", branch: "6-0-stable"
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rails", github: "rails/rails", branch: "master"
6
+ gem "arel", github: "rails/arel", branch: "master"
@@ -0,0 +1,306 @@
1
+ require "fileutils"
2
+ require "uri"
3
+ require "active_support/core_ext/class/attribute_accessors"
4
+ require "active_support/core_ext/string/strip"
5
+
6
+ module ActionController
7
+ module Caching
8
+ # Page caching is an approach to caching where the entire action output of is
9
+ # stored as a HTML file that the web server can serve without going through
10
+ # Action Pack. This is the fastest way to cache your content as opposed to going
11
+ # dynamically through the process of generating the content. Unfortunately, this
12
+ # incredible speed-up is only available to stateless pages where all visitors are
13
+ # treated the same. Content management systems -- including weblogs and wikis --
14
+ # have many pages that are a great fit for this approach, but account-based systems
15
+ # where people log in and manipulate their own data are often less likely candidates.
16
+ #
17
+ # Specifying which actions to cache is done through the +caches_page+ class method:
18
+ #
19
+ # class WeblogController < ActionController::Base
20
+ # caches_page :show, :new
21
+ # end
22
+ #
23
+ # This will generate cache files such as <tt>weblog/show/5.html</tt> and
24
+ # <tt>weblog/new.html</tt>, which match the URLs used that would normally trigger
25
+ # dynamic page generation. Page caching works by configuring a web server to first
26
+ # check for the existence of files on disk, and to serve them directly when found,
27
+ # without passing the request through to Action Pack. This is much faster than
28
+ # handling the full dynamic request in the usual way.
29
+ #
30
+ # Expiration of the cache is handled by deleting the cached file, which results
31
+ # in a lazy regeneration approach where the cache is not restored before another
32
+ # hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
33
+ #
34
+ # class WeblogController < ActionController::Base
35
+ # def update
36
+ # List.update(params[:list][:id], params[:list])
37
+ # expire_page action: "show", id: params[:list][:id]
38
+ # redirect_to action: "show", id: params[:list][:id]
39
+ # end
40
+ # end
41
+ #
42
+ # Additionally, you can expire caches using Sweepers that act on changes in
43
+ # the model to determine when a cache is supposed to be expired.
44
+ module Pages
45
+ extend ActiveSupport::Concern
46
+
47
+ included do
48
+ # The cache directory should be the document root for the web server and is
49
+ # set using <tt>Base.page_cache_directory = "/document/root"</tt>. For Rails,
50
+ # this directory has already been set to Rails.public_path (which is usually
51
+ # set to <tt>Rails.root + "/public"</tt>). Changing this setting can be useful
52
+ # to avoid naming conflicts with files in <tt>public/</tt>, but doing so will
53
+ # likely require configuring your web server to look in the new location for
54
+ # cached files.
55
+ class_attribute :page_cache_directory
56
+ self.page_cache_directory ||= ""
57
+
58
+ # The compression used for gzip. If +false+ (default), the page is not compressed.
59
+ # If can be a symbol showing the ZLib compression method, for example, <tt>:best_compression</tt>
60
+ # or <tt>:best_speed</tt> or an integer configuring the compression level.
61
+ class_attribute :page_cache_compression
62
+ self.page_cache_compression ||= false
63
+ end
64
+
65
+ class PageCache #:nodoc:
66
+ def initialize(cache_directory, default_extension, controller = nil)
67
+ @cache_directory = cache_directory
68
+ @default_extension = default_extension
69
+ @controller = controller
70
+ end
71
+
72
+ def expire(path)
73
+ instrument :expire_page, path do
74
+ delete(cache_path(path))
75
+ end
76
+ end
77
+
78
+ def cache(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
79
+ instrument :write_page, path do
80
+ write(content, cache_path(path, extension), gzip)
81
+ end
82
+ end
83
+
84
+ private
85
+ def cache_directory
86
+ case @cache_directory
87
+ when Proc
88
+ handle_proc_cache_directory
89
+ when Symbol
90
+ handle_symbol_cache_directory
91
+ else
92
+ handle_default_cache_directory
93
+ end
94
+ end
95
+
96
+ def handle_proc_cache_directory
97
+ if @controller
98
+ @controller.instance_exec(&@cache_directory)
99
+ else
100
+ raise_runtime_error
101
+ end
102
+ end
103
+
104
+ def handle_symbol_cache_directory
105
+ if @controller
106
+ @controller.send(@cache_directory)
107
+ else
108
+ raise_runtime_error
109
+ end
110
+ end
111
+
112
+ def handle_callable_cache_directory
113
+ if @controller
114
+ @cache_directory.call(@controller.request)
115
+ else
116
+ raise_runtime_error
117
+ end
118
+ end
119
+
120
+ def handle_default_cache_directory
121
+ if @cache_directory.respond_to?(:call)
122
+ handle_callable_cache_directory
123
+ else
124
+ @cache_directory.to_s
125
+ end
126
+ end
127
+
128
+ def raise_runtime_error
129
+ raise RuntimeError, <<-MSG.strip_heredoc
130
+ Dynamic page_cache_directory used with class-level cache_page method
131
+
132
+ You have specified either a Proc, Symbol or callable object for page_cache_directory
133
+ which needs to be executed within the context of a request. If you need to call the
134
+ cache_page method from a class-level context then set the page_cache_directory to a
135
+ static value and override the setting at the instance-level using before_action.
136
+ MSG
137
+ end
138
+
139
+ def default_extension
140
+ @default_extension
141
+ end
142
+
143
+ def cache_file(path, extension)
144
+ if path.empty? || path =~ %r{\A/+\z}
145
+ name = "/index"
146
+ else
147
+ name = URI.parser.unescape(path.chomp("/"))
148
+ end
149
+
150
+ if File.extname(name).empty?
151
+ name + (extension || default_extension)
152
+ else
153
+ name
154
+ end
155
+ end
156
+
157
+ def cache_path(path, extension = nil)
158
+ File.join(cache_directory, cache_file(path, extension))
159
+ end
160
+
161
+ def delete(path)
162
+ File.delete(path) if File.exist?(path)
163
+ File.delete(path + ".gz") if File.exist?(path + ".gz")
164
+ end
165
+
166
+ def write(content, path, gzip)
167
+ FileUtils.makedirs(File.dirname(path))
168
+ File.open(path, "wb+") { |f| f.write(content) }
169
+
170
+ if gzip
171
+ Zlib::GzipWriter.open(path + ".gz", gzip) { |f| f.write(content) }
172
+ end
173
+ end
174
+
175
+ def instrument(name, path)
176
+ ActiveSupport::Notifications.instrument("#{name}.action_controller", path: path) { yield }
177
+ end
178
+ end
179
+
180
+ module ClassMethods
181
+ # Expires the page that was cached with the +path+ as a key.
182
+ #
183
+ # expire_page "/lists/show"
184
+ def expire_page(path)
185
+ if perform_caching
186
+ page_cache.expire(path)
187
+ end
188
+ end
189
+
190
+ # Manually cache the +content+ in the key determined by +path+.
191
+ #
192
+ # cache_page "I'm the cached content", "/lists/show"
193
+ def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
194
+ if perform_caching
195
+ page_cache.cache(content, path, extension, gzip)
196
+ end
197
+ end
198
+
199
+ # Caches the +actions+ using the page-caching approach that'll store
200
+ # the cache in a path within the +page_cache_directory+ that
201
+ # matches the triggering url.
202
+ #
203
+ # You can also pass a <tt>:gzip</tt> option to override the class configuration one.
204
+ #
205
+ # # cache the index action
206
+ # caches_page :index
207
+ #
208
+ # # cache the index action except for JSON requests
209
+ # caches_page :index, if: Proc.new { !request.format.json? }
210
+ #
211
+ # # don't gzip images
212
+ # caches_page :image, gzip: false
213
+ def caches_page(*actions)
214
+ if perform_caching
215
+ options = actions.extract_options!
216
+
217
+ gzip_level = options.fetch(:gzip, page_cache_compression)
218
+ gzip_level = \
219
+ case gzip_level
220
+ when Symbol
221
+ Zlib.const_get(gzip_level.upcase)
222
+ when Integer
223
+ gzip_level
224
+ when false
225
+ nil
226
+ else
227
+ Zlib::BEST_COMPRESSION
228
+ end
229
+
230
+ after_action({ only: actions }.merge(options)) do |c|
231
+ c.cache_page(nil, nil, gzip_level)
232
+ end
233
+ end
234
+ end
235
+
236
+ private
237
+ def page_cache
238
+ PageCache.new(page_cache_directory, default_static_extension)
239
+ end
240
+ end
241
+
242
+ # Expires the page that was cached with the +options+ as a key.
243
+ #
244
+ # expire_page controller: "lists", action: "show"
245
+ def expire_page(options = {})
246
+ if perform_caching?
247
+ case options
248
+ when Hash
249
+ case options[:action]
250
+ when Array
251
+ options[:action].each { |action| expire_page(options.merge(action: action)) }
252
+ else
253
+ page_cache.expire(url_for(options.merge(only_path: true)))
254
+ end
255
+ else
256
+ page_cache.expire(options)
257
+ end
258
+ end
259
+ end
260
+
261
+ # Manually cache the +content+ in the key determined by +options+. If no content is provided,
262
+ # the contents of response.body is used. If no options are provided, the url of the current
263
+ # request being handled is used.
264
+ #
265
+ # cache_page "I'm the cached content", controller: "lists", action: "show"
266
+ def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
267
+ if perform_caching? && caching_allowed?
268
+ path = \
269
+ case options
270
+ when Hash
271
+ url_for(options.merge(only_path: true, format: params[:format]))
272
+ when String
273
+ options
274
+ else
275
+ request.path
276
+ end
277
+
278
+ type = if self.respond_to?(:media_type)
279
+ Mime::LOOKUP[self.media_type]
280
+ else
281
+ Mime::LOOKUP[self.content_type]
282
+ end
283
+
284
+ if type && (type_symbol = type.symbol).present?
285
+ extension = ".#{type_symbol}"
286
+ end
287
+
288
+ page_cache.cache(content || response.body, path, extension, gzip)
289
+ end
290
+ end
291
+
292
+ def caching_allowed?
293
+ (request.get? || request.head?) && response.status == 200
294
+ end
295
+
296
+ def perform_caching?
297
+ self.class.perform_caching
298
+ end
299
+
300
+ private
301
+ def page_cache
302
+ PageCache.new(page_cache_directory, default_static_extension, self)
303
+ end
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,13 @@
1
+ require "action_controller/caching/pages"
2
+
3
+ module ActionController
4
+ module Caching
5
+ eager_autoload do
6
+ autoload :Pages
7
+ end
8
+
9
+ include Pages
10
+ end
11
+ end
12
+
13
+ ActionController::Base.send(:include, ActionController::Caching::Pages)
@@ -0,0 +1 @@
1
+ require "actionpack/page_caching/railtie"
@@ -0,0 +1,17 @@
1
+ require "rails/railtie"
2
+
3
+ module ActionPack
4
+ module PageCaching
5
+ class Railtie < Rails::Railtie
6
+ initializer "action_pack.page_caching" do
7
+ ActiveSupport.on_load(:action_controller) do
8
+ require "action_controller/page_caching"
9
+ end
10
+ end
11
+
12
+ initializer "action_pack.page_caching.set_config", before: "action_controller.set_configs" do |app|
13
+ app.config.action_controller.page_cache_directory ||= app.config.paths["public"].first
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ require "bundler/setup"
2
+ require "minitest/autorun"
3
+ require "action_controller"
4
+ require "action_controller/page_caching"
5
+
6
+ if ActiveSupport.respond_to?(:test_order)
7
+ ActiveSupport.test_order = :random
8
+ end
9
+
10
+ if ActionController::Base.respond_to?(:enable_fragment_cache_logging=)
11
+ ActionController::Base.enable_fragment_cache_logging = true
12
+ end
@@ -0,0 +1,524 @@
1
+ require "abstract_unit"
2
+ require "mocha/setup"
3
+
4
+ CACHE_DIR = "test_cache"
5
+ # Don't change "../tmp" cavalierly or you might hose something you don't want hosed
6
+ TEST_TMP_DIR = File.expand_path("../tmp", __FILE__)
7
+ FILE_STORE_PATH = File.join(TEST_TMP_DIR, CACHE_DIR)
8
+
9
+ module PageCachingTestHelpers
10
+ def setup
11
+ super
12
+
13
+ @routes = ActionDispatch::Routing::RouteSet.new
14
+
15
+ FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
16
+ FileUtils.mkdir_p(FILE_STORE_PATH)
17
+ end
18
+
19
+ def teardown
20
+ super
21
+
22
+ FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
23
+ @controller.perform_caching = false
24
+ end
25
+
26
+ private
27
+
28
+ def assert_page_cached(action, options = {})
29
+ expected = options[:content] || action.to_s
30
+ path = cache_file(action, options)
31
+
32
+ assert File.exist?(path), "The cache file #{path} doesn't exist"
33
+
34
+ if File.extname(path) == ".gz"
35
+ actual = Zlib::GzipReader.open(path) { |f| f.read }
36
+ else
37
+ actual = File.read(path)
38
+ end
39
+
40
+ assert_equal expected, actual, "The cached content doesn't match the expected value"
41
+ end
42
+
43
+ def assert_page_not_cached(action, options = {})
44
+ path = cache_file(action, options)
45
+ assert !File.exist?(path), "The cache file #{path} still exists"
46
+ end
47
+
48
+ def cache_file(action, options = {})
49
+ path = options[:path] || FILE_STORE_PATH
50
+ controller = options[:controller] || self.class.name.underscore
51
+ format = options[:format] || "html"
52
+
53
+ "#{path}/#{controller}/#{action}.#{format}"
54
+ end
55
+
56
+ def draw(&block)
57
+ @routes = ActionDispatch::Routing::RouteSet.new
58
+ @routes.draw(&block)
59
+ @controller.extend(@routes.url_helpers)
60
+ end
61
+ end
62
+
63
+ class CachingMetalController < ActionController::Metal
64
+ abstract!
65
+
66
+ include AbstractController::Callbacks
67
+ include ActionController::Caching
68
+
69
+ self.page_cache_directory = FILE_STORE_PATH
70
+ self.cache_store = :file_store, FILE_STORE_PATH
71
+ end
72
+
73
+ class PageCachingMetalTestController < CachingMetalController
74
+ caches_page :ok
75
+
76
+ def ok
77
+ self.response_body = "ok"
78
+ end
79
+ end
80
+
81
+ class PageCachingMetalTest < ActionController::TestCase
82
+ include PageCachingTestHelpers
83
+ tests PageCachingMetalTestController
84
+
85
+ def test_should_cache_get_with_ok_status
86
+ draw do
87
+ get "/page_caching_metal_test/ok", to: "page_caching_metal_test#ok"
88
+ end
89
+
90
+ get :ok
91
+ assert_response :ok
92
+ assert_page_cached :ok
93
+ end
94
+ end
95
+
96
+ ActionController::Base.page_cache_directory = FILE_STORE_PATH
97
+
98
+ class CachingController < ActionController::Base
99
+ abstract!
100
+
101
+ self.cache_store = :file_store, FILE_STORE_PATH
102
+
103
+ protected
104
+ if ActionPack::VERSION::STRING < "4.1"
105
+ def render(options)
106
+ if options.key?(:html)
107
+ super({ text: options.delete(:html) }.merge(options))
108
+ else
109
+ super
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ class PageCachingTestController < CachingController
116
+ self.page_cache_compression = :best_compression
117
+
118
+ caches_page :ok, :no_content, if: Proc.new { |c| !c.request.format.json? }
119
+ caches_page :found, :not_found
120
+ caches_page :about_me
121
+ caches_page :default_gzip
122
+ caches_page :no_gzip, gzip: false
123
+ caches_page :gzip_level, gzip: :best_speed
124
+
125
+ def ok
126
+ render html: "ok"
127
+ end
128
+
129
+ def no_content
130
+ head :no_content
131
+ end
132
+
133
+ def found
134
+ redirect_to action: "ok"
135
+ end
136
+
137
+ def not_found
138
+ head :not_found
139
+ end
140
+
141
+ def custom_path
142
+ render html: "custom_path"
143
+ cache_page(nil, "/index.html")
144
+ end
145
+
146
+ def default_gzip
147
+ render html: "default_gzip"
148
+ end
149
+
150
+ def no_gzip
151
+ render html: "no_gzip"
152
+ end
153
+
154
+ def gzip_level
155
+ render html: "gzip_level"
156
+ end
157
+
158
+ def expire_custom_path
159
+ expire_page("/index.html")
160
+ head :ok
161
+ end
162
+
163
+ def trailing_slash
164
+ render html: "trailing_slash"
165
+ end
166
+
167
+ def about_me
168
+ respond_to do |format|
169
+ format.html { render html: "I am html" }
170
+ format.xml { render xml: "I am xml" }
171
+ end
172
+ end
173
+ end
174
+
175
+ class PageCachingTest < ActionController::TestCase
176
+ include PageCachingTestHelpers
177
+ tests PageCachingTestController
178
+
179
+ def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
180
+ draw do
181
+ get "posts.:format", to: "posts#index", as: :formatted_posts
182
+ get "/", to: "posts#index", as: :main
183
+ end
184
+
185
+ defaults = { controller: "posts", action: "index", only_path: true }
186
+
187
+ assert_equal "/posts.rss", @routes.url_for(defaults.merge(format: "rss"))
188
+ assert_equal "/", @routes.url_for(defaults.merge(format: nil))
189
+ end
190
+
191
+ def test_should_cache_head_with_ok_status
192
+ draw do
193
+ get "/page_caching_test/ok", to: "page_caching_test#ok"
194
+ end
195
+
196
+ head :ok
197
+ assert_response :ok
198
+ assert_page_cached :ok
199
+ end
200
+
201
+ def test_should_cache_get_with_ok_status
202
+ draw do
203
+ get "/page_caching_test/ok", to: "page_caching_test#ok"
204
+ end
205
+
206
+ get :ok
207
+ assert_response :ok
208
+ assert_page_cached :ok
209
+ end
210
+
211
+ def test_should_cache_with_custom_path
212
+ draw do
213
+ get "/page_caching_test/custom_path", to: "page_caching_test#custom_path"
214
+ end
215
+
216
+ get :custom_path
217
+ assert_page_cached :index, controller: ".", content: "custom_path"
218
+ end
219
+
220
+ def test_should_expire_cache_with_custom_path
221
+ draw do
222
+ get "/page_caching_test/custom_path", to: "page_caching_test#custom_path"
223
+ get "/page_caching_test/expire_custom_path", to: "page_caching_test#expire_custom_path"
224
+ end
225
+
226
+ get :custom_path
227
+ assert_page_cached :index, controller: ".", content: "custom_path"
228
+
229
+ get :expire_custom_path
230
+ assert_page_not_cached :index, controller: ".", content: "custom_path"
231
+ end
232
+
233
+ def test_should_gzip_cache
234
+ draw do
235
+ get "/page_caching_test/custom_path", to: "page_caching_test#custom_path"
236
+ get "/page_caching_test/expire_custom_path", to: "page_caching_test#expire_custom_path"
237
+ end
238
+
239
+ get :custom_path
240
+ assert_page_cached :index, controller: ".", format: "html.gz", content: "custom_path"
241
+
242
+ get :expire_custom_path
243
+ assert_page_not_cached :index, controller: ".", format: "html.gz"
244
+ end
245
+
246
+ def test_should_allow_to_disable_gzip
247
+ draw do
248
+ get "/page_caching_test/no_gzip", to: "page_caching_test#no_gzip"
249
+ end
250
+
251
+ get :no_gzip
252
+ assert_page_cached :no_gzip, format: "html"
253
+ assert_page_not_cached :no_gzip, format: "html.gz"
254
+ end
255
+
256
+ def test_should_use_config_gzip_by_default
257
+ draw do
258
+ get "/page_caching_test/default_gzip", to: "page_caching_test#default_gzip"
259
+ end
260
+
261
+ @controller.expects(:cache_page).with(nil, nil, Zlib::BEST_COMPRESSION)
262
+ get :default_gzip
263
+ end
264
+
265
+ def test_should_set_gzip_level
266
+ draw do
267
+ get "/page_caching_test/gzip_level", to: "page_caching_test#gzip_level"
268
+ end
269
+
270
+ @controller.expects(:cache_page).with(nil, nil, Zlib::BEST_SPEED)
271
+ get :gzip_level
272
+ end
273
+
274
+ def test_should_cache_without_trailing_slash_on_url
275
+ @controller.class.cache_page "cached content", "/page_caching_test/trailing_slash"
276
+ assert_page_cached :trailing_slash, content: "cached content"
277
+ end
278
+
279
+ def test_should_obey_http_accept_attribute
280
+ draw do
281
+ get "/page_caching_test/about_me", to: "page_caching_test#about_me"
282
+ end
283
+
284
+ @request.env["HTTP_ACCEPT"] = "text/xml"
285
+ get :about_me
286
+ assert_equal "I am xml", @response.body
287
+ assert_page_cached :about_me, format: "xml", content: "I am xml"
288
+ end
289
+
290
+ def test_cached_page_should_not_have_trailing_slash_even_if_url_has_trailing_slash
291
+ @controller.class.cache_page "cached content", "/page_caching_test/trailing_slash/"
292
+ assert_page_cached :trailing_slash, content: "cached content"
293
+ end
294
+
295
+ def test_should_cache_ok_at_custom_path
296
+ draw do
297
+ get "/page_caching_test/ok", to: "page_caching_test#ok"
298
+ end
299
+
300
+ @request.env["PATH_INFO"] = "/index.html"
301
+ get :ok
302
+ assert_response :ok
303
+ assert_page_cached :index, controller: ".", content: "ok"
304
+ end
305
+
306
+ [:ok, :no_content, :found, :not_found].each do |status|
307
+ [:get, :post, :patch, :put, :delete].each do |method|
308
+ unless method == :get && status == :ok
309
+ define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
310
+ draw do
311
+ get "/page_caching_test/ok", to: "page_caching_test#ok"
312
+ match "/page_caching_test/#{status}", to: "page_caching_test##{status}", via: method
313
+ end
314
+
315
+ send(method, status)
316
+ assert_response status
317
+ assert_page_not_cached status
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ def test_page_caching_conditional_options
324
+ draw do
325
+ get "/page_caching_test/ok", to: "page_caching_test#ok"
326
+ end
327
+
328
+ get :ok, format: "json"
329
+ assert_page_not_cached :ok
330
+ end
331
+
332
+ def test_page_caching_directory_set_as_pathname
333
+ begin
334
+ ActionController::Base.page_cache_directory = Pathname.new(FILE_STORE_PATH)
335
+
336
+ draw do
337
+ get "/page_caching_test/ok", to: "page_caching_test#ok"
338
+ end
339
+
340
+ get :ok
341
+ assert_response :ok
342
+ assert_page_cached :ok
343
+ ensure
344
+ ActionController::Base.page_cache_directory = FILE_STORE_PATH
345
+ end
346
+ end
347
+
348
+ def test_page_caching_directory_set_on_controller_instance
349
+ draw do
350
+ get "/page_caching_test/ok", to: "page_caching_test#ok"
351
+ end
352
+
353
+ file_store_path = File.join(TEST_TMP_DIR, "instance_cache")
354
+ @controller.page_cache_directory = file_store_path
355
+
356
+ get :ok
357
+ assert_response :ok
358
+ assert_page_cached :ok, path: file_store_path
359
+ end
360
+ end
361
+
362
+ class ProcPageCachingTestController < CachingController
363
+ self.page_cache_directory = -> { File.join(TEST_TMP_DIR, request.domain) }
364
+
365
+ caches_page :ok
366
+
367
+ def ok
368
+ render html: "ok"
369
+ end
370
+
371
+ def expire_ok
372
+ expire_page action: :ok
373
+ head :ok
374
+ end
375
+ end
376
+
377
+ class ProcPageCachingTest < ActionController::TestCase
378
+ include PageCachingTestHelpers
379
+ tests ProcPageCachingTestController
380
+
381
+ def test_page_is_cached_by_domain
382
+ draw do
383
+ get "/proc_page_caching_test/ok", to: "proc_page_caching_test#ok"
384
+ get "/proc_page_caching_test/ok/expire", to: "proc_page_caching_test#expire_ok"
385
+ end
386
+
387
+ @request.env["HTTP_HOST"] = "www.foo.com"
388
+ get :ok
389
+ assert_response :ok
390
+ assert_page_cached :ok, path: TEST_TMP_DIR + "/foo.com"
391
+
392
+ get :expire_ok
393
+ assert_response :ok
394
+ assert_page_not_cached :ok, path: TEST_TMP_DIR + "/foo.com"
395
+
396
+ @request.env["HTTP_HOST"] = "www.bar.com"
397
+ get :ok
398
+ assert_response :ok
399
+ assert_page_cached :ok, path: TEST_TMP_DIR + "/bar.com"
400
+
401
+ get :expire_ok
402
+ assert_response :ok
403
+ assert_page_not_cached :ok, path: TEST_TMP_DIR + "/bar.com"
404
+ end
405
+
406
+ def test_class_level_cache_page_raise_error
407
+ assert_raises(RuntimeError, /class-level cache_page method/) do
408
+ @controller.class.cache_page "cached content", "/proc_page_caching_test/ok"
409
+ end
410
+ end
411
+ end
412
+
413
+ class SymbolPageCachingTestController < CachingController
414
+ self.page_cache_directory = :domain_cache_directory
415
+
416
+ caches_page :ok
417
+
418
+ def ok
419
+ render html: "ok"
420
+ end
421
+
422
+ def expire_ok
423
+ expire_page action: :ok
424
+ head :ok
425
+ end
426
+
427
+ protected
428
+ def domain_cache_directory
429
+ File.join(TEST_TMP_DIR, request.domain)
430
+ end
431
+ end
432
+
433
+ class SymbolPageCachingTest < ActionController::TestCase
434
+ include PageCachingTestHelpers
435
+ tests SymbolPageCachingTestController
436
+
437
+ def test_page_is_cached_by_domain
438
+ draw do
439
+ get "/symbol_page_caching_test/ok", to: "symbol_page_caching_test#ok"
440
+ get "/symbol_page_caching_test/ok/expire", to: "symbol_page_caching_test#expire_ok"
441
+ end
442
+
443
+ @request.env["HTTP_HOST"] = "www.foo.com"
444
+ get :ok
445
+ assert_response :ok
446
+ assert_page_cached :ok, path: TEST_TMP_DIR + "/foo.com"
447
+
448
+ get :expire_ok
449
+ assert_response :ok
450
+ assert_page_not_cached :ok, path: TEST_TMP_DIR + "/foo.com"
451
+
452
+ @request.env["HTTP_HOST"] = "www.bar.com"
453
+ get :ok
454
+ assert_response :ok
455
+ assert_page_cached :ok, path: TEST_TMP_DIR + "/bar.com"
456
+
457
+ get :expire_ok
458
+ assert_response :ok
459
+ assert_page_not_cached :ok, path: TEST_TMP_DIR + "/bar.com"
460
+ end
461
+
462
+ def test_class_level_cache_page_raise_error
463
+ assert_raises(RuntimeError, /class-level cache_page method/) do
464
+ @controller.class.cache_page "cached content", "/symbol_page_caching_test/ok"
465
+ end
466
+ end
467
+ end
468
+
469
+ class CallablePageCachingTestController < CachingController
470
+ class DomainCacheDirectory
471
+ def self.call(request)
472
+ File.join(TEST_TMP_DIR, request.domain)
473
+ end
474
+ end
475
+
476
+ self.page_cache_directory = DomainCacheDirectory
477
+
478
+ caches_page :ok
479
+
480
+ def ok
481
+ render html: "ok"
482
+ end
483
+
484
+ def expire_ok
485
+ expire_page action: :ok
486
+ head :ok
487
+ end
488
+ end
489
+
490
+ class CallablePageCachingTest < ActionController::TestCase
491
+ include PageCachingTestHelpers
492
+ tests CallablePageCachingTestController
493
+
494
+ def test_page_is_cached_by_domain
495
+ draw do
496
+ get "/callable_page_caching_test/ok", to: "callable_page_caching_test#ok"
497
+ get "/callable_page_caching_test/ok/expire", to: "callable_page_caching_test#expire_ok"
498
+ end
499
+
500
+ @request.env["HTTP_HOST"] = "www.foo.com"
501
+ get :ok
502
+ assert_response :ok
503
+ assert_page_cached :ok, path: TEST_TMP_DIR + "/foo.com"
504
+
505
+ get :expire_ok
506
+ assert_response :ok
507
+ assert_page_not_cached :ok, path: TEST_TMP_DIR + "/foo.com"
508
+
509
+ @request.env["HTTP_HOST"] = "www.bar.com"
510
+ get :ok
511
+ assert_response :ok
512
+ assert_page_cached :ok, path: TEST_TMP_DIR + "/bar.com"
513
+
514
+ get :expire_ok
515
+ assert_response :ok
516
+ assert_page_not_cached :ok, path: TEST_TMP_DIR + "/bar.com"
517
+ end
518
+
519
+ def test_class_level_cache_page_raise_error
520
+ assert_raises(RuntimeError, /class-level cache_page method/) do
521
+ @controller.class.cache_page "cached content", "/callable_page_caching_test/ok"
522
+ end
523
+ end
524
+ end