diff options
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/CHANGELOG | 34 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base.rb | 61 | ||||
-rw-r--r-- | actionpack/lib/action_controller/caching/fragments.rb | 26 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatcher.rb | 3 | ||||
-rwxr-xr-x | actionpack/lib/action_controller/request.rb | 14 | ||||
-rw-r--r-- | actionpack/lib/action_controller/response.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_view/base.rb | 16 | ||||
-rw-r--r-- | actionpack/test/controller/dispatcher_test.rb | 10 | ||||
-rw-r--r-- | actionpack/test/controller/render_test.rb | 23 |
9 files changed, 116 insertions, 75 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index c68bfc753c..fdc9be2ff4 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,39 @@ *Edge* +* Added stale? and fresh_when methods to provide a layer of abstraction above request.fresh? and friends [DHH]. Example: + + class ArticlesController < ApplicationController + def show_with_respond_to_block + @article = Article.find(params[:id]) + + + # If the request sends headers that differs from the options provided to stale?, then + # the request is indeed stale and the respond_to block is triggered (and the options + # to the stale? call is set on the response). + # + # If the request headers match, then the request is fresh and the respond_to block is + # not triggered. Instead the default render will occur, which will check the last-modified + # and etag headers and conclude that it only needs to send a "304 Not Modified" instead + # of rendering the template. + if stale?(:last_modified => @article.published_at.utc, :etag => @article) + respond_to do |wants| + # normal response processing + end + end + end + + def show_with_implied_render + @article = Article.find(params[:id]) + + # Sets the response headers and checks them against the request, if the request is stale + # (i.e. no match of either etag or last-modified), then the default render of the template happens. + # If the request is fresh, then the default render will return a "304 Not Modified" + # instead of rendering the template. + fresh_when(:last_modified => @article.published_at.utc, :etag => @article) + end + end + + * Added inline builder yield to atom_feed_helper tags where appropriate [Sam Ruby]. Example: entry.summary :type => 'xhtml' do |xhtml| diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 3ede681253..2cff05dfa4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -965,22 +965,6 @@ module ActionController #:nodoc: render :nothing => true, :status => status end - # Sets the Last-Modified response header. Returns 304 Not Modified if the - # If-Modified-Since request header is <= last modified. - def last_modified!(utc_time) - response.last_modified= utc_time - if request.if_modified_since && request.if_modified_since <= utc_time - head(:not_modified) - end - end - - # Sets the ETag response header. Returns 304 Not Modified if the - # If-None-Match request header matches. - def etag!(etag) - response.etag = etag - head(:not_modified) if response.etag == request.if_none_match - end - # Clears the rendered results, allowing for another render to be performed. def erase_render_results #:nodoc: response.body = nil @@ -1090,6 +1074,51 @@ module ActionController #:nodoc: @performed_redirect = true end + # Sets the etag and/or last_modified on the response and checks it against + # the client request. If the request doesn't match the options provided, the + # request is considered stale and should be generated from scratch. Otherwise, + # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent. + # + # Example: + # + # def show + # @article = Article.find(params[:id]) + # + # if stale?(:etag => @article, :last_modified => @article.created_at.utc) + # @statistics = @article.really_expensive_call + # respond_to do |format| + # # all the supported formats + # end + # end + # end + def stale?(options) + fresh_when(options) + !request.fresh?(response) + end + + # Sets the etag, last_modified, or both on the response and renders a + # "304 Not Modified" response if the request is already fresh. + # + # Example: + # + # def show + # @article = Article.find(params[:id]) + # fresh_when(:etag => @article, :last_modified => @article.created_at.utc) + # end + # + # This will render the show template if the request isn't sending a matching etag or + # If-Modified-Since header and just a "304 Not Modified" response if there's a match. + def fresh_when(options) + options.assert_valid_keys(:etag, :last_modified) + + response.etag = options[:etag] if options[:etag] + response.last_modified = options[:last_modified] if options[:last_modified] + + if request.fresh?(response) + head :not_modified + end + end + # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that # intermediate caches shouldn't cache the response. # diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index e9b434dd25..31cbe27452 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -26,32 +26,6 @@ module ActionController #:nodoc: # # expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics") module Fragments - def self.included(base) #:nodoc: - base.class_eval do - class << self - def fragment_cache_store=(store_option) #:nodoc: - ActiveSupport::Deprecation.warn('The fragment_cache_store= method is now use cache_store=') - self.cache_store = store_option - end - - def fragment_cache_store #:nodoc: - ActiveSupport::Deprecation.warn('The fragment_cache_store method is now use cache_store') - cache_store - end - end - - def fragment_cache_store=(store_option) #:nodoc: - ActiveSupport::Deprecation.warn('The fragment_cache_store= method is now use cache_store=') - self.cache_store = store_option - end - - def fragment_cache_store #:nodoc: - ActiveSupport::Deprecation.warn('The fragment_cache_store method is now use cache_store') - cache_store - end - end - end - # Given a key (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading, # writing, or expiring a cached fragment. If the key is a hash, the generated key is the return # value of url_for on that hash (without the protocol). All keys are prefixed with "views/" and uses diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 90c8400c11..28f8ce3d53 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -39,7 +39,7 @@ module ActionController # Add a preparation callback. Preparation callbacks are run before every # request in development mode, and before the first request in production # mode. - # + # # An optional identifier may be supplied for the callback. If provided, # to_prepare may be called again with the same identifier to replace the # existing callback. Passing an identifier is a suggested practice if the @@ -144,6 +144,7 @@ module ActionController Routing::Routes.reload ActionController::Base.view_paths.reload! + ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear end # Cleanup the application by clearing out loaded classes so they can diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 5e492e3ee1..9f33cbc55f 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -120,9 +120,19 @@ module ActionController end # Check response freshness (Last-Modified and ETag) against request - # If-Modified-Since and If-None-Match conditions. + # If-Modified-Since and If-None-Match conditions. If both headers are + # supplied, both must match, or the request is not considered fresh. def fresh?(response) - not_modified?(response.last_modified) || etag_matches?(response.etag) + case + when if_modified_since && if_none_match + not_modified?(response.last_modified) && etag_matches?(response.etag) + when if_modified_since + not_modified?(response.last_modified) + when if_none_match + etag_matches?(response.etag) + else + false + end end # Returns the Mime type for the \format used in the request. diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index 54a99996ef..b440065482 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -114,8 +114,8 @@ module ActionController # :nodoc: def redirect(url, status) self.status = status - self.location = url - self.body = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>" + self.location = url.gsub(/[\r\n]/, '') + self.body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>" end def sending_file? diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 8df10c40cc..e22978fe27 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -172,18 +172,6 @@ module ActionView #:nodoc: delegate :logger, :to => 'ActionController::Base' end - def self.cache_template_loading=(*args) - ActiveSupport::Deprecation.warn( - "config.action_view.cache_template_loading option has been deprecated" + - "and has no effect. Please remove it from your config files.", caller) - end - - def self.cache_template_extensions=(*args) - ActiveSupport::Deprecation.warn( - "config.action_view.cache_template_extensions option has been" + - "deprecated and has no effect. Please remove it from your config files.", caller) - end - # Templates that are exempt from layouts @@exempt_from_layout = Set.new([/\.rjs$/]) @@ -259,10 +247,6 @@ module ActionView #:nodoc: if options[:layout] _render_with_layout(options, local_assigns, &block) elsif options[:file] - if options[:use_full_path] - ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller) - end - _pick_template(options[:file]).render_template(self, options[:locals]) elsif options[:partial] render_partial(options) diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 911fcab67b..3ee78a6156 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -26,9 +26,17 @@ class DispatcherTest < Test::Unit::TestCase end def test_clears_dependencies_after_dispatch_if_in_loading_mode - ActionController::Routing::Routes.expects(:reload).once ActiveSupport::Dependencies.expects(:clear).once + dispatch(@output, false) + end + + def test_reloads_routes_before_dispatch_if_in_loading_mode + ActionController::Routing::Routes.expects(:reload).once + dispatch(@output, false) + end + def test_clears_asset_tag_cache_before_dispatch_if_in_loading_mode + ActionView::Helpers::AssetTagHelper::AssetTag::Cache.expects(:clear).once dispatch(@output, false) end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 7b8bb6856b..35d9b19cc0 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -30,24 +30,18 @@ class TestController < ActionController::Base end def conditional_hello - response.last_modified = Time.now.utc.beginning_of_day - response.etag = [:foo, 123] - - if request.fresh?(response) - head :not_modified - else + if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123]) render :action => 'hello_world' end end - + def conditional_hello_with_bangs render :action => 'hello_world' end before_filter :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs def handle_last_modified_and_etags - last_modified! Time.now.utc.beginning_of_day - etag! [:foo, 123] + fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ]) end def render_hello_world @@ -248,7 +242,7 @@ class TestController < ActionController::Base if @alternate_default_render @alternate_default_render.call else - render + super end end @@ -1422,6 +1416,13 @@ class LastModifiedRenderTest < Test::Unit::TestCase assert_equal @last_modified, @response.headers['Last-Modified'] end + def test_request_not_modified_but_etag_differs + @request.if_modified_since = @last_modified + @request.if_none_match = "234" + get :conditional_hello + assert_response :success + end + def test_request_modified @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT' get :conditional_hello @@ -1445,7 +1446,7 @@ class LastModifiedRenderTest < Test::Unit::TestCase def test_last_modified_works_with_less_than_too @request.if_modified_since = 5.years.ago.httpdate get :conditional_hello_with_bangs - assert_response :not_modified + assert_response :success end end |