Ticket #10800: refactor_action_view_io_caching.2.patch
| File refactor_action_view_io_caching.2.patch, 27.5 kB (added by lifofifo, 2 years ago) |
|---|
-
a/actionpack/lib/action_controller/base.rb
old new 5 5 require 'action_controller/resources' 6 6 require 'action_controller/url_rewriter' 7 7 require 'action_controller/status_codes' 8 require 'action_view/template_finder' 8 9 require 'drb' 9 10 require 'set' 10 11 … … 428 429 429 430 def view_paths=(value) 430 431 @view_paths = value 432 ActionView::TemplateFinder.process_view_paths(value) 431 433 end 432 434 433 435 # Adds a view_path to the front of the view_paths array. … … 440 442 def prepend_view_path(path) 441 443 @view_paths = superclass.view_paths.dup if @view_paths.nil? 442 444 view_paths.unshift(*path) 445 ActionView::TemplateFinder.process_view_paths(path) 443 446 end 444 447 445 448 # Adds a view_path to the end of the view_paths array. … … 452 455 def append_view_path(path) 453 456 @view_paths = superclass.view_paths.dup if @view_paths.nil? 454 457 view_paths.push(*path) 458 ActionView::TemplateFinder.process_view_paths(path) 455 459 end 456 460 457 461 # Replace sensitive parameter data from the request log. … … 642 646 643 647 # View load paths for controller. 644 648 def view_paths 645 @template. view_paths649 @template.finder.view_paths 646 650 end 647 651 648 652 def view_paths=(value) 649 @template. view_paths = value653 @template.finder.view_paths = value # Mutex needed 650 654 end 651 655 652 656 # Adds a view_path to the front of the view_paths array. … … 656 660 # self.prepend_view_path(["views/default", "views/custom"]) 657 661 # 658 662 def prepend_view_path(path) 659 @template. prepend_view_path(path)663 @template.finder.prepend_view_path(path) # Mutex needed 660 664 end 661 665 662 666 # Adds a view_path to the end of the view_paths array. … … 666 670 # self.append_view_path(["views/default", "views/custom"]) 667 671 # 668 672 def append_view_path(path) 669 @template.append_view_path(path)673 @template.finder.append_view_path(path) # Mutex needed 670 674 end 671 675 672 676 protected … … 1249 1253 end 1250 1254 1251 1255 def template_exists?(template_name = default_template_name) 1252 @template.fi le_exists?(template_name)1256 @template.finder.file_exists?(template_name) 1253 1257 end 1254 1258 1255 1259 def template_public?(template_name = default_template_name) … … 1257 1261 end 1258 1262 1259 1263 def template_exempt_from_layout?(template_name = default_template_name) 1260 extension = @template && @template. pick_template_extension(template_name)1264 extension = @template && @template.finder.pick_template_extension(template_name) 1261 1265 name_with_extension = !template_name.include?('.') && extension ? "#{template_name}.#{extension}" : template_name 1262 1266 @@exempt_from_layout.any? { |ext| name_with_extension =~ ext } 1263 1267 end -
a/actionpack/lib/action_controller/dispatcher.rb
old new 153 153 154 154 if unprepared || force 155 155 run_callbacks :prepare 156 ActionView::TemplateFinder.reload! unless ActionView::Base.cache_template_loading 156 157 self.unprepared = false 157 158 end 158 159 end -
a/actionpack/lib/action_controller/layout.rb
old new 208 208 conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})} 209 209 end 210 210 211 def layout_directory_exists_cache212 @@layout_directory_exists_cache ||= Hash.new do |h, dirname|213 h[dirname] = File.directory? dirname214 end215 end216 217 211 def default_layout_with_format(format, layout) 218 212 list = layout_list 219 213 if list.grep(%r{layouts/#{layout}\.#{format}(\.[a-z][0-9a-z]*)+$}).empty? … … 313 307 end 314 308 end 315 309 316 # Does a layout directory for this class exist?317 # we cache this info in a class level hash318 310 def layout_directory?(layout_name) 319 view_paths.find do |path| 320 next unless template_path = Dir[File.join(path, 'layouts', layout_name) + ".*"].first 321 self.class.send!(:layout_directory_exists_cache)[File.dirname(template_path)] 322 end 311 @template.finder.find_template_extension_from_handler(File.join('layouts', layout_name)) 323 312 end 324 313 end 325 314 end -
a/actionpack/lib/action_view.rb
old new 26 26 require 'action_view/template_handlers/erb' 27 27 require 'action_view/template_handlers/rjs' 28 28 29 require 'action_view/template_finder' 30 29 31 require 'action_view/base' 30 32 require 'action_view/partials' 31 33 require 'action_view/template_error' -
a/actionpack/lib/action_view/base.rb
old new 150 150 class Base 151 151 include ERB::Util 152 152 153 attr_reader :first_render 153 attr_reader :first_render, :finder 154 154 attr_accessor :base_path, :assigns, :template_extension 155 attr_accessor :controller , :view_paths155 attr_accessor :controller 156 156 157 157 attr_reader :logger, :response, :headers 158 158 attr_internal :cookies, :flash, :headers, :params, :request, :response, :session … … 204 204 @@template_args = {} 205 205 # Count the number of inline templates 206 206 @@inline_template_count = 0 207 # Maps template paths without extension to their file extension returned by pick_template_extension.208 # If for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions209 # used by pick_template_extension determines whether ext1 or ext2 will be stored.210 @@cached_template_extension = {}211 # Maps template paths / extensions to212 @@cached_base_paths = {}213 207 214 208 # Cache public asset paths 215 209 cattr_reader :computed_public_paths … … 241 235 # return the rendered template as a string. 242 236 def self.register_template_handler(extension, klass) 243 237 @@template_handlers[extension.to_sym] = klass 238 TemplateFinder.update_extension_cache_for(extension.to_s) 244 239 end 245 240 246 241 def self.template_handler_extensions 247 @@template_handler _extensions ||= @@template_handlers.keys.map(&:to_s).sort242 @@template_handlers.keys.map(&:to_s).sort 248 243 end 249 244 250 245 def self.register_default_template_handler(extension, klass) … … 265 260 register_template_handler :rxml, TemplateHandlers::Builder 266 261 267 262 def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc: 268 @view_paths = view_paths.respond_to?(:find) ? view_paths.dup : [*view_paths].compact269 263 @assigns = assigns_for_first_render 270 264 @assigns_added = nil 271 265 @controller = controller 272 @logger = controller && controller.logger 266 @logger = controller && controller.logger 267 @finder = TemplateFinder.new(self, view_paths) 273 268 end 274 269 275 270 # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true, … … 290 285 end 291 286 292 287 @first_render ||= template_path 293 template_path_without_extension, template_extension = path_and_extension(template_path)288 template_path_without_extension, template_extension = @finder.path_and_extension(template_path) 294 289 if use_full_path 295 290 if template_extension 296 template_file_name = full_template_path(template_path_without_extension, template_extension)291 template_file_name = @finder.template_exists?(template_path_without_extension, template_extension) 297 292 else 298 template_extension = pick_template_extension(template_path).to_s293 template_extension = @finder.pick_template_extension(template_path).to_s 299 294 unless template_extension 300 raise ActionViewError, "No template found for #{template_path} in #{ view_paths.inspect}"295 raise ActionViewError, "No template found for #{template_path} in #{@finder.view_paths.inspect}" 301 296 end 302 template_file_name = full_template_path(template_path, template_extension)297 template_file_name = @finder.template_exists?(template_path, template_extension) 303 298 template_extension = template_extension.gsub(/^.+\./, '') # strip off any formats 304 299 end 305 300 else … … 309 304 template_source = nil # Don't read the source until we know that it is required 310 305 311 306 if template_file_name.blank? 312 raise ActionViewError, "Couldn't find template file for #{template_path} in #{ view_paths.inspect}"307 raise ActionViewError, "Couldn't find template file for #{template_path} in #{@finder.view_paths.inspect}" 313 308 end 314 309 315 310 begin … … 319 314 e.sub_template_of(template_file_name) 320 315 raise e 321 316 else 322 raise TemplateError.new(find_base_path_for("#{template_path_without_extension}.#{template_extension}") || view_paths.first, template_file_name, @assigns, template_source, e) 317 raise TemplateError.new(@finder.find_base_path_for("#{template_path_without_extension}.#{template_extension}") || 318 @finder.view_paths.first, template_file_name, @assigns, template_source, e) 323 319 end 324 320 end 325 321 end … … 371 367 end 372 368 end 373 369 374 # Gets the full template path with base path for the given template_path and extension.375 #376 # full_template_path('users/show', 'html.erb')377 # # => '~/rails/app/views/users/show.html.erb378 #379 def full_template_path(template_path, extension)380 if @@cache_template_extensions381 (@@cached_base_paths[template_path] ||= {})[extension.to_s] ||= find_full_template_path(template_path, extension)382 else383 find_full_template_path(template_path, extension)384 end385 end386 387 # Gets the extension for an existing template with the given template_path.388 # Returns the format with the extension if that template exists.389 #390 # pick_template_extension('users/show')391 # # => 'html.erb'392 #393 # pick_template_extension('users/legacy')394 # # => "rhtml"395 #396 def pick_template_extension(template_path)#:nodoc:397 if @@cache_template_extensions398 (@@cached_template_extension[template_path] ||= {})[template_format] ||= find_template_extension_for(template_path)399 else400 find_template_extension_for(template_path)401 end402 end403 404 def file_exists?(template_path)#:nodoc:405 template_file_name, template_file_extension = path_and_extension(template_path)406 if template_file_extension407 template_exists?(template_file_name, template_file_extension)408 else409 template_exists?(template_file_name, pick_template_extension(template_path))410 end411 end412 413 370 # Returns true is the file may be rendered implicitly. 414 371 def file_public?(template_path)#:nodoc: 415 372 template_path.split('/').last[0,1] != '_' … … 422 379 @template_format = format.blank? ? :html : format.to_sym 423 380 end 424 381 425 # Adds a view_path to the front of the view_paths array.426 # This change affects the current request only.427 #428 # @template.prepend_view_path("views/default")429 # @template.prepend_view_path(["views/default", "views/custom"])430 #431 def prepend_view_path(path)432 @view_paths.unshift(*path)433 end434 435 # Adds a view_path to the end of the view_paths array.436 # This change affects the current request only.437 #438 # @template.append_view_path("views/default")439 # @template.append_view_path(["views/default", "views/custom"])440 #441 def append_view_path(path)442 @view_paths.push(*path)443 end444 445 382 private 446 383 def wrap_content_for_layout(content) 447 384 original_content_for_layout = @content_for_layout 448 385 @content_for_layout = content 449 386 returning(yield) { @content_for_layout = original_content_for_layout } 450 387 end 451 452 def find_full_template_path(template_path, extension)453 file_name = "#{template_path}.#{extension}"454 base_path = find_base_path_for(file_name)455 base_path.blank? ? "" : "#{base_path}/#{file_name}"456 end457 458 # Asserts the existence of a template.459 def template_exists?(template_path, extension)460 file_path = full_template_path(template_path, extension)461 !file_path.blank? && @@method_names.has_key?(file_path) || File.exist?(file_path)462 end463 464 # Splits the path and extension from the given template_path and returns as an array.465 def path_and_extension(template_path)466 template_path_without_extension = template_path.sub(/\.(\w+)$/, '')467 [ template_path_without_extension, $1 ]468 end469 470 # Returns the view path that contains the given relative template path.471 def find_base_path_for(template_file_name)472 view_paths.find { |p| File.file?(File.join(p, template_file_name)) }473 end474 475 # Returns the view path that the full path resides in.476 def extract_base_path_from(full_path)477 view_paths.find { |p| full_path[0..p.size - 1] == p }478 end479 480 # Determines the template's file extension, such as rhtml, rxml, or rjs.481 def find_template_extension_for(template_path)482 find_template_extension_from_handler(template_path, true) ||483 find_template_extension_from_handler(template_path) ||484 find_template_extension_from_first_render()485 end486 487 def find_template_extension_from_handler(template_path, formatted = nil)488 checked_template_path = formatted ? "#{template_path}.#{template_format}" : template_path489 490 self.class.template_handler_extensions.each do |extension|491 if template_exists?(checked_template_path, extension)492 return formatted ? "#{template_format}.#{extension}" : extension.to_s493 end494 end495 nil496 end497 498 # Determine the template extension from the <tt>@first_render</tt> filename499 def find_template_extension_from_first_render500 File.basename(@first_render.to_s)[/^[^.]+\.(.+)$/, 1]501 end502 388 503 389 # This method reads a template file. 504 390 def read_template_file(template_path, extension) … … 603 489 logger.debug "Backtrace: #{e.backtrace.join("\n")}" 604 490 end 605 491 606 raise TemplateError.new(extract_base_path_from(file_name) || view_paths.first, file_name || template, @assigns, template, e) 492 raise TemplateError.new(@finder.extract_base_path_from(file_name) || 493 @finder.view_paths.first, file_name || template, @assigns, template, e) 607 494 end 608 495 609 496 @@compile_time[render_symbol] = Time.now -
a/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 = {}, options = nil, &block) 35 template_extension = find_template_extension_for(first_render)[/\.?(\w+)$/, 1].to_sym35 template_extension = @finder.pick_template_extension(first_render)[/\.?(\w+)$/, 1].to_sym 36 36 handler = Base.handler_class_for_extension(template_extension) 37 37 handler.new(@controller).cache_fragment(block, name, options) 38 38 end -
/dev/null
old new 1 module ActionView #:nodoc: 2 class TemplateFinder #:nodoc: 3 4 class InvalidViewPath < StandardError #:nodoc: 5 end 6 7 cattr_reader :processed_view_paths 8 @@processed_view_paths = Hash.new {|hash, key| hash[key] = []} 9 10 cattr_reader :file_extension_cache 11 @@file_extension_cache = Hash.new {|hash, key| 12 hash[key] = Hash.new {|hash, key| hash[key] = []} 13 } 14 15 class << self #:nodoc: 16 17 # This method is not thread safe. Mutex should be used whenever this is accessed from an instance method 18 def process_view_paths(*view_paths) 19 view_paths.flatten.compact.each do |dir| 20 next if @@processed_view_paths.has_key?(dir) 21 22 @@processed_view_paths[dir] = [] 23 Dir.glob("#{dir}/**/*").each do |file| 24 unless File.directory?(file) 25 @@processed_view_paths[dir] << file.split(dir).last.sub(/^\//, '') 26 27 # Build extension cache 28 extension = file.split(".").last 29 if template_handler_extensions.include?(extension) 30 key = file.split(dir).last.sub(/^\//, '').sub(/\.(\w+)$/, '') 31 @@file_extension_cache[dir][key] << extension 32 end 33 end 34 end 35 end 36 end 37 38 def update_extension_cache_for(extension) 39 @@processed_view_paths.keys.each do |dir| 40 Dir.glob("#{dir}/**/*.#{extension}").each do |file| 41 key = file.split(dir).last.sub(/^\//, '').sub(/\.(\w+)$/, '') 42 @@file_extension_cache[dir][key] << extension 43 end 44 end 45 end 46 47 def template_handler_extensions 48 ActionView::Base.template_handler_extensions 49 end 50 51 def reload! 52 view_paths = @@processed_view_paths.keys 53 54 @@processed_view_paths = Hash.new {|hash, key| hash[key] = []} 55 @@file_extension_cache = Hash.new {|hash, key| 56 hash[key] = Hash.new {|hash, key| hash[key] = []} 57 } 58 59 process_view_paths(view_paths) 60 end 61 end 62 63 attr_accessor :view_paths 64 65 def initialize(*args) 66 @template = args.shift 67 68 @view_paths = args.flatten 69 @view_paths = @view_paths.respond_to?(:find) ? @view_paths.dup : [*@view_paths].compact 70 check_view_paths(@view_paths) 71 end 72 73 def prepend_view_path(path) 74 @view_paths.unshift(*path) 75 76 self.class.process_view_paths(path) 77 end 78 79 def append_view_path(path) 80 @view_paths.push(*path) 81 82 self.class.process_view_paths(path) 83 end 84 85 def view_paths=(path) 86 @view_paths = path 87 self.class.process_view_paths(path) 88 end 89 90 def template_exists?(template_path, extension) 91 file_name = "#{template_path}.#{extension}" 92 base_path = find_base_path_for(file_name) 93 base_path.blank? ? false : "#{base_path}/#{file_name}" 94 end 95 96 def file_exists?(template_path) 97 template_file_name, template_file_extension = path_and_extension(template_path) 98 if template_file_extension 99 template_exists?(template_file_name, template_file_extension) 100 else 101 template_exists?(template_file_name, pick_template_extension(template_path)) 102 end 103 end 104 105 def find_base_path_for(template_file_name) 106 @view_paths.find { |path| processed_view_paths[path].include?(template_file_name) } 107 end 108 109 # Returns the view path that the full path resides in. 110 def extract_base_path_from(full_path) 111 @view_paths.find { |p| full_path[0..p.size - 1] == p } 112 end 113 114 # Gets the extension for an existing template with the given template_path. 115 # Returns the format with the extension if that template exists. 116 # 117 # pick_template_extension('users/show') 118 # # => 'html.erb' 119 # 120 # pick_template_extension('users/legacy') 121 # # => "rhtml" 122 # 123 def pick_template_extension(template_path) 124 find_template_extension_from_handler(template_path, true) || 125 find_template_extension_from_handler(template_path) || 126 find_template_extension_from_first_render() 127 end 128 129 def find_template_extension_from_handler(template_path, formatted = nil) 130 checked_template_path = formatted ? "#{template_path}.#{@template.template_format}" : template_path 131 132 view_paths.each do |path| 133 if file_extension_cache[path].has_key?(checked_template_path) 134 extension = file_extension_cache[path][checked_template_path].first 135 return formatted ? "#{@template.template_format}.#{extension}" : extension.to_s 136 end 137 end 138 nil 139 end 140 141 # Splits the path and extension from the given template_path and returns as an array. 142 def path_and_extension(template_path) 143 template_path_without_extension = template_path.sub(/\.(\w+)$/, '') 144 [ template_path_without_extension, $1 ] 145 end 146 147 # Determine the template extension from the <tt>@first_render</tt> filename 148 def find_template_extension_from_first_render 149 File.basename(@template.first_render.to_s)[/^[^.]+\.(.+)$/, 1] 150 end 151 152 private 153 154 def check_view_paths(view_paths) 155 view_paths.each do |path| 156 raise(InvalidViewPath, "Unprocessed view path found in #{view_paths.inspect}") unless processed_view_paths.has_key?(path) 157 end 158 end 159 160 end 161 end -
a/actionpack/test/controller/mime_responds_test.rb
old new 437 437 end 438 438 439 439 class AbstractPostController < ActionController::Base 440 class << self 441 def view_paths 442 [ File.dirname(__FILE__) + "/../fixtures/post_test/" ] 443 end 444 end 440 self.view_paths = File.dirname(__FILE__) + "/../fixtures/post_test/" 445 441 end 446 442 447 443 # For testing layouts which are set automatically -
a/actionpack/test/controller/render_test.rb
old new 199 199 end 200 200 201 201 TestController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] 202 Fun::GamesController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] 202 Fun::GamesController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ] 203 203 204 204 class RenderTest < Test::Unit::TestCase 205 205 def setup -
a/actionpack/test/controller/view_paths_test.rb
old new 16 16 def hello_world_at_request_time() render(:action => 'hello_world') end 17 17 private 18 18 def add_view_path 19 self.class.view_paths.unshift"#{LOAD_PATH_ROOT}/override"19 prepend_view_path "#{LOAD_PATH_ROOT}/override" 20 20 end 21 21 end 22 22 … … 27 27 28 28 def setup 29 29 TestController.view_paths = nil 30 ActionView::Base.cache_template_extensions = false31 30 32 31 @request = ActionController::TestRequest.new 33 32 @response = ActionController::TestResponse.new … … 45 44 46 45 def teardown 47 46 ActiveSupport::Deprecation.behavior = @old_behavior 48 ActionView::Base.cache_template_extensions = true49 47 end 50 48 51 49 def test_template_load_path_was_set_correctly … … 99 97 end 100 98 101 99 def test_view_paths_override 102 TestController. view_paths.unshift"#{LOAD_PATH_ROOT}/override"100 TestController.prepend_view_path "#{LOAD_PATH_ROOT}/override" 103 101 get :hello_world 104 102 assert_response :success 105 103 assert_equal "Hello overridden world!", @response.body -
/dev/null
old new 1 require 'abstract_unit' 2 3 class TemplateFinderTest < Test::Unit::TestCase 4 5 LOAD_PATH_ROOT = File.join(File.dirname(__FILE__), '..', 'fixtures') 6 7 def setup 8 ActionView::TemplateFinder.process_view_paths(LOAD_PATH_ROOT) 9 ActionView::Base::register_template_handler :mab, Class.new(ActionView::TemplateHandler) 10 @template = ActionView::Base.new 11 @finder = ActionView::TemplateFinder.new(@template, LOAD_PATH_ROOT) 12 end 13 14 def test_should_raise_exception_for_unprocessed_view_path 15 assert_raises ActionView::TemplateFinder::InvalidViewPath do 16 ActionView::TemplateFinder.new(@template, File.dirname(__FILE__)) 17 end 18 end 19 20 def test_should_cache_file_extension_properly 21 assert_equal ["builder", "erb", "rhtml", "rjs", "rxml", "mab"].sort, 22 ActionView::TemplateFinder.file_extension_cache[LOAD_PATH_ROOT].values.flatten.uniq.sort 23 24 assert_equal Dir.glob("#{LOAD_PATH_ROOT}/**/*.{erb,rjs,rjs,builder,rxml,mab}").size, 25 ActionView::TemplateFinder.file_extension_cache[LOAD_PATH_ROOT].keys.size 26 end 27 28 def test_should_cache_dir_content_properly 29 assert ActionView::TemplateFinder.processed_view_paths[LOAD_PATH_ROOT] 30 assert_equal Dir.glob("#{LOAD_PATH_ROOT}/**/*").find_all {|f| !File.directory?(f) }.size, 31 ActionView::TemplateFinder.processed_view_paths[LOAD_PATH_ROOT].size 32 end 33 34 uses_mocha 'Template finder tests' do 35 36 def test_should_update_extension_cache_when_template_handler_is_registered 37 ActionView::TemplateFinder.expects(:update_extension_cache_for).with("funky") 38 ActionView::Base::register_template_handler :funky, Class.new(ActionView::TemplateHandler) 39 end 40 41 end 42 43 end -
a/railties/environments/development.rb
old new 12 12 config.action_controller.consider_all_requests_local = true 13 13 config.action_view.debug_rjs = true 14 14 config.action_controller.perform_caching = false 15 config.action_view.cache_template_extensions = false16 15 17 16 # Don't care if the mailer can't send 18 17 config.action_mailer.raise_delivery_errors = false