Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #6642: fragment_caching_for_non_erb_templates3.diff

File fragment_caching_for_non_erb_templates3.diff, 14.2 kB (added by zsombor, 7 months ago)

Patch against /trunk @8440

  • actionpack/test/controller/caching_test.rb

    old new  
    2525  def not_found 
    2626    head :not_found 
    2727  end 
    28    
     28 
    2929  def custom_path 
    3030    render :text => "Super soaker" 
    3131    cache_page("Super soaker", "/index.html") 
    3232  end 
    33    
     33 
    3434  def expire_custom_path 
    3535    expire_page("/index.html") 
    3636    head :ok 
    3737  end 
    38    
     38 
    3939  def trailing_slash 
    4040    render :text => "Sneak attack" 
    4141  end 
     
    9595    get :expire_custom_path 
    9696    assert !File.exist?("#{FILE_STORE_PATH}/index.html") 
    9797  end 
    98    
     98 
    9999  def test_should_cache_without_trailing_slash_on_url 
    100100    @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash' 
    101101    assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html") 
     
    215215    get :index 
    216216    assert_equal cached_time, @response.body 
    217217  end 
    218    
     218 
    219219  def test_action_cache_with_custom_cache_path 
    220220    get :show 
    221221    cached_time = content_to_cache 
     
    262262    @request.host = 'jamis.hostname.com' 
    263263    get :index 
    264264    jamis_cache = content_to_cache 
    265      
     265 
    266266    reset! 
    267      
     267 
    268268    @request.host = 'david.hostname.com' 
    269269    get :index 
    270270    david_cache = content_to_cache 
     
    308308    assert_equal 'xml', path_object.extension 
    309309    assert_equal 'example.org/posts/index.xml', path_object.path 
    310310  end 
    311    
     311 
    312312  def test_correct_content_type_is_returned_for_cache_hit 
    313313    # run it twice to cache it the first time 
    314314    get :index, :id => 'content-type.xml' 
     
    341341      @controller = ActionCachingTestController.new 
    342342      @request.host = 'hostname.com' 
    343343    end 
    344      
     344 
    345345    def assert_cache_exists(path) 
    346346      full_path = File.join(FILE_STORE_PATH, path + '.cache') 
    347347      assert File.exist?(full_path), "#{full_path.inspect} does not exist." 
    348348    end 
    349349end 
     350 
     351class FragmentCachingTestController < ActionController::Base 
     352  def some_action; end; 
     353end 
     354 
     355class FragmentCachingTest < Test::Unit::TestCase 
     356  def setup 
     357    ActionController::Base.perform_caching = true 
     358    @store = ActionController::Caching::Fragments::UnthreadedMemoryStore.new 
     359    ActionController::Base.fragment_cache_store = @store 
     360    @controller = FragmentCachingTestController.new 
     361    @params = {:controller => 'posts', :action => 'index'} 
     362    @request = ActionController::TestRequest.new 
     363    @response = ActionController::TestResponse.new 
     364    @controller.params = @params 
     365    @controller.request = @request 
     366    @controller.response = @response 
     367    @controller.send(:initialize_current_url) 
     368  end 
     369 
     370  def test_fragement_cache_key 
     371    assert_equal 'what a key', @controller.fragment_cache_key('what a key') 
     372    assert_equal( "test.host/fragment_caching_test/some_action", 
     373                  @controller.fragment_cache_key(:controller => 'fragment_caching_test',:action => 'some_action')) 
     374  end 
     375 
     376  def test_read_fragment__with_caching_enabled 
     377    @store.write('name', 'value') 
     378    assert_equal 'value', @controller.read_fragment('name') 
     379  end 
     380 
     381  def test_read_fragment__with_caching_disabled 
     382    ActionController::Base.perform_caching = false 
     383    @store.write('name', 'value') 
     384    assert_nil @controller.read_fragment('name') 
     385  end 
     386 
     387  def test_write_fragment__with_caching_enabled 
     388    assert_nil @store.read('name') 
     389    assert_equal 'value', @controller.write_fragment('name', 'value') 
     390    assert_equal 'value', @store.read('name') 
     391  end 
     392 
     393  def test_write_fragment__with_caching_disabled 
     394    assert_nil @store.read('name') 
     395    ActionController::Base.perform_caching = false 
     396    assert_equal nil, @controller.write_fragment('name', 'value') 
     397    assert_nil @store.read('name') 
     398  end 
     399 
     400  def test_expire_fragment__with_simple_key 
     401    @store.write('name', 'value') 
     402    @controller.expire_fragment 'name' 
     403    assert_nil @store.read('name') 
     404  end 
     405 
     406  def test_expire_fragment__with__regexp 
     407    @store.write('name', 'value') 
     408    @store.write('another_name', 'another_value') 
     409    @store.write('primalgrasp', 'will not expire ;-)') 
     410 
     411    @controller.expire_fragment /name/ 
     412 
     413    assert_nil @store.read('name') 
     414    assert_nil @store.read('another_name') 
     415    assert_equal 'will not expire ;-)', @store.read('primalgrasp') 
     416  end 
     417 
     418  def test_fragment_for__with_disabled_caching 
     419    ActionController::Base.perform_caching = false 
     420 
     421    @store.write('expensive', 'fragment content') 
     422    fragment_computed = false 
     423 
     424    buffer = 'generated till now -> ' 
     425    @controller.fragment_for(Proc.new { fragment_computed = true }, 'expensive') { buffer } 
     426 
     427    assert fragment_computed 
     428    assert_equal 'generated till now -> ', buffer 
     429  end 
     430 
     431 
     432  def test_fragment_for 
     433    @store.write('expensive', 'fragment content') 
     434    fragment_computed = false 
     435 
     436    buffer = 'generated till now -> ' 
     437    @controller.fragment_for(Proc.new { fragment_computed = true }, 'expensive') { buffer} 
     438 
     439    assert !fragment_computed 
     440    assert_equal 'generated till now -> fragment content', buffer 
     441  end 
     442 
     443  def test_cache_erb_fragment 
     444    @store.write('expensive', 'fragment content') 
     445    _erbout = 'generated till now -> ' 
     446 
     447    assert_equal( 'generated till now -> fragment content', 
     448                  @controller.cache_erb_fragment(Proc.new{ }, 'expensive')) 
     449  end 
     450 
     451  def test_cache_rxml_fragment 
     452    @store.write('expensive', 'fragment content') 
     453    xml = 'generated till now -> ' 
     454    class << xml; def target!; to_s; end; end 
     455 
     456    assert_equal( 'generated till now -> fragment content', 
     457                  @controller.cache_rxml_fragment(Proc.new{ }, 'expensive')) 
     458  end 
     459 
     460  def test_cache_rjs_fragment 
     461    @store.write('expensive', 'fragment content') 
     462    page = 'generated till now -> ' 
     463 
     464    assert_equal( 'generated till now -> fragment content', 
     465                  @controller.cache_rjs_fragment(Proc.new{ }, 'expensive')) 
     466  end 
     467 
     468  def test_cache_rjs_fragment_debug_mode_does_not_interfere 
     469    @store.write('expensive', 'fragment content') 
     470    page = 'generated till now -> ' 
     471 
     472    begin 
     473      debug_mode, ActionView::Base.debug_rjs = ActionView::Base.debug_rjs, true 
     474      assert_equal( 'generated till now -> fragment content', 
     475                    @controller.cache_rjs_fragment(Proc.new{ }, 'expensive')) 
     476      assert ActionView::Base.debug_rjs 
     477    ensure 
     478      ActionView::Base.debug_rjs = debug_mode 
     479    end 
     480  end 
     481end 
  • actionpack/lib/action_controller/caching.rb

    old new  
    187187    #     before_filter :authenticate, :except => :public 
    188188    #     caches_page   :public 
    189189    #     caches_action :show, :cache_path => { :project => 1 } 
    190     #     caches_action :show, :cache_path => Proc.new { |controller|  
    191     #       controller.params[:user_id] ?  
     190    #     caches_action :show, :cache_path => Proc.new { |controller| 
     191    #       controller.params[:user_id] ? 
    192192    #         controller.send(:user_list_url, c.params[:user_id], c.params[:id]) : 
    193193    #         controller.send(:list_url, c.params[:id]) } 
    194194    #   end 
     
    262262            controller.request.get? && controller.response.headers['Status'].to_i == 200 
    263263          end 
    264264      end 
    265        
     265 
    266266      class ActionCachePath 
    267267        attr_reader :path, :extension 
    268          
     268 
    269269        class << self 
    270270          def path_for(controller, options) 
    271271            new(controller, options).path 
    272272          end 
    273273        end 
    274          
     274 
    275275        def initialize(controller, options = {}) 
    276276          @extension = extract_extension(controller.request.path) 
    277277          path = controller.url_for(options).split('://').last 
     
    279279          add_extension!(path, @extension) 
    280280          @path = URI.unescape(path) 
    281281        end 
    282          
     282 
    283283        private 
    284284          def normalize!(path) 
    285285            path << 'index' if path[-1] == ?/ 
    286286          end 
    287          
     287 
    288288          def add_extension!(path, extension) 
    289289            path << ".#{extension}" if extension 
    290290          end 
    291            
     291 
    292292          def extract_extension(file_path) 
    293293            # Don't want just what comes after the last '.' to accommodate multi part extensions 
    294294            # such as tar.gz. 
     
    307307    #     <%= render :partial => "topic", :collection => Topic.find(:all) %> 
    308308    #   <% end %> 
    309309    # 
    310     # This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would  
    311     # be able to invalidate it using <tt>expire_fragment(:controller => "topics", :action => "list")</tt>.  
    312     #  
    313     # This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using  
     310    # This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would 
     311    # be able to invalidate it using <tt>expire_fragment(:controller => "topics", :action => "list")</tt>. 
     312    # 
     313    # This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using 
    314314    # <tt>caches_action</tt>, so we also have the option to qualify the name of the cached fragment with something like: 
    315315    # 
    316316    #   <% cache(:action => "list", :action_suffix => "all_topics") do %> 
    317317    # 
    318     # That would result in a name such as "/topics/list/all_topics", avoiding conflicts with the action cache and with any fragments that use a  
    319     # different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique  
    320     # cache names that we can refer to when we need to expire the cache.  
    321     #  
     318    # That would result in a name such as "/topics/list/all_topics", avoiding conflicts with the action cache and with any fragments that use a 
     319    # different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique 
     320    # cache names that we can refer to when we need to expire the cache. 
     321    # 
    322322    # The expiration call for this example is: 
    323     #  
     323    # 
    324324    #   expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics") 
    325325    # 
    326326    # == Fragment stores 
    327327    # 
    328328    # By default, cached fragments are stored in memory. The available store options are: 
    329329    # 
    330     # * FileStore: Keeps the fragments on disk in the +cache_path+, which works well for all types of environments and allows all  
     330    # * FileStore: Keeps the fragments on disk in the +cache_path+, which works well for all types of environments and allows all 
    331331    #   processes running from the same application directory to access the cached content. 
    332332    # * MemoryStore: Keeps the fragments in memory, which is fine for WEBrick and for FCGI (if you don't care that each FCGI process holds its 
    333333    #   own fragment store). It's not suitable for CGI as the process is thrown away at the end of each request. It can potentially also take 
     
    364364        end 
    365365      end 
    366366 
    367       # Given a name (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading,  
     367      # Given a name (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading, 
    368368      # writing, or expiring a cached fragment. If the name is a hash, the generated name is the return 
    369369      # value of url_for on that hash (without the protocol). 
    370370      def fragment_cache_key(name) 
    371371        name.is_a?(Hash) ? url_for(name).split("://").last : name 
    372372      end 
    373373 
    374       # Called by CacheHelper#cache 
    375       def cache_erb_fragment(block, name = {}, options = nil) 
     374 
     375      def fragment_for(block, name = {}, options = nil) 
    376376        unless perform_caching then block.call; return end 
    377  
    378         buffer = eval(ActionView::Base.erb_variable, block.binding) 
    379  
     377        buffer = yield 
    380378        if cache = read_fragment(name, options) 
    381379          buffer.concat(cache) 
    382380        else 
     
    386384        end 
    387385      end 
    388386 
     387      def cache_rxml_fragment(block, name = {}, options = nil) 
     388        fragment_for(block, name, options) do 
     389          eval('xml.target!', block.binding) 
     390        end 
     391      end 
     392 
     393      # Called by CacheHelper#cache 
     394      def cache_rjs_fragment(block, name = {}, options = nil) 
     395        fragment_for(block, name, options) do 
     396          begin 
     397            debug_mode, ActionView::Base.debug_rjs = ActionView::Base.debug_rjs, false 
     398            eval('page.to_s', block.binding) 
     399          ensure 
     400            ActionView::Base.debug_rjs = debug_mode 
     401          end 
     402        end 
     403      end 
     404 
     405      # Called by CacheHelper#cache 
     406      def cache_erb_fragment(block, name = {}, options = nil) 
     407        fragment_for(block, name, options) do 
     408          eval(ActionView::Base.erb_variable, block.binding) 
     409        end 
     410      end 
     411 
     412 
    389413      # Writes <tt>content</tt> to the location signified by <tt>name</tt> (see <tt>expire_fragment</tt> for acceptable formats) 
    390414      def write_fragment(name, content, options = nil) 
    391415        return unless perform_caching 
  • actionpack/lib/action_view/helpers/cache_helper.rb

    old new  
    3232      #      <i>Topics listed alphabetically</i> 
    3333      #    <% end %> 
    3434      def cache(name = {}, &block) 
    35         @controller.cache_erb_fragment(block, name) 
     35        template_extension = first_render[/\.(\w+)$/, 1].to_sym 
     36        case template_extension 
     37        when :erb, :rhtml 
     38          @controller.cache_erb_fragment(block, name) 
     39        when :rjs 
     40          @controller.cache_rjs_fragment(block, name) 
     41        when :builder, :rxml 
     42          @controller.cache_rxml_fragment(block, name) 
     43        else 
     44          # do a last ditch effort for those brave souls using 
     45          # different template engines. This should give plugin 
     46          # writters a simple hook. 
     47          raise "fragment caching not supported for #{template_extension} files." unless @controller.respond_to?("cache_#{template_extension}_fragment") 
     48          @controller.send "cache_#{template_extension}_fragment", block, name 
     49        end 
    3650      end 
    3751    end 
    3852  end