From ddd85ef9c6a6297a8ff28816d907bbbf2eae5856 Mon Sep 17 00:00:00 2001
From: artemave <artemave@gmail.com>
Date: Fri, 17 Sep 2010 20:39:14 +0000
Subject: #948 template_inheritance

---
 actionpack/lib/abstract_controller/layouts.rb      |  4 +-
 actionpack/lib/abstract_controller/rendering.rb    | 16 ++++++--
 .../lib/action_controller/metal/implicit_render.rb |  4 +-
 actionpack/lib/action_view/lookup_context.rb       | 31 ++++++++++------
 actionpack/lib/action_view/path_set.rb             | 16 +++++---
 .../lib/action_view/renderer/partial_renderer.rb   |  4 +-
 .../lib/action_view/renderer/template_renderer.rb  |  4 +-
 actionpack/lib/action_view/testing/resolvers.rb    |  6 ++-
 .../test/abstract/abstract_controller_test.rb      | 14 +++----
 actionpack/test/abstract/render_test.rb            |  4 +-
 .../controller/new_base/render_partial_test.rb     | 25 ++++++++++++-
 actionpack/test/controller/new_base/render_test.rb | 43 +++++++++++++++++++++-
 12 files changed, 129 insertions(+), 42 deletions(-)

diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 606f7eedec..4ee54474cc 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -265,11 +265,11 @@ module AbstractController
           raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
         when nil
           if name
-            _prefix = "layouts" unless _implied_layout_name =~ /\blayouts/
+            _prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
 
             self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
               def _layout
-                if template_exists?("#{_implied_layout_name}", #{_prefix.inspect})
+                if template_exists?("#{_implied_layout_name}", #{_prefixes.inspect})
                   "#{_implied_layout_name}"
                 else
                   super
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 91b75273fa..06f4441609 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -114,9 +114,17 @@ module AbstractController
       view_context.render(options)
     end
 
-    # The prefix used in render "foo" shortcuts.
-    def _prefix
-      controller_path
+    # The prefixes used in render "foo" shortcuts.
+    def _prefixes
+      prefixes = [controller_path]
+      parent_controller = self.class.superclass
+
+      until parent_controller.abstract?
+        prefixes << parent_controller.controller_path
+        parent_controller = parent_controller.superclass
+      end
+
+      prefixes
     end
 
     private
@@ -156,7 +164,7 @@ module AbstractController
       end
 
       if (options.keys & [:partial, :file, :template, :once]).empty?
-        options[:prefix] ||= _prefix
+        options[:prefixes] ||= _prefixes
       end
 
       options[:template] ||= (options[:action] || action_name).to_s
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index 282dcf66b3..cfa7004048 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -12,10 +12,10 @@ module ActionController
 
     def method_for_action(action_name)
       super || begin
-        if template_exists?(action_name.to_s, _prefix)
+        if template_exists?(action_name.to_s, _prefixes)
           "default_render"
         end
       end
     end
   end
-end
\ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index d524c68450..14cccd88f0 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -78,17 +78,17 @@ module ActionView
         @view_paths = ActionView::Base.process_view_paths(paths)
       end
 
-      def find(name, prefix = nil, partial = false, keys = [])
-        @view_paths.find(*args_for_lookup(name, prefix, partial, keys))
+      def find(name, prefixes = [], partial = false, keys = [])
+        @view_paths.find(*args_for_lookup(name, prefixes, partial, keys))
       end
       alias :find_template :find
 
-      def find_all(name, prefix = nil, partial = false, keys = [])
-        @view_paths.find_all(*args_for_lookup(name, prefix, partial, keys))
+      def find_all(name, prefixes = [], partial = false, keys = [])
+        @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys))
       end
 
-      def exists?(name, prefix = nil, partial = false, keys = [])
-        @view_paths.exists?(*args_for_lookup(name, prefix, partial, keys))
+      def exists?(name, prefixes = [], partial = false, keys = [])
+        @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys))
       end
       alias :template_exists? :exists?
 
@@ -107,18 +107,25 @@ module ActionView
 
     protected
 
-      def args_for_lookup(name, prefix, partial, keys) #:nodoc:
-        name, prefix = normalize_name(name, prefix)
-        [name, prefix, partial || false, @details, details_key, keys]
+      def args_for_lookup(name, prefixes, partial, keys) #:nodoc:
+        name, prefixes = normalize_name(name, prefixes)
+        [name, prefixes, partial || false, @details, details_key, keys]
       end
 
       # Support legacy foo.erb names even though we now ignore .erb
       # as well as incorrectly putting part of the path in the template
       # name instead of the prefix.
-      def normalize_name(name, prefix) #:nodoc:
+      def normalize_name(name, prefixes) #:nodoc:
         name  = name.to_s.gsub(handlers_regexp, '')
         parts = name.split('/')
