aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/base.rb5
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb1
-rw-r--r--actionpack/lib/action_controller/layout.rb38
-rw-r--r--actionpack/lib/action_view/base.rb4
-rw-r--r--actionpack/lib/action_view/paths.rb67
-rw-r--r--actionpack/lib/action_view/renderable.rb2
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/controller/layout_test.rb14
-rw-r--r--actionpack/test/template/compiled_templates_test.rb9
-rw-r--r--actionpack/test/template/render_test.rb17
-rw-r--r--railties/lib/initializer.rb7
11 files changed, 74 insertions, 92 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index dca66ff0a5..7e38f95076 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -867,9 +867,8 @@ module ActionController #:nodoc:
end
end
- layout = pick_layout(options)
- response.layout = layout.path_without_format_and_extension if layout
- logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout
+ response.layout = layout = pick_layout(options)
+ logger.info("Rendering template within #{layout}") if logger && layout
if content_type = options[:content_type]
response.content_type = content_type.to_s
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index 6e4aba2280..1c7a4b0545 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -137,6 +137,7 @@ module ActionController
run_callbacks :prepare_dispatch
Routing::Routes.reload
+ ActionController::Base.view_paths.reload!
ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear
end
diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb
index 54108df06d..3631ce86af 100644
--- a/actionpack/lib/action_controller/layout.rb
+++ b/actionpack/lib/action_controller/layout.rb
@@ -175,12 +175,13 @@ module ActionController #:nodoc:
def default_layout(format) #:nodoc:
layout = read_inheritable_attribute(:layout)
return layout unless read_inheritable_attribute(:auto_layout)
- find_layout(layout, format)
+ @default_layout ||= {}
+ @default_layout[format] ||= default_layout_with_format(format, layout)
+ @default_layout[format]
end
- def find_layout(layout, *formats) #:nodoc:
- return layout if layout.respond_to?(:render)
- view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats)
+ def layout_list #:nodoc:
+ Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
end
private
@@ -188,7 +189,7 @@ module ActionController #:nodoc:
inherited_without_layout(child)
unless child.name.blank?
layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
- child.layout(layout_match, {}, true) if child.find_layout(layout_match, :all)
+ child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
end
end
@@ -199,6 +200,15 @@ module ActionController #:nodoc:
def normalize_conditions(conditions)
conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
end
+
+ def default_layout_with_format(format, layout)
+ list = layout_list
+ if list.grep(%r{layouts/#{layout}\.#{format}(\.[a-z][0-9a-z]*)+$}).empty?
+ (!list.grep(%r{layouts/#{layout}\.([a-z][0-9a-z]*)+$}).empty? && format == :html) ? layout : nil
+ else
+ layout
+ end
+ end
end
# Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
@@ -207,18 +217,20 @@ module ActionController #:nodoc:
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
def active_layout(passed_layout = nil)
layout = passed_layout || self.class.default_layout(default_template_format)
-
active_layout = case layout
+ when String then layout
when Symbol then __send__(layout)
when Proc then layout.call(self)
- else layout
end
+ # Explicitly passed layout names with slashes are looked up relative to the template root,
+ # but auto-discovered layouts derived from a nested controller will contain a slash, though be relative
+ # to the 'layouts' directory so we have to check the file system to infer which case the layout name came from.
if active_layout
- if layout = self.class.find_layout(active_layout, @template.template_format)
- layout
+ if active_layout.include?('/') && ! layout_directory?(active_layout)
+ active_layout
else
- raise ActionView::MissingTemplate.new(self.class.view_paths, active_layout)
+ "layouts/#{active_layout}"
end
end
end
@@ -259,6 +271,12 @@ module ActionController #:nodoc:
end
end
+ def layout_directory?(layout_name)
+ @template.__send__(:_pick_template, "#{File.join('layouts', layout_name)}.#{@template.template_format}") ? true : false
+ rescue ActionView::MissingTemplate
+ false
+ end
+
def default_template_format
response.template.template_format
end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index da1f283deb..7697848713 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -322,7 +322,9 @@ module ActionView #:nodoc:
end
# OPTIMIZE: Checks to lookup template in view path
- if template = self.view_paths.find_template(template_file_name, template_format)
+ if template = self.view_paths["#{template_file_name}.#{template_format}"]
+ template
+ elsif template = self.view_paths[template_file_name]
template
elsif (first_render = @_render_stack.first) && first_render.respond_to?(:format_and_extension) &&
(template = self.view_paths["#{template_file_name}.#{first_render.format_and_extension}"])
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index f01ed9e6e0..d6bf2137af 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -40,10 +40,18 @@ module ActionView #:nodoc:
end
class Path #:nodoc:
+ def self.eager_load_templates!
+ @eager_load_templates = true
+ end
+
+ def self.eager_load_templates?
+ @eager_load_templates || false
+ end
+
attr_reader :path, :paths
delegate :to_s, :to_str, :hash, :inspect, :to => :path
- def initialize(path, load = false)
+ def initialize(path, load = true)
raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
@path = path.freeze
reload! if load
@@ -57,35 +65,9 @@ module ActionView #:nodoc:
to_str == path.to_str
end
- # Returns a ActionView::Template object for the given path string. The
- # input path should be relative to the view path directory,
- # +hello/index.html.erb+. This method also has a special exception to
- # match partial file names without a handler extension. So
- # +hello/index.html+ will match the first template it finds with a
- # known template extension, +hello/index.html.erb+. Template extensions
- # should not be confused with format extensions +html+, +js+, +xml+,
- # etc. A format must be supplied to match a formated file. +hello/index+
- # will never match +hello/index.html.erb+.
- #
- # This method also has two different implementations, one that is "lazy"
- # and makes file system calls every time and the other is cached,
- # "eager" which looks up the template in an in memory index. The "lazy"
- # version is designed for development where you want to automatically
- # find new templates between requests. The "eager" version is designed
- # for production mode and it is much faster but requires more time
- # upfront to build the file index.
def [](path)
- if loaded?
- @paths[path]
- else
- Dir.glob("#{@path}/#{path}*").each do |file|
- template = create_template(file)
- if path == template.path_without_extension || path == template.path
- return template
- end
- end
- nil
- end
+ raise "Unloaded view path! #{@path}" unless @loaded
+ @paths[path]
end
def loaded?
@@ -102,7 +84,9 @@ module ActionView #:nodoc:
@paths = {}
templates_in_path do |template|
- template.freeze
+ # Eager load memoized methods and freeze cached template
+ template.freeze if self.class.eager_load_templates?
+
@paths[template.path] = template
@paths[template.path_without_extension] ||= template
end
@@ -114,13 +98,11 @@ module ActionView #:nodoc:
private
def templates_in_path
(Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
- yield create_template(file) unless File.directory?(file)
+ unless File.directory?(file)
+ yield Template.new(file.split("#{self}/").last, self)
+ end
end
end
-
- def create_template(file)
- Template.new(file.split("#{self}/").last, self)
- end
end
def load
@@ -139,20 +121,5 @@ module ActionView #:nodoc:
end
nil
end
-
- def find_template(path, *formats)
- if formats && formats.first == :all
- formats = Mime::EXTENSION_LOOKUP.values.map(&:to_sym)
- end
- formats.each do |format|
- if template = self["#{path}.#{format}"]
- return template
- end
- end
- if template = self[path]
- return template
- end
- nil
- end
end
end
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index c04399f2c9..5ff5569db6 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -96,7 +96,7 @@ module ActionView
# The template will be compiled if the file has not been compiled yet, or
# if local_assigns has a new key, which isn't supported by the compiled code yet.
def recompile?(symbol)
- !(frozen? && Base::CompiledTemplates.method_defined?(symbol))
+ !(ActionView::PathSet::Path.eager_load_templates? && Base::CompiledTemplates.method_defined?(symbol))
end
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 24fdc03507..bee598e1ef 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -30,8 +30,8 @@ ActionController::Base.logger = nil
ActionController::Routing::Routes.reload rescue nil
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
+ActionView::PathSet::Path.eager_load_templates!
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
-ActionController::Base.view_paths.load
def uses_mocha(test_name)
yield
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index 18c01f755c..61c20f8299 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -3,10 +3,6 @@ require 'abstract_unit'
# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited
# method has access to the view_paths array when looking for a layout to automatically assign.
old_load_paths = ActionController::Base.view_paths
-
-ActionView::Template::register_template_handler :mab,
- lambda { |template| template.source.inspect }
-
ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ]
class LayoutTest < ActionController::Base
@@ -35,6 +31,9 @@ end
class MultipleExtensions < LayoutTest
end
+ActionView::Template::register_template_handler :mab,
+ lambda { |template| template.source.inspect }
+
class LayoutAutoDiscoveryTest < ActionController::TestCase
def setup
@request.host = "www.nextangle.com"
@@ -53,9 +52,10 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
end
def test_third_party_template_library_auto_discovers_layout
+ ThirdPartyTemplateLibraryController.view_paths.reload!
@controller = ThirdPartyTemplateLibraryController.new
get :hello
- assert_equal 'layouts/third_party_template_library.mab', @controller.active_layout.to_s
+ assert_equal 'layouts/third_party_template_library', @controller.active_layout
assert_equal 'layouts/third_party_template_library', @response.layout
assert_response :success
assert_equal 'Mab', @response.body
@@ -64,14 +64,14 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_namespaced_controllers_auto_detect_layouts
@controller = ControllerNameSpace::NestedController.new
get :hello
- assert_equal 'layouts/controller_name_space/nested', @controller.active_layout.to_s
+ assert_equal 'layouts/controller_name_space/nested', @controller.active_layout
assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
end
def test_namespaced_controllers_auto_detect_layouts
@controller = MultipleExtensions.new
get :hello
- assert_equal 'layouts/multiple_extensions.html.erb', @controller.active_layout.to_s
+ assert_equal 'layouts/multiple_extensions', @controller.active_layout
assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
end
end
diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb
index 0b3d039409..f7688b2072 100644
--- a/actionpack/test/template/compiled_templates_test.rb
+++ b/actionpack/test/template/compiled_templates_test.rb
@@ -30,6 +30,15 @@ uses_mocha 'TestTemplateRecompilation' do
assert_equal "Hello world!", render(:file => "test/hello_world.erb")
end
+ def test_compiled_template_will_always_be_recompiled_when_eager_loaded_templates_is_off
+ ActionView::PathSet::Path.expects(:eager_load_templates?).times(4).returns(false)
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", render(:file => "#{FIXTURE_LOAD_PATH}/test/hello_world.erb")
+ ActionView::Template.any_instance.expects(:compile!).times(3)
+ 3.times { assert_equal "Hello world!", render(:file => "#{FIXTURE_LOAD_PATH}/test/hello_world.erb") }
+ assert_equal 1, @compiled_templates.instance_methods.size
+ end
+
private
def render(*args)
ActionView::Base.new(ActionController::Base.view_paths, {}).render(*args)
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 28d38b0c76..b316d5c23e 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -4,9 +4,7 @@ require 'controller/fake_models'
class ViewRenderTest < Test::Unit::TestCase
def setup
@assigns = { :secret => 'in the sauce' }
- view_paths = ActionController::Base.view_paths
- @view = ActionView::Base.new(view_paths, @assigns)
- assert view_paths.first.loaded?
+ @view = ActionView::Base.new(ActionController::Base.view_paths, @assigns)
end
def test_render_file
@@ -159,7 +157,7 @@ class ViewRenderTest < Test::Unit::TestCase
end
def test_render_fallbacks_to_erb_for_unknown_types
- assert_equal "Hello, World!", @view.render(:inline => "Hello, World!", :type => :bar)
+ assert_equal "Hello, World!", @view.render(:inline => "Hello, World!", :type => :foo)
end
CustomHandler = lambda do |template|
@@ -198,14 +196,3 @@ class ViewRenderTest < Test::Unit::TestCase
@view.render(:file => "test/nested_layout.erb", :layout => "layouts/yield")
end
end
-
-class LazyViewRenderTest < ViewRenderTest
- # Test the same thing as above, but make sure the view path
- # is not eager loaded
- def setup
- @assigns = { :secret => 'in the sauce' }
- view_paths = ActionView::Base.process_view_paths(FIXTURE_LOAD_PATH)
- @view = ActionView::Base.new(view_paths, @assigns)
- assert !view_paths.first.loaded?
- end
-end
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index a69898e2c5..0c06d1bf21 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -372,10 +372,9 @@ Run `rake gems:install` to install the missing gems.
def load_view_paths
if configuration.frameworks.include?(:action_view)
- if configuration.cache_classes
- ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller)
- ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer)
- end
+ ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes
+ ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller)
+ ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer)
end
end