1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Added controller-level etag additions that will be part of the action etag computation *Jeremy Kemper/DHH*

This commit is contained in:
David Heinemeier Hansson 2012-08-29 15:06:30 -05:00
parent 502d5e24e2
commit ed5c938fa3
3 changed files with 78 additions and 4 deletions

View file

@ -1,5 +1,17 @@
## Rails 4.0.0 (unreleased) ## ## 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* * 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 * When building a URL fails, add missing keys provided by Journey. Failed URL

View file

@ -5,6 +5,30 @@ module ActionController
include RackDelegation include RackDelegation
include Head 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 # 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. # <tt>304 Not Modified</tt> response if the request is already fresh.
# #
@ -42,12 +66,12 @@ module ActionController
options.assert_valid_keys(:etag, :last_modified, :public) options.assert_valid_keys(:etag, :last_modified, :public)
else else
record = record_or_options 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 end
response.etag = options[:etag] if options[:etag] response.etag = combine_etags(options[:etag]) if options[:etag]
response.last_modified = options[:last_modified] if options[:last_modified] response.last_modified = options[:last_modified] if options[:last_modified]
response.cache_control[:public] = true if options[:public] response.cache_control[:public] = true if options[:public]
head :not_modified if request.fresh?(response) head :not_modified if request.fresh?(response)
end end
@ -133,5 +157,11 @@ module ActionController
def expires_now #:doc: def expires_now #:doc:
response.cache_control.replace(:no_cache => true) response.cache_control.replace(:no_cache => true)
end end
private
def combine_etags(etag)
[ etag, *etaggers.map { |etagger| instance_exec &etagger }.compact ]
end
end end
end end

View file

@ -22,6 +22,18 @@ module Quiz
end end
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 class TestController < ActionController::Base
protect_from_forgery protect_from_forgery
@ -1626,6 +1638,26 @@ class LastModifiedRenderTest < ActionController::TestCase
end end
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 class MetalRenderTest < ActionController::TestCase
tests MetalTestController tests MetalTestController