-        return parts.pop, [prefix, *parts].compact.join("/")
+        name  = parts.pop
+        prx = if not prefixes or prefixes.empty?
+                [parts.compact.join('/')]
+              else
+                prefixes.map {|prefix| [prefix, *parts].compact.join('/') }
+              end
+
+        return name, prx
       end
 
       def default_handlers #:nodoc:
@@ -237,4 +244,4 @@ module ActionView
     include Details
     include ViewPaths
   end
-end
\ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/path_set.rb b/actionpack/lib/action_view/path_set.rb
index fa35120a0d..a7078b8232 100644
--- a/actionpack/lib/action_view/path_set.rb
+++ b/actionpack/lib/action_view/path_set.rb
@@ -11,15 +11,19 @@ module ActionView #:nodoc:
     end
 
     def find(*args)
-      find_all(*args).first || raise(MissingTemplate.new(self, "#{args[1]}/#{args[0]}", args[3], args[2]))
+      template = find_all(*args).first
+      template or raise MissingTemplate.new(self, "{#{args[1].join(',')},}/#{args[0]}", args[3], args[2])
     end
 
-    def find_all(*args)
-      each do |resolver|
-        templates = resolver.find_all(*args)
-        return templates unless templates.empty?
+    def find_all(path, prefixes = [], *args)
+      templates = []
+      prefixes.each do |prefix|
+        each do |resolver|
+          templates << resolver.find_all(path, prefix, *args)
+        end
+        # return templates unless templates.flatten!.empty? XXX this was original behavior; turns this method into find_some, but probably makes it faster
       end
-      []
+      templates.flatten
     end
 
     def exists?(*args)
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 317479ad4c..e83f7be610 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -111,8 +111,8 @@ module ActionView
     end 
 
     def find_template(path=@path, locals=@locals.keys)
-      prefix = @view.controller_prefix unless path.include?(?/)
-      @lookup_context.find_template(path, prefix, true, locals)
+      prefixes = path.include?(?/) ? [] : @view.controller._prefixes
+      @lookup_context.find_template(path, prefixes, true, locals)
     end
 
     def collection_with_template
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index ece3f621b6..4a0bc503d3 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -43,13 +43,13 @@ module ActionView
       if options.key?(:text)
         Template::Text.new(options[:text], formats.try(:first))
       elsif options.key?(:file)
-        with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
+        with_fallbacks { find_template(options[:file], [], false, keys) }
       elsif options.key?(:inline)
         handler = Template.handler_for_extension(options[:type] || "erb")
         Template.new(options[:inline], "inline template", handler, :locals => keys)
       elsif options.key?(:template)
         options[:template].respond_to?(:render) ?
-          options[:template] : find_template(options[:template], options[:prefix], false, keys)
+          options[:template] : find_template(options[:template], options[:prefixes], false, keys)
       end
     end
 
diff --git a/actionpack/lib/action_view/testing/resolvers.rb b/actionpack/lib/action_view/testing/resolvers.rb
index 55583096e0..5c5cab7c7d 100644
--- a/actionpack/lib/action_view/testing/resolvers.rb
+++ b/actionpack/lib/action_view/testing/resolvers.rb
@@ -13,7 +13,11 @@ module ActionView #:nodoc:
       @hash = hash
     end
 
-    private
+    def to_s
+      @hash.keys.join(', ')
+    end
+
+  private
 
     def query(path, exts, formats)
       query = ""
diff --git a/actionpack/test/abstract/abstract_controller_test.rb b/actionpack/test/abstract/abstract_controller_test.rb
index 19855490b4..981c023d38 100644
--- a/actionpack/test/abstract/abstract_controller_test.rb
+++ b/actionpack/test/abstract/abstract_controller_test.rb
@@ -30,14 +30,14 @@ module AbstractController
     class RenderingController < AbstractController::Base
       include ::AbstractController::Rendering
 
-      def _prefix() end
+      def _prefixes
+        []
+      end
 
       def render(options = {})
         if options.is_a?(String)
           options = {:_template_name => options}
         end
-
-        options[:_prefix] = _prefix
         super
       end
 
@@ -116,8 +116,8 @@ module AbstractController
         name.underscore
       end
 
-      def _prefix
-        self.class.prefix
+      def _prefixes
+        [self.class.prefix]
       end
     end
 
@@ -157,10 +157,10 @@ module AbstractController
       private
       def self.layout(formats)
         begin
-          find_template(name.underscore, {:formats => formats}, :_prefix => "layouts")
+          find_template(name.underscore, {:formats => formats}, :_prefixes => ["layouts"])
         rescue ActionView::MissingTemplate
           begin
-            find_template("application", {:formats => formats}, :_prefix => "layouts")
+            find_template("application", {:formats => formats}, :_prefixes => ["layouts"])
           rescue ActionView::MissingTemplate
           end
         end
diff --git a/actionpack/test/abstract/render_test.rb b/actionpack/test/abstract/render_test.rb
index 25dc8bd804..b9293d1241 100644
--- a/actionpack/test/abstract/render_test.rb
+++ b/actionpack/test/abstract/render_test.rb
@@ -6,8 +6,8 @@ module AbstractController
     class ControllerRenderer < AbstractController::Base
       include AbstractController::Rendering
 
