From 2ed39424dbecf1d040b15d1450f0265b35b78022 Mon Sep 17 00:00:00 2001
From: Arthur Neves <arthurnn@gmail.com>
Date: Wed, 7 Jan 2015 21:23:55 -0500
Subject: Implement http_cache_forever to ActionController

Add http_cache_forever to ActionController, so we can cache results
forever.
Things like static pages are a good candidate for this type of caching.

This cache only controls caching headers, so it is up to the browser to
cache those requests.
---
 actionpack/CHANGELOG.md                            |  4 ++
 .../lib/action_controller/metal/conditional_get.rb | 18 ++++++++
 actionpack/test/controller/render_test.rb          | 53 ++++++++++++++++++++++
 3 files changed, 75 insertions(+)

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.
+    #
+    # <tt>public</tt> 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.
+    #
+    # <tt>version</tt> 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
-- 
cgit v1.2.3