diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 3b115a7bf7..268f037bfe 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add http_cache_forever to Action Controller, so we can cache a response that never gets expired. + + *arthurnn* + * ActionController#translate supports symbols as shortcuts. When shortcut is given it also lookups without action name. diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 28f0b6e349..bc6d336e19 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -215,6 +215,24 @@ module ActionController response.cache_control.replace(:no_cache => true) end + # Cache or yield the block. The cache is suppose to never expire. + # + # You can use this method when you have a HTTP response that never changes, + # and the browser and proxies should cache it indefinitely. + # + # public By default, HTTP responses are private, cached only on the + # user's web browser. To allow proxies to cache the response, set +true+ to + # indicate that they can serve the cached response to all users. + # + # version is the version passed as a key for the cache. + def http_cache_forever(public: false, version: 'v1') + expires_in 100.years, public: public + + yield if stale?(etag: "#{version}-#{request.fullpath}", + last_modified: Time.parse('2011-01-01').utc, + public: public) + end + private def combine_etags(options) etags = etaggers.map { |etagger| instance_exec(options, &etagger) }.compact diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 8d6b62f2bf..488585c7a4 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -561,3 +561,56 @@ class HeadRenderTest < ActionController::TestCase assert_response :forbidden end end + +class HttpCacheForeverTest < ActionController::TestCase + class HttpCacheForeverController < ActionController::Base + def cache_me_forever + http_cache_forever(public: params[:public], version: params[:version] || 'v1') do + render text: 'hello' + end + end + end + + tests HttpCacheForeverController + + def test_cache_with_public + get :cache_me_forever, params: {public: true} + assert_equal "max-age=#{100.years.to_i}, public", @response.headers["Cache-Control"] + assert_not_nil @response.etag + end + + def test_cache_with_private + get :cache_me_forever + assert_equal "max-age=#{100.years.to_i}, private", @response.headers["Cache-Control"] + assert_not_nil @response.etag + assert_response :success + end + + def test_cache_response_code_with_if_modified_since + get :cache_me_forever + assert_response :success + @request.if_modified_since = @response.headers['Last-Modified'] + get :cache_me_forever + assert_response :not_modified + end + + def test_cache_response_code_with_etag + get :cache_me_forever + assert_response :success + @request.if_modified_since = @response.headers['Last-Modified'] + @request.if_none_match = @response.etag + + get :cache_me_forever + assert_response :not_modified + @request.if_modified_since = @response.headers['Last-Modified'] + @request.if_none_match = @response.etag + + get :cache_me_forever, params: {version: 'v2'} + assert_response :success + @request.if_modified_since = @response.headers['Last-Modified'] + @request.if_none_match = @response.etag + + get :cache_me_forever, params: {version: 'v2'} + assert_response :not_modified + end +end