-      def _prefix
-        "renderer"
+      def _prefixes
+        %w[renderer]
       end
 
       self.view_paths = [ActionView::FixtureResolver.new(
diff --git a/actionpack/test/controller/new_base/render_partial_test.rb b/actionpack/test/controller/new_base/render_partial_test.rb
index d800ea264d..83b0d039ad 100644
--- a/actionpack/test/controller/new_base/render_partial_test.rb
+++ b/actionpack/test/controller/new_base/render_partial_test.rb
@@ -9,7 +9,10 @@ module RenderPartial
       "render_partial/basic/basic.html.erb"      => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>",
       "render_partial/basic/with_json.html.erb"  => "<%= render 'with_json.json' %>",
       "render_partial/basic/_with_json.json.erb" => "<%= render 'final' %>",
-      "render_partial/basic/_final.json.erb"     => "{ final: json }"
+      "render_partial/basic/_final.json.erb"     => "{ final: json }",
+      "render_partial/basic/overriden.html.erb"    => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'overriden' %><%= @test_unchanged %>",
+      "render_partial/basic/_overriden.html.erb"    => "ParentPartial!",
+      "render_partial/child/_overriden.html.erb"    => "OverridenPartial!"
     )]
 
     def html_with_json_inside_json
@@ -20,7 +23,13 @@ module RenderPartial
       @test_unchanged = 'hello'
       render :action => "basic"
     end
+
+    def overriden
+      @test_unchanged = 'hello'
+    end
   end
+  
+  class ChildController < BasicController; end
 
   class TestPartial < Rack::TestCase
     testing BasicController
@@ -37,4 +46,18 @@ module RenderPartial
     end
   end
 
+  class TestInheritedPartial < Rack::TestCase
+    testing ChildController
+
+    test "partial from parent controller gets picked if missing in child one" do
+      get :changing
+      assert_response("goodbyeBasicPartial!goodbye")
+    end
+
+    test "partial from child controller gets picked" do
+      get :overriden
+      assert_response("goodbyeOverridenPartial!goodbye")
+    end
+  end
+
 end
diff --git a/actionpack/test/controller/new_base/render_test.rb b/actionpack/test/controller/new_base/render_test.rb
index df97a2725b..a520934c5b 100644
--- a/actionpack/test/controller/new_base/render_test.rb
+++ b/actionpack/test/controller/new_base/render_test.rb
@@ -6,7 +6,11 @@ module Render
       "render/blank_render/index.html.erb"                  => "Hello world!",
       "render/blank_render/access_request.html.erb"         => "The request: <%= request.method.to_s.upcase %>",
       "render/blank_render/access_action_name.html.erb"     => "Action Name: <%= action_name %>",
-      "render/blank_render/access_controller_name.html.erb" => "Controller Name: <%= controller_name %>"
+      "render/blank_render/access_controller_name.html.erb" => "Controller Name: <%= controller_name %>",
+      "render/blank_render/overriden_with_own_view_paths_appended.html.erb"              => "parent content",
+      "render/blank_render/overriden_with_own_view_paths_prepended.html.erb"              => "parent content",
+      "render/blank_render/overriden.html.erb"              => "parent content",
+      "render/child_render/overriden.html.erb"              => "child content"
     )]
 
     def index
@@ -21,6 +25,15 @@ module Render
       render :action => "access_action_name"
     end
 
+    def overriden_with_own_view_paths_appended
+    end
+
+    def overriden_with_own_view_paths_prepended
+    end
+
+    def overriden
+    end
+
     private
 
     def secretz
@@ -35,6 +48,11 @@ module Render
     end
   end
 
+  class ChildRenderController < BlankRenderController
+    append_view_path ActionView::FixtureResolver.new("render/child_render/overriden_with_own_view_paths_appended.html.erb" => "child content")
+    prepend_view_path ActionView::FixtureResolver.new("render/child_render/overriden_with_own_view_paths_prepended.html.erb" => "child content")
+  end
+
   class RenderTest < Rack::TestCase
     test "render with blank" do
       with_routing do |set|
@@ -94,4 +112,27 @@ module Render
       assert_body "Controller Name: blank_render"
     end
   end
+
+  class TestViewInheritance < Rack::TestCase
+    test "Template from child controller gets picked over parent one" do
+      get "/render/child_render/overriden"
+      assert_body "child content"
+    end
+
+    test "Template from child controller with custom view_paths prepended gets picked over parent one" do
+      get "/render/child_render/overriden_with_own_view_paths_prepended"
+      assert_body "child content"
+    end
+
+    test "Template from child controller with custom view_paths appended gets picked over parent one" do
+      get "/render/child_render/overriden_with_own_view_paths_appended"
+      assert_body "child content"
+    end
+
+    test "Template from parent controller gets picked if missing in child controller" do
+      get "/render/child_render/index"
+      assert_body "Hello world!"
+    end
+  end
+
 end
-- 
cgit v1.2.3