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) |
|---|
-
actionpack/test/controller/caching_test.rb
old new 25 25 def not_found 26 26 head :not_found 27 27 end 28 28 29 29 def custom_path 30 30 render :text => "Super soaker" 31 31 cache_page("Super soaker", "/index.html") 32 32 end 33 33 34 34 def expire_custom_path 35 35 expire_page("/index.html") 36 36 head :ok 37 37 end 38 38 39 39 def trailing_slash 40 40 render :text => "Sneak attack" 41 41 end … … 95 95 get :expire_custom_path 96 96 assert !File.exist?("#{FILE_STORE_PATH}/index.html") 97 97 end 98 98 99 99 def test_should_cache_without_trailing_slash_on_url 100 100 @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash' 101 101 assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html") … … 215 215 get :index 216 216 assert_equal cached_time, @response.body 217 217 end 218 218 219 219 def test_action_cache_with_custom_cache_path 220 220 get :show 221 221 cached_time = content_to_cache … … 262 262 @request.host = 'jamis.hostname.com' 263 263 get :index 264 264 jamis_cache = content_to_cache 265 265 266 266 reset! 267 267 268 268 @request.host = 'david.hostname.com' 269 269 get :index 270 270 david_cache = content_to_cache … … 308 308 assert_equal 'xml', path_object.extension 309 309 assert_equal 'example.org/posts/index.xml', path_object.path 310 310 end 311 311 312 312 def test_correct_content_type_is_returned_for_cache_hit 313 313 # run it twice to cache it the first time 314 314 get :index, :id => 'content-type.xml' … … 341 341 @controller = ActionCachingTestController.new 342 342 @request.host = 'hostname.com' 343 343 end 344 344 345 345 def assert_cache_exists(path) 346 346 full_path = File.join(FILE_STORE_PATH, path + '.cache') 347 347 assert File.exist?(full_path), "#{full_path.inspect} does not exist." 348 348 end 349 349 end 350 351 class FragmentCachingTestController < ActionController::Base 352 def some_action; end; 353 end 354 355 class 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 481 end -
actionpack/lib/action_controller/caching.rb
old new 187 187 # before_filter :authenticate, :except => :public 188 188 # caches_page :public 189 189 # 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] ? 192 192 # controller.send(:user_list_url, c.params[:user_id], c.params[:id]) : 193 193 # controller.send(:list_url, c.params[:id]) } 194 194 # end … … 262 262 controller.request.get? && controller.response.headers['Status'].to_i == 200 263 263 end 264 264 end 265 265 266 266 class ActionCachePath 267 267 attr_reader :path, :extension 268 268 269 269 class << self 270 270 def path_for(controller, options) 271 271 new(controller, options).path 272 272 end 273 273 end 274 274 275 275 def initialize(controller, options = {}) 276 276 @extension = extract_extension(controller.request.path) 277 277 path = controller.url_for(options).split('://').last … … 279 279 add_extension!(path, @extension) 280 280 @path = URI.unescape(path) 281 281 end 282 282 283 283 private 284 284 def normalize!(path) 285 285 path << 'index' if path[-1] == ?/ 286 286 end 287 287 288 288 def add_extension!(path, extension) 289 289 path << ".#{extension}" if extension 290 290 end 291 291 292 292 def extract_extension(file_path) 293 293 # Don't want just what comes after the last '.' to accommodate multi part extensions 294 294 # such as tar.gz. … … 307 307 # <%= render :partial => "topic", :collection => Topic.find(:all) %> 308 308 # <% end %> 309 309 # 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 314 314 # <tt>caches_action</tt>, so we also have the option to qualify the name of the cached fragment with something like: 315 315 # 316 316 # <% cache(:action => "list", :action_suffix => "all_topics") do %> 317 317 # 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 # 322 322 # The expiration call for this example is: 323 # 323 # 324 324 # expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics") 325 325 # 326 326 # == Fragment stores 327 327 # 328 328 # By default, cached fragments are stored in memory. The available store options are: 329 329 # 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 331 331 # processes running from the same application directory to access the cached content. 332 332 # * 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 333 333 # 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 … … 364 364 end 365 365 end 366 366 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, 368 368 # writing, or expiring a cached fragment. If the name is a hash, the generated name is the return 369 369 # value of url_for on that hash (without the protocol). 370 370 def fragment_cache_key(name) 371 371 name.is_a?(Hash) ? url_for(name).split("://").last : name 372 372 end 373 373 374 # Called by CacheHelper#cache 375 def cache_erb_fragment(block, name = {}, options = nil)374 375 def fragment_for(block, name = {}, options = nil) 376 376 unless perform_caching then block.call; return end 377 378 buffer = eval(ActionView::Base.erb_variable, block.binding) 379 377 buffer = yield 380 378 if cache = read_fragment(name, options) 381 379 buffer.concat(cache) 382 380 else … … 386 384 end 387 385 end 388 386 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 389 413 # Writes <tt>content</tt> to the location signified by <tt>name</tt> (see <tt>expire_fragment</tt> for acceptable formats) 390 414 def write_fragment(name, content, options = nil) 391 415 return unless perform_caching -
actionpack/lib/action_view/helpers/cache_helper.rb
old new 32 32 # <i>Topics listed alphabetically</i> 33 33 # <% end %> 34 34 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 36 50 end 37 51 end 38 52 end