Permalink
Browse files

Added controller-level etag additions that will be part of the action…

… etag computation *Jeremy Kemper/DHH*
  • Loading branch information...
dhh committed Aug 29, 2012
1 parent 502d5e2 commit ed5c938fa36995f06d4917d9543ba78ed506bb8d
View
@@ -1,5 +1,17 @@
## Rails 4.0.0 (unreleased) ##
* Added controller-level etag additions that will be part of the action etag computation *Jeremy Kemper/DHH*
class InvoicesController < ApplicationController
etag { current_user.try :id }
def show
# Etag will differ even for the same invoice when it's viewed by a different current_user
@invoice = Invoice.find(params[:id])
fresh_when(@invoice)
end
end
* Add automatic template digests to all CacheHelper#cache calls (originally spiked in the cache_digests plugin) *DHH*
* When building a URL fails, add missing keys provided by Journey. Failed URL
@@ -5,6 +5,30 @@ module ConditionalGet
include RackDelegation
include Head
included { cattr_accessor(:etaggers) { Array.new } }
module ClassMethods
# Allows you to consider additional controller-wide information when generating an etag.
# For example, if you serve pages tailored depending on who's logged in at the moment, you
# may want to add the current user id to be part of the etag to prevent authorized displaying
# of cached pages.
#
# === Example
#
# class InvoicesController < ApplicationController
# etag { current_user.try :id }
#
# def show
# # Etag will differ even for the same invoice when it's viewed by a different current_user
# @invoice = Invoice.find(params[:id])
# fresh_when(@invoice)
# end
# end
def etag(&etagger)
self.etaggers += [etagger]
end
end
# Sets the etag, last_modified, or both on the response and renders a
# <tt>304 Not Modified</tt> response if the request is already fresh.
#
@@ -42,12 +66,12 @@ def fresh_when(record_or_options, additional_options = {})
options.assert_valid_keys(:etag, :last_modified, :public)
else
record = record_or_options
options = { :etag => record, :last_modified => record.try(:updated_at) }.merge(additional_options)
options = { etag: record, last_modified: record.try(:updated_at) }.merge(additional_options)
end
response.etag = options[:etag] if options[:etag]
response.last_modified = options[:last_modified] if options[:last_modified]
response.cache_control[:public] = true if options[:public]
response.etag = combine_etags(options[:etag]) if options[:etag]
response.last_modified = options[:last_modified] if options[:last_modified]
response.cache_control[:public] = true if options[:public]
head :not_modified if request.fresh?(response)
end
@@ -133,5 +157,11 @@ def expires_in(seconds, options = {}) #:doc:
def expires_now #:doc:
response.cache_control.replace(:no_cache => true)
end
private
def combine_etags(etag)
[ etag, *etaggers.map { |etagger| instance_exec &etagger }.compact ]
end
end
end
@@ -22,6 +22,18 @@ def new
end
end
class TestControllerWithExtraEtags < ActionController::Base
etag { nil }
etag { 'ab' }
etag { :cde }
etag { [:f] }
etag { nil }
def fresh
render text: "stale" if stale?(etag: '123')
end
end
class TestController < ActionController::Base
protect_from_forgery
@@ -1626,6 +1638,26 @@ def test_last_modified_works_with_less_than_too
end
end
class EtagRenderTest < ActionController::TestCase
tests TestControllerWithExtraEtags
def setup
super
@request.host = "www.nextangle.com"
end
def test_multiple_etags
@request.if_none_match = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key([ "123", 'ab', :cde, [:f] ]))}")
get :fresh
assert_response :not_modified
@request.if_none_match = %("nomatch")
get :fresh
assert_response :success
end
end
class MetalRenderTest < ActionController::TestCase
tests MetalTestController

0 comments on commit ed5c938

Please sign in to comment.