aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
authorrick <technoweenie@gmail.com>2008-06-19 09:59:36 -0700
committerrick <technoweenie@gmail.com>2008-06-19 09:59:36 -0700
commit10c581a6deed66e8b62de6e7a3621a63de90baad (patch)
tree588ceb1118761602caa8ccf248dd08d59f33896c /actionpack/lib
parent64637da284ed4685591c178202ee103e6bee71cf (diff)
parent81025b5808886289f54d698f73f4199c99223e7e (diff)
downloadrails-10c581a6deed66e8b62de6e7a3621a63de90baad.tar.gz
rails-10c581a6deed66e8b62de6e7a3621a63de90baad.tar.bz2
rails-10c581a6deed66e8b62de6e7a3621a63de90baad.zip
fix merge
Diffstat (limited to 'actionpack/lib')
-rwxr-xr-xactionpack/lib/action_controller/base.rb35
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb41
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb2
-rw-r--r--actionpack/lib/action_controller/integration.rb9
-rw-r--r--actionpack/lib/action_controller/layout.rb2
-rw-r--r--actionpack/lib/action_controller/performance_test.rb16
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb4
-rw-r--r--actionpack/lib/action_controller/resources.rb6
-rw-r--r--actionpack/lib/action_controller/templates/rescues/layout.erb2
-rw-r--r--actionpack/lib/action_controller/verification.rb2
-rw-r--r--actionpack/lib/action_view.rb10
-rw-r--r--actionpack/lib/action_view/base.rb107
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb5
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb5
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb60
-rw-r--r--actionpack/lib/action_view/inline_template.rb9
-rw-r--r--actionpack/lib/action_view/partial_template.rb76
-rw-r--r--actionpack/lib/action_view/template.rb109
-rw-r--r--actionpack/lib/action_view/template_file.rb88
-rw-r--r--actionpack/lib/action_view/template_finder.rb177
-rw-r--r--actionpack/lib/action_view/template_handler.rb1
-rw-r--r--actionpack/lib/action_view/template_handlers.rb46
-rw-r--r--actionpack/lib/action_view/template_handlers/compilable.rb2
-rw-r--r--actionpack/lib/action_view/test_case.rb5
-rw-r--r--actionpack/lib/action_view/view_load_paths.rb103
28 files changed, 520 insertions, 415 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index a036600c2b..bf34edcd85 100755
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -283,13 +283,6 @@ module ActionController #:nodoc:
@@debug_routes = true
cattr_accessor :debug_routes
- # Indicates to Mongrel or Webrick whether to allow concurrent action
- # processing. Your controller actions and any other code they call must
- # also behave well when called from concurrent threads. Turned off by
- # default.
- @@allow_concurrency = false
- cattr_accessor :allow_concurrency
-
# Modern REST web services often need to submit complex data to the web application.
# The <tt>@@param_parsers</tt> hash lets you register handlers which will process the HTTP body and add parameters to the
# <tt>params</tt> hash. These handlers are invoked for POST and PUT requests.
@@ -428,8 +421,7 @@ module ActionController #:nodoc:
end
def view_paths=(value)
- @view_paths = value
- ActionView::TemplateFinder.process_view_paths(value)
+ @view_paths = ActionView::ViewLoadPaths.new(Array(value)) if value
end
# Adds a view_path to the front of the view_paths array.
@@ -441,8 +433,7 @@ module ActionController #:nodoc:
#
def prepend_view_path(path)
@view_paths = superclass.view_paths.dup if @view_paths.nil?
- view_paths.unshift(*path)
- ActionView::TemplateFinder.process_view_paths(path)
+ @view_paths.unshift(*path)
end
# Adds a view_path to the end of the view_paths array.
@@ -454,8 +445,7 @@ module ActionController #:nodoc:
#
def append_view_path(path)
@view_paths = superclass.view_paths.dup if @view_paths.nil?
- view_paths.push(*path)
- ActionView::TemplateFinder.process_view_paths(path)
+ @view_paths.push(*path)
end
# Replace sensitive parameter data from the request log.
@@ -613,8 +603,8 @@ module ActionController #:nodoc:
#
# This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
# would have slashed-off the path components after the changed action.
- def url_for(options = nil) #:doc:
- case options || {}
+ def url_for(options = {}) #:doc:
+ case options
when String
options
when Hash
@@ -647,11 +637,11 @@ module ActionController #:nodoc:
# View load paths for controller.
def view_paths
- @template.finder.view_paths
+ @template.view_paths
end
def view_paths=(value)
- @template.finder.view_paths = value # Mutex needed
+ @template.view_paths = ViewLoadPaths.new(value)
end
# Adds a view_path to the front of the view_paths array.
@@ -661,7 +651,7 @@ module ActionController #:nodoc:
# self.prepend_view_path(["views/default", "views/custom"])
#
def prepend_view_path(path)
- @template.finder.prepend_view_path(path) # Mutex needed
+ @template.view_paths.unshift(*path)
end
# Adds a view_path to the end of the view_paths array.
@@ -671,7 +661,7 @@ module ActionController #:nodoc:
# self.append_view_path(["views/default", "views/custom"])
#
def append_view_path(path)
- @template.finder.append_view_path(path) # Mutex needed
+ @template.view_paths.push(*path)
end
protected
@@ -1232,7 +1222,7 @@ module ActionController #:nodoc:
end
def template_exists?(template_name = default_template_name)
- @template.finder.file_exists?(template_name)
+ @template.file_exists?(template_name)
end
def template_public?(template_name = default_template_name)
@@ -1240,9 +1230,8 @@ module ActionController #:nodoc:
end
def template_exempt_from_layout?(template_name = default_template_name)
- extension = @template && @template.finder.pick_template_extension(template_name)
- name_with_extension = !template_name.include?('.') && extension ? "#{template_name}.#{extension}" : template_name
- @@exempt_from_layout.any? { |ext| name_with_extension =~ ext }
+ template_name = @template.send(:template_file_from_name, template_name) if @template
+ @@exempt_from_layout.any? { |ext| template_name.to_s =~ ext }
end
def default_template_name(action_name = self.action_name)
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index c4b0a97a33..65a36f7f98 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -67,10 +67,10 @@ module ActionController #:nodoc:
if options[:action].is_a?(Array)
options[:action].dup.each do |action|
- expire_fragment(ActionCachePath.path_for(self, options.merge({ :action => action })))
+ expire_fragment(ActionCachePath.path_for(self, options.merge({ :action => action }), false))
end
else
- expire_fragment(ActionCachePath.path_for(self, options))
+ expire_fragment(ActionCachePath.path_for(self, options, false))
end
end
@@ -125,16 +125,24 @@ module ActionController #:nodoc:
attr_reader :path, :extension
class << self
- def path_for(controller, options)
- new(controller, options).path
+ def path_for(controller, options, infer_extension=true)
+ new(controller, options, infer_extension).path
end
end
-
- def initialize(controller, options = {})
- @extension = extract_extension(controller.request.path)
+
+ # When true, infer_extension will look up the cache path extension from the request's path & format.
+ # This is desirable when reading and writing the cache, but not when expiring the cache - expire_action should expire the same files regardless of the request format.
+ def initialize(controller, options = {}, infer_extension=true)
+ if infer_extension and options.is_a? Hash
+ request_extension = extract_extension(controller.request)
+ options = options.reverse_merge(:format => request_extension)
+ end
path = controller.url_for(options).split('://').last
normalize!(path)
- add_extension!(path, @extension)
+ if infer_extension
+ @extension = request_extension
+ add_extension!(path, @extension)
+ end
@path = URI.unescape(path)
end
@@ -144,13 +152,22 @@ module ActionController #:nodoc:
end
def add_extension!(path, extension)
- path << ".#{extension}" if extension
+ path << ".#{extension}" if extension and !path.ends_with?(extension)
end
-
- def extract_extension(file_path)
+
+ def extract_extension(request)
# Don't want just what comes after the last '.' to accommodate multi part extensions
# such as tar.gz.
- file_path[/^[^.]+\.(.+)$/, 1]
+ extension = request.path[/^[^.]+\.(.+)$/, 1]
+
+ # If there's no extension in the path, check request.format
+ if extension.nil?
+ extension = request.format.to_sym.to_s
+ if extension=='all'
+ extension = nil
+ end
+ end
+ extension
end
end
end
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index fe4f6b4a7e..7df987d525 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -134,7 +134,7 @@ module ActionController
run_callbacks :prepare_dispatch
Routing::Routes.reload
- ActionView::TemplateFinder.reload! unless ActionView::Base.cache_template_loading
+ ActionController::Base.view_paths.reload!
end
# Cleanup the application by clearing out loaded classes so they can
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index bd69d02ed7..18c2df8b37 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -1,9 +1,10 @@
-require 'stringio'
-require 'uri'
-
+require 'active_support/test_case'
require 'action_controller/dispatcher'
require 'action_controller/test_process'
+require 'stringio'
+require 'uri'
+
module ActionController
module Integration #:nodoc:
# An integration Session instance represents a set of requests and responses
@@ -580,7 +581,7 @@ EOF
# end
# end
# end
- class IntegrationTest < Test::Unit::TestCase
+ class IntegrationTest < ActiveSupport::TestCase
include Integration::Runner
# Work around a bug in test/unit caused by the default test being named
diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb
index b5b59f2d7c..0721f71498 100644
--- a/actionpack/lib/action_controller/layout.rb
+++ b/actionpack/lib/action_controller/layout.rb
@@ -304,7 +304,7 @@ module ActionController #:nodoc:
end
def layout_directory?(layout_name)
- @template.finder.find_template_extension_from_handler(File.join('layouts', layout_name))
+ @template.view_paths.find_template_file_for_path("#{File.join('layouts', layout_name)}.#{@template.template_format}.erb") ? true : false
end
end
end
diff --git a/actionpack/lib/action_controller/performance_test.rb b/actionpack/lib/action_controller/performance_test.rb
new file mode 100644
index 0000000000..85543fffae
--- /dev/null
+++ b/actionpack/lib/action_controller/performance_test.rb
@@ -0,0 +1,16 @@
+require 'action_controller/integration'
+require 'active_support/testing/performance'
+require 'active_support/testing/default'
+
+module ActionController
+ # An integration test that runs a code profiler on your test methods.
+ # Profiling output for combinations of each test method, measurement, and
+ # output format are written to your tmp/performance directory.
+ #
+ # By default, process_time is measured and both flat and graph_html output
+ # formats are written, so you'll have two output files per test method.
+ class PerformanceTest < ActionController::IntegrationTest
+ include ActiveSupport::Testing::Performance
+ include ActiveSupport::Testing::Default
+ end
+end
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index f69c3d6163..742d290ad6 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -66,12 +66,12 @@ module ActionController
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
# If no id is found, prefix with "new_" instead. Examples:
#
- # dom_id(Post.new(:id => 45)) # => "post_45"
+ # dom_id(Post.find(45)) # => "post_45"
# dom_id(Post.new) # => "new_post"
#
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
#
- # dom_id(Post.new(:id => 45), :edit) # => "edit_post_45"
+ # dom_id(Post.find(45), :edit) # => "edit_post_45"
def dom_id(record, prefix = nil)
if record_id = record.id
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb
index 9fb1f9fa39..af2fcaf3ad 100644
--- a/actionpack/lib/action_controller/resources.rb
+++ b/actionpack/lib/action_controller/resources.rb
@@ -72,7 +72,7 @@ module ActionController
end
def conditions
- @conditions = @options[:conditions] || {}
+ @conditions ||= @options[:conditions] || {}
end
def path
@@ -80,9 +80,9 @@ module ActionController
end
def new_path
- new_action = self.options[:path_names][:new] if self.options[:path_names]
+ new_action = self.options[:path_names][:new] if self.options[:path_names]
new_action ||= Base.resources_path_names[:new]
- @new_path ||= "#{path}/#{new_action}"
+ @new_path ||= "#{path}/#{new_action}"
end
def member_path
diff --git a/actionpack/lib/action_controller/templates/rescues/layout.erb b/actionpack/lib/action_controller/templates/rescues/layout.erb
index d38f3e67f9..4a04742e40 100644
--- a/actionpack/lib/action_controller/templates/rescues/layout.erb
+++ b/actionpack/lib/action_controller/templates/rescues/layout.erb
@@ -1,4 +1,4 @@
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Action Controller: Exception caught</title>
<style>
diff --git a/actionpack/lib/action_controller/verification.rb b/actionpack/lib/action_controller/verification.rb
index 9f606e7b7c..35b12a7f13 100644
--- a/actionpack/lib/action_controller/verification.rb
+++ b/actionpack/lib/action_controller/verification.rb
@@ -116,7 +116,7 @@ module ActionController #:nodoc:
end
def apply_redirect_to(redirect_to_option) # :nodoc:
- redirect_to_option.is_a?(Symbol) ? self.send!(redirect_to_option) : redirect_to_option
+ (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.send!(redirect_to_option) : redirect_to_option
end
def apply_remaining_actions(options) # :nodoc:
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 5f4126e4e9..973020a768 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -21,13 +21,9 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-require 'action_view/template_handler'
-require 'action_view/template_handlers/compilable'
-require 'action_view/template_handlers/builder'
-require 'action_view/template_handlers/erb'
-require 'action_view/template_handlers/rjs'
-
-require 'action_view/template_finder'
+require 'action_view/template_handlers'
+require 'action_view/template_file'
+require 'action_view/view_load_paths'
require 'action_view/template'
require 'action_view/partial_template'
require 'action_view/inline_template'
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index eeebf335dc..4f3cc46a14 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -1,17 +1,17 @@
module ActionView #:nodoc:
class ActionViewError < StandardError #:nodoc:
end
-
+
class MissingTemplate < ActionViewError #:nodoc:
end
- # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
- # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
+ # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
+ # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
# If the template file has a <tt>.rjs</tt> extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
- #
+ #
# = ERb
- #
- # You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
+ #
+ # You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
# following loop for names:
#
# <b>Names of all the people</b>
@@ -51,7 +51,7 @@ module ActionView #:nodoc:
# <title><%= @page_title %></title>
#
# == Passing local variables to sub templates
- #
+ #
# You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
#
# <%= render "shared/header", { :headline => "Welcome", :person => person } %>
@@ -77,8 +77,8 @@ module ActionView #:nodoc:
#
# == Builder
#
- # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An XmlMarkup object
- # named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
+ # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An XmlMarkup object
+ # named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
#
# Here are some basic examples:
#
@@ -87,7 +87,7 @@ module ActionView #:nodoc:
# xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
# xml.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
# # NOTE: order of attributes is not specified.
- #
+ #
# Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
#
# xml.div {
@@ -111,7 +111,7 @@ module ActionView #:nodoc:
# xml.description "Basecamp: Recent items"
# xml.language "en-us"
# xml.ttl "40"
- #
+ #
# for item in @recent_items
# xml.item do
# xml.title(item_title(item))
@@ -119,7 +119,7 @@ module ActionView #:nodoc:
# xml.pubDate(item_pubDate(item))
# xml.guid(@person.firm.account.url + @recent_items.url(item))
# xml.link(@person.firm.account.url + @recent_items.url(item))
- #
+ #
# xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
# end
# end
@@ -130,12 +130,12 @@ module ActionView #:nodoc:
#
# == JavaScriptGenerator
#
- # JavaScriptGenerator templates end in <tt>.rjs</tt>. Unlike conventional templates which are used to
- # render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to
- # modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax
+ # JavaScriptGenerator templates end in <tt>.rjs</tt>. Unlike conventional templates which are used to
+ # render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to
+ # modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax
# and make updates to the page where the request originated from.
- #
- # An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, which is implicitly wrapped in an ActionView::Helpers::PrototypeHelper#update_page block.
+ #
+ # An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, which is implicitly wrapped in an ActionView::Helpers::PrototypeHelper#update_page block.
#
# When an <tt>.rjs</tt> action is called with +link_to_remote+, the generated JavaScript is automatically evaluated. Example:
#
@@ -145,15 +145,14 @@ module ActionView #:nodoc:
#
# page.replace_html 'sidebar', :partial => 'sidebar'
# page.remove "person-#{@person.id}"
- # page.visual_effect :highlight, 'user-list'
+ # page.visual_effect :highlight, 'user-list'
#
# This refreshes the sidebar, removes a person element and highlights the user list.
- #
+ #
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
class Base
include ERB::Util
- attr_reader :finder
attr_accessor :base_path, :assigns, :template_extension, :first_render
attr_accessor :controller
@@ -170,22 +169,22 @@ module ActionView #:nodoc:
# Specify whether file modification times should be checked to see if a template needs recompilation
@@cache_template_loading = false
cattr_accessor :cache_template_loading
-
+
def self.cache_template_extensions=(*args)
ActiveSupport::Deprecation.warn("config.action_view.cache_template_extensions option has been deprecated and has no affect. " <<
"Please remove it from your config files.", caller)
end
# Specify whether RJS responses should be wrapped in a try/catch block
- # that alert()s the caught exception (and then re-raises it).
+ # that alert()s the caught exception (and then re-raises it).
@@debug_rjs = false
cattr_accessor :debug_rjs
attr_internal :request
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
- :flash, :logger, :action_name, :to => :controller
-
+ :flash, :logger, :action_name, :controller_name, :to => :controller
+
module CompiledTemplates #:nodoc:
# holds compiled template code
end
@@ -221,11 +220,17 @@ module ActionView #:nodoc:
@assigns = assigns_for_first_render
@assigns_added = nil
@controller = controller
- @finder = TemplateFinder.new(self, view_paths)
+ self.view_paths = view_paths
end
- # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,
- # it's relative to the view_paths array, otherwise it's absolute. The hash in <tt>local_assigns</tt>
+ attr_reader :view_paths
+
+ def view_paths=(paths)
+ @view_paths = ViewLoadPaths.new(Array(paths))
+ end
+
+ # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,
+ # it's relative to the view_paths array, otherwise it's absolute. The hash in <tt>local_assigns</tt>
# is made available as local variables.
def render_file(template_path, use_full_path = true, local_assigns = {}) #:nodoc:
if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/")
@@ -240,11 +245,11 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
render :partial => 'signup' # no mailer_name necessary
END_ERROR
end
-
+
Template.new(self, template_path, use_full_path, local_assigns).render_template
end
-
- # Renders the template present at <tt>template_path</tt> (relative to the view_paths array).
+
+ # Renders the template present at <tt>template_path</tt> (relative to the view_paths array).
# The hash in <tt>local_assigns</tt> is made available as local variables.
def render(options = {}, local_assigns = {}, &block) #:nodoc:
if options.is_a?(String)
@@ -257,7 +262,7 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
if partial_layout = options.delete(:layout)
if block_given?
- wrap_content_for_layout capture(&block) do
+ wrap_content_for_layout capture(&block) do
concat(render(options.merge(:partial => partial_layout)))
end
else
@@ -314,6 +319,10 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
end
end
+ def file_exists?(template_path)
+ view_paths.template_exists?(template_file_from_name(template_path))
+ end
+
private
def wrap_content_for_layout(content)
original_content_for_layout, @content_for_layout = @content_for_layout, content
@@ -334,11 +343,43 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
def assign_variables_from_controller
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
-
+
def execute(template)
send(template.method, template.locals) do |*names|
instance_variable_get "@content_for_#{names.first || 'layout'}"
- end
+ end
+ end
+
+ def template_file_from_name(template_name)
+ template_name = TemplateFile.from_path(template_name)
+ pick_template_extension(template_name) unless template_name.extension
+ end
+
+ # Gets the extension for an existing template with the given template_path.
+ # Returns the format with the extension if that template exists.
+ #
+ # pick_template_extension('users/show')
+ # # => 'html.erb'
+ #
+ # pick_template_extension('users/legacy')
+ # # => "rhtml"
+ #
+ def pick_template_extension(file)
+ if f = self.view_paths.find_template_file_for_path(file.dup_with_extension(template_format)) || file_from_first_render(file)
+ f
+ elsif template_format == :js && f = self.view_paths.find_template_file_for_path(file.dup_with_extension(:html))
+ @template_format = :html
+ f
+ else
+ nil
+ end
+ end
+
+ # Determine the template extension from the <tt>@first_render</tt> filename
+ def file_from_first_render(file)
+ if extension = File.basename(@first_render.to_s)[/^[^.]+\.(.+)$/, 1]
+ file.dup_with_extension(extension)
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 25e62f78fb..9cd9d3d06a 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -31,7 +31,7 @@ module ActionView
# </body></html>
#
def capture(*args, &block)
- if @output_buffer
+ if output_buffer
with_output_buffer { block.call(*args) }
else
block.call(*args)
@@ -121,10 +121,11 @@ module ActionView
private
def with_output_buffer(buf = '')
- @output_buffer, old_buffer = buf, @output_buffer
+ self.output_buffer, old_buffer = buf, output_buffer
yield
+ output_buffer
ensure
- @output_buffer = old_buffer
+ self.output_buffer = old_buffer
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 90bd99a861..b3f8e63c1b 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -304,7 +304,7 @@ module ActionView
#
# NOTE: Only the option tags are returned, you have to wrap this call in
# a regular HTML select tag.
- def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ActiveSupport::TimeZone)
+ def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
zone_options = ""
zones = model.all
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 85b205c264..7404a251e4 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -202,11 +202,6 @@ module ActionView
end
js_option
end
-
- private
- def block_is_within_action_view?(block)
- !@output_buffer.nil?
- end
end
JavascriptHelper = JavaScriptHelper unless const_defined? :JavascriptHelper
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index a8a5987b1f..e1abec1847 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -122,10 +122,6 @@ module ActionView
" #{attrs.sort * ' '}" unless attrs.empty?
end
end
-
- def block_is_within_action_view?(block)
- !@output_buffer.nil?
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 3077c8f970..a1a91f6b3d 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -29,8 +29,9 @@ module ActionView
if unused_binding
ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.")
end
- if @output_buffer && string
- @output_buffer << string
+
+ if output_buffer && string
+ output_buffer << string
else
string
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 4b12adf225..baecd304cd 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -90,6 +90,13 @@ module ActionView
# link will be used in place of a referrer if none exists. If nil is passed as
# a name, the link itself will become the name.
#
+ # ==== Signatures
+ #
+ # link_to(name, options = {}, html_options = nil)
+ # link_to(options = {}, html_options = nil) do
+ # # name
+ # end
+ #
# ==== Options
# * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
# prompt with the question specified. If the user accepts, the link is
@@ -147,6 +154,13 @@ module ActionView
# link_to "Profiles", :controller => "profiles"
# # => <a href="/profiles">Profiles</a>
#
+ # You can use a block as well if your link target is hard to fit into the name parameter. ERb example:
+ #
+ # <% link_to(@profile) do %>
+ # <strong><%= @profile.name %></strong> -- <span>Check it out!!</span>
+ # <% end %>
+ # # => <a href="/profiles/1"><strong>David</strong> -- <span>Check it out!!</span></a>
+ #
# Classes and ids for CSS are easy to produce:
#
# link_to "Articles", articles_path, :id => "news", :class => "article"
@@ -189,27 +203,37 @@ module ActionView
# f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;
# var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
# m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;">Delete Image</a>
- def link_to(name, options = {}, html_options = nil)
- url = case options
- when String
- options
- when :back
- @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
+ def link_to(*args, &block)
+ if block_given?
+ options = args.first || {}
+ html_options = args.second
+ concat(link_to(capture(&block), options, html_options))
+ else
+ name = args.first
+ options = args.second || {}
+ html_options = args.third
+
+ url = case options
+ when String
+ options
+ when :back
+ @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
+ else
+ self.url_for(options)
+ end
+
+ if html_options
+ html_options = html_options.stringify_keys
+ href = html_options['href']
+ convert_options_to_javascript!(html_options, url)
+ tag_options = tag_options(html_options)
else
- self.url_for(options)
+ tag_options = nil
end
-
- if html_options
- html_options = html_options.stringify_keys
- href = html_options['href']
- convert_options_to_javascript!(html_options, url)
- tag_options = tag_options(html_options)
- else
- tag_options = nil
+
+ href_attr = "href=\"#{url}\"" unless href
+ "<a #{href_attr}#{tag_options}>#{name || url}</a>"
end
-
- href_attr = "href=\"#{url}\"" unless href
- "<a #{href_attr}#{tag_options}>#{name || url}</a>"
end
# Generates a form containing a single button that submits to the URL created
diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/inline_template.rb
index 87c012d181..fd0ad48302 100644
--- a/actionpack/lib/action_view/inline_template.rb
+++ b/actionpack/lib/action_view/inline_template.rb
@@ -1,20 +1,17 @@
module ActionView #:nodoc:
class InlineTemplate < Template #:nodoc:
-
def initialize(view, source, locals = {}, type = nil)
@view = view
- @finder = @view.finder
-
+
@source = source
@extension = type
@locals = locals || {}
-
+
@handler = self.class.handler_class_for_extension(@extension).new(@view)
end
-
+
def method_key
@source
end
-
end
end
diff --git a/actionpack/lib/action_view/partial_template.rb b/actionpack/lib/action_view/partial_template.rb
index 0b374db888..0cf996ca04 100644
--- a/actionpack/lib/action_view/partial_template.rb
+++ b/actionpack/lib/action_view/partial_template.rb
@@ -1,70 +1,70 @@
module ActionView #:nodoc:
class PartialTemplate < Template #:nodoc:
-
attr_reader :variable_name, :object
-
+
def initialize(view, partial_path, object = nil, locals = {})
- @path, @variable_name = extract_partial_name_and_path(view, partial_path)
+ @view_controller = view.controller if view.respond_to?(:controller)
+ set_path_and_variable_name!(partial_path)
super(view, @path, true, locals)
add_object_to_local_assigns!(object)
# This is needed here in order to compile template with knowledge of 'counter'
- initialize_counter
-
+ initialize_counter!
+
# Prepare early. This is a performance optimization for partial collections
prepare!
end
-
+
def render
- ActionController::Base.benchmark("Rendered #{@path}", Logger::DEBUG, false) do
+ ActionController::Base.benchmark("Rendered #{@path.path_without_format_and_extension}", Logger::DEBUG, false) do
@handler.render(self)
end
end
-
+
def render_member(object)
@locals[:object] = @locals[@variable_name] = object
-
+
template = render_template
@locals[@counter_name] += 1
@locals.delete(@variable_name)
@locals.delete(:object)
-
+
template
end
-
+
def counter=(num)
@locals[@counter_name] = num
end
private
+ def add_object_to_local_assigns!(object)
+ @locals[:object] ||=
+ @locals[@variable_name] ||=
+ if object.is_a?(ActionView::Base::ObjectWrapper)
+ object.value
+ else
+ object
+ end || @view_controller.instance_variable_get("@#{variable_name}")
+ end
- def add_object_to_local_assigns!(object)
- @locals[:object] ||=
- @locals[@variable_name] ||=
- if object.is_a?(ActionView::Base::ObjectWrapper)
- object.value
- else
- object
- end || @view.controller.instance_variable_get("@#{variable_name}")
- end
-
- def extract_partial_name_and_path(view, partial_path)
- path, partial_name = partial_pieces(view, partial_path)
- [File.join(path, "_#{partial_name}"), partial_name.split('/').last.split('.').first.to_sym]
- end
-
- def partial_pieces(view, partial_path)
- if partial_path.include?('/')
- return File.dirname(partial_path), File.basename(partial_path)
- else
- return view.controller.class.controller_path, partial_path
+ def set_path_and_variable_name!(partial_path)
+ if partial_path.include?('/')
+ @variable_name = File.basename(partial_path)
+ @path = "#{File.dirname(partial_path)}/_#{@variable_name}"
+ elsif @view_controller
+ @variable_name = partial_path
+ @path = "#{@view_controller.class.controller_path}/_#{@variable_name}"
+ else
+ @variable_name = partial_path
+ @path = "_#{@variable_name}"
+ end
+
+ @variable_name = @variable_name.sub(/\..*$/, '').to_sym
+ end
+
+ def initialize_counter!
+ @counter_name ||= "#{@variable_name}_counter".to_sym
+ @locals[@counter_name] = 0
end
- end
-
- def initialize_counter
- @counter_name ||= "#{@variable_name}_counter".to_sym
- @locals[@counter_name] = 0
- end
-
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 369526188f..4c3f252c10 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,19 +1,20 @@
module ActionView #:nodoc:
class Template #:nodoc:
+ extend TemplateHandlers
attr_accessor :locals
- attr_reader :handler, :path, :extension, :filename, :path_without_extension, :method
+ attr_reader :handler, :path, :extension, :filename, :method
def initialize(view, path, use_full_path, locals = {})
@view = view
- @finder = @view.finder
+ @paths = view.view_paths
- # Clear the forward slash at the beginning if exists
- @path = use_full_path ? path.sub(/^\//, '') : path
- @view.first_render ||= @path
+ @original_path = path
+ @path = TemplateFile.from_path(path, !use_full_path)
+ @view.first_render ||= @path.to_s
@source = nil # Don't read the source until we know that it is required
set_extension_and_file_name(use_full_path)
-
+
@locals = locals || {}
@handler = self.class.handler_class_for_extension(@extension).new(@view)
end
@@ -29,12 +30,16 @@ module ActionView #:nodoc:
raise TemplateError.new(self, @view.assigns, e)
end
end
-
+
def render
prepare!
@handler.render(self)
end
+ def path_without_extension
+ @path.path_without_extension
+ end
+
def source
@source ||= File.read(self.filename)
end
@@ -44,13 +49,13 @@ module ActionView #:nodoc:
end
def base_path_for_exception
- @finder.find_base_path_for("#{@path_without_extension}.#{@extension}") || @finder.view_paths.first
+ (@paths.find_load_path_for_path(@path) || @paths.first).to_s
end
-
+
def prepare!
@view.send :evaluate_assigns
@view.current_render_extension = @extension
-
+
if @handler.compilable?
@handler.compile_template(self) # compile the given template, if necessary
@method = @view.method_names[method_key] # Set the method name for this template and run it
@@ -58,70 +63,32 @@ module ActionView #:nodoc:
end
private
-
- def set_extension_and_file_name(use_full_path)
- @path_without_extension, @extension = @finder.path_and_extension(@path)
- if use_full_path
- if @extension
- @filename = @finder.pick_template(@path_without_extension, @extension)
+ def set_extension_and_file_name(use_full_path)
+ @extension = @path.extension
+
+ if use_full_path
+ unless @extension
+ @path = @view.send(:template_file_from_name, @path)
+ raise_missing_template_exception unless @path
+ @extension = @path.extension
+ end
+
+ if @path = @paths.find_template_file_for_path(path)
+ @filename = @path.full_path
+ @extension = @path.extension
+ end
else
- @extension = @finder.pick_template_extension(@path).to_s
- raise_missing_template_exception unless @extension
-
- @filename = @finder.pick_template(@path, @extension)
- @extension = @extension.gsub(/^.+\./, '') # strip off any formats
+ @filename = @path.full_path
end
- else
- @filename = @path
- end
-
- raise_missing_template_exception if @filename.blank?
- end
-
- def raise_missing_template_exception
- full_template_path = @path.include?('.') ? @path : "#{@path}.#{@view.template_format}.erb"
- display_paths = @finder.view_paths.join(':')
- template_type = (@path =~ /layouts/i) ? 'layout' : 'template'
- raise(MissingTemplate, "Missing #{template_type} #{full_template_path} in view path #{display_paths}")
- end
-
- # Template Handlers
-
- @@template_handlers = HashWithIndifferentAccess.new
- @@default_template_handlers = nil
-
- # Register a class that knows how to handle template files with the given
- # extension. This can be used to implement new template types.
- # The constructor for the class must take the ActiveView::Base instance
- # as a parameter, and the class must implement a +render+ method that
- # takes the contents of the template to render as well as the Hash of
- # local assigns available to the template. The +render+ method ought to
- # return the rendered template as a string.
- def self.register_template_handler(extension, klass)
- @@template_handlers[extension.to_sym] = klass
- TemplateFinder.update_extension_cache_for(extension.to_s)
- end
- def self.template_handler_extensions
- @@template_handlers.keys.map(&:to_s).sort
- end
-
- def self.register_default_template_handler(extension, klass)
- register_template_handler(extension, klass)
- @@default_template_handlers = klass
- end
-
- def self.handler_class_for_extension(extension)
- (extension && @@template_handlers[extension.to_sym]) || @@default_template_handlers
- end
-
- register_default_template_handler :erb, TemplateHandlers::ERB
- register_template_handler :rjs, TemplateHandlers::RJS
- register_template_handler :builder, TemplateHandlers::Builder
+ raise_missing_template_exception if @filename.blank?
+ end
- # TODO: Depreciate old template extensions
- register_template_handler :rhtml, TemplateHandlers::ERB
- register_template_handler :rxml, TemplateHandlers::Builder
-
+ def raise_missing_template_exception
+ full_template_path = @original_path.include?('.') ? @original_path : "#{@original_path}.#{@view.template_format}.erb"
+ display_paths = @paths.join(':')
+ template_type = (@original_path =~ /layouts/i) ? 'layout' : 'template'
+ raise(MissingTemplate, "Missing #{template_type} #{full_template_path} in view path #{display_paths}")
+ end
end
end
diff --git a/actionpack/lib/action_view/template_file.rb b/actionpack/lib/action_view/template_file.rb
new file mode 100644
index 0000000000..dd66482b3c
--- /dev/null
+++ b/actionpack/lib/action_view/template_file.rb
@@ -0,0 +1,88 @@
+module ActionView #:nodoc:
+ # TemplateFile abstracts the pattern of querying a file path for its
+ # path with or without its extension. The path is only the partial path
+ # from the load path root e.g. "hello/index.html.erb" not
+ # "app/views/hello/index.html.erb"
+ class TemplateFile
+ def self.from_path(path, use_full_path = false)
+ path.is_a?(self) ? path : new(path, use_full_path)
+ end
+
+ def self.from_full_path(load_path, full_path)
+ file = new(full_path.split(load_path).last)
+ file.load_path = load_path
+ file.freeze
+ end
+
+ attr_accessor :load_path, :base_path, :name, :format, :extension
+ delegate :to_s, :inspect, :to => :path
+
+ def initialize(path, use_full_path = false)
+ path = path.dup
+
+ # Clear the forward slash in the beginning unless using full path
+ trim_forward_slash!(path) unless use_full_path
+
+ @base_path, @name, @format, @extension = split(path)
+ end
+
+ def freeze
+ @load_path.freeze
+ @base_path.freeze
+ @name.freeze
+ @format.freeze
+ @extension.freeze
+ super
+ end
+
+ def format_and_extension
+ extensions = [format, extension].compact.join(".")
+ extensions.blank? ? nil : extensions
+ end
+
+ def full_path
+ if load_path
+ "#{load_path}/#{path}"
+ else
+ path
+ end
+ end
+
+ def path
+ base_path.to_s + [name, format, extension].compact.join(".")
+ end
+
+ def path_without_extension
+ base_path.to_s + [name, format].compact.join(".")
+ end
+
+ def path_without_format_and_extension
+ "#{base_path}#{name}"
+ end
+
+ def dup_with_extension(extension)
+ file = dup
+ file.extension = extension ? extension.to_s : nil
+ file
+ end
+
+ private
+ def trim_forward_slash!(path)
+ path.sub!(/^\//, '')
+ end
+
+ # Returns file split into an array
+ # [base_path, name, format, extension]
+ def split(file)
+ if m = file.match(/^(.*\/)?(\w+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/)
+ if m[5] # Mulipart formats
+ [m[1], m[2], "#{m[3]}.#{m[4]}", m[5]]
+ elsif m[4] # Single format
+ [m[1], m[2], m[3], m[4]]
+ else # No format
+ [m[1], m[2], nil, m[3]]
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template_finder.rb b/actionpack/lib/action_view/template_finder.rb
deleted file mode 100644
index 83b7e27c09..0000000000
--- a/actionpack/lib/action_view/template_finder.rb
+++ /dev/null
@@ -1,177 +0,0 @@
-module ActionView #:nodoc:
- class TemplateFinder #:nodoc:
-
- class InvalidViewPath < StandardError #:nodoc:
- attr_reader :unprocessed_path
- def initialize(path)
- @unprocessed_path = path
- super("Unprocessed view path found: #{@unprocessed_path.inspect}. Set your view paths with #append_view_path, #prepend_view_path, or #view_paths=.")
- end
- end
-
- cattr_reader :processed_view_paths
- @@processed_view_paths = Hash.new {|hash, key| hash[key] = []}
-
- cattr_reader :file_extension_cache
- @@file_extension_cache = Hash.new {|hash, key|
- hash[key] = Hash.new {|hash, key| hash[key] = []}
- }
-
- class << self #:nodoc:
-
- # This method is not thread safe. Mutex should be used whenever this is accessed from an instance method
- def process_view_paths(*view_paths)
- view_paths.flatten.compact.each do |dir|
- next if @@processed_view_paths.has_key?(dir)
- @@processed_view_paths[dir] = []
-
- #
- # Dir.glob("#{dir}/**/*/**") reads all the directories in view path and templates inside those directories
- # Dir.glob("#{dir}/**") reads templates residing at top level of view path
- #
- (Dir.glob("#{dir}/**/*/**") | Dir.glob("#{dir}/**")).each do |file|
- unless File.directory?(file)
- @@processed_view_paths[dir] << file.split(dir).last.sub(/^\//, '')
-
- # Build extension cache
- extension = file.split(".").last
- if template_handler_extensions.include?(extension)
- key = file.split(dir).last.sub(/^\//, '').sub(/\.(\w+)$/, '')
- @@file_extension_cache[dir][key] << extension
- end
- end
- end
- end
- end
-
- def update_extension_cache_for(extension)
- @@processed_view_paths.keys.each do |dir|
- Dir.glob("#{dir}/**/*.#{extension}").each do |file|
- key = file.split(dir).last.sub(/^\//, '').sub(/\.(\w+)$/, '')
- @@file_extension_cache[dir][key] << extension
- end
- end
- end
-
- def template_handler_extensions
- ActionView::Template.template_handler_extensions
- end
-
- def reload!
- view_paths = @@processed_view_paths.keys
-
- @@processed_view_paths = Hash.new {|hash, key| hash[key] = []}
- @@file_extension_cache = Hash.new {|hash, key|
- hash[key] = Hash.new {|hash, key| hash[key] = []}
- }
-
- process_view_paths(view_paths)
- end
- end
-
- attr_accessor :view_paths
-
- def initialize(*args)
- @template = args.shift
-
- @view_paths = args.flatten
- @view_paths = @view_paths.respond_to?(:find) ? @view_paths.dup : [*@view_paths].compact
- check_view_paths(@view_paths)
- end
-
- def prepend_view_path(path)
- @view_paths.unshift(*path)
-
- self.class.process_view_paths(path)
- end
-
- def append_view_path(path)
- @view_paths.push(*path)
-
- self.class.process_view_paths(path)
- end
-
- def view_paths=(path)
- @view_paths = path
- self.class.process_view_paths(path)
- end
-
- def pick_template(template_path, extension)
- file_name = "#{template_path}.#{extension}"
- base_path = find_base_path_for(file_name)
- base_path.blank? ? false : "#{base_path}/#{file_name}"
- end
- alias_method :template_exists?, :pick_template
-
- def file_exists?(template_path)
- # Clear the forward slash in the beginning if exists
- template_path = template_path.sub(/^\//, '')
-
- template_file_name, template_file_extension = path_and_extension(template_path)
-
- if template_file_extension
- template_exists?(template_file_name, template_file_extension)
- else
- template_exists?(template_file_name, pick_template_extension(template_path))
- end
- end
-
- def find_base_path_for(template_file_name)
- @view_paths.find { |path| @@processed_view_paths[path].include?(template_file_name) }
- end
-
- # Returns the view path that the full path resides in.
- def extract_base_path_from(full_path)
- @view_paths.find { |p| full_path[0..p.size - 1] == p }
- end
-
- # Gets the extension for an existing template with the given template_path.
- # Returns the format with the extension if that template exists.
- #
- # pick_template_extension('users/show')
- # # => 'html.erb'
- #
- # pick_template_extension('users/legacy')
- # # => "rhtml"
- #
- def pick_template_extension(template_path)
- if extension = find_template_extension_from_handler(template_path, @template.template_format) || find_template_extension_from_first_render
- extension
- elsif @template.template_format == :js && extension = find_template_extension_from_handler(template_path, :html)
- @template.template_format = :html
- extension
- end
- end
-
- def find_template_extension_from_handler(template_path, template_format = @template.template_format)
- formatted_template_path = "#{template_path}.#{template_format}"
-
- view_paths.each do |path|
- if (extensions = @@file_extension_cache[path][formatted_template_path]).any?
- return "#{template_format}.#{extensions.first}"
- elsif (extensions = @@file_extension_cache[path][template_path]).any?
- return extensions.first.to_s
- end
- end
- nil
- end
-
- # Splits the path and extension from the given template_path and returns as an array.
- def path_and_extension(template_path)
- template_path_without_extension = template_path.sub(/\.(\w+)$/, '')
- [ template_path_without_extension, $1 ]
- end
-
- # Determine the template extension from the <tt>@first_render</tt> filename
- def find_template_extension_from_first_render
- File.basename(@template.first_render.to_s)[/^[^.]+\.(.+)$/, 1]
- end
-
- private
- def check_view_paths(view_paths)
- view_paths.each do |path|
- raise InvalidViewPath.new(path) unless @@processed_view_paths.has_key?(path)
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/template_handler.rb b/actionpack/lib/action_view/template_handler.rb
index ec407e3fb3..39e578e586 100644
--- a/actionpack/lib/action_view/template_handler.rb
+++ b/actionpack/lib/action_view/template_handler.rb
@@ -1,6 +1,5 @@
module ActionView
class TemplateHandler
-
def self.line_offset
0
end
diff --git a/actionpack/lib/action_view/template_handlers.rb b/actionpack/lib/action_view/template_handlers.rb
new file mode 100644
index 0000000000..1471e99e01
--- /dev/null
+++ b/actionpack/lib/action_view/template_handlers.rb
@@ -0,0 +1,46 @@
+require 'action_view/template_handler'
+require 'action_view/template_handlers/compilable'
+require 'action_view/template_handlers/builder'
+require 'action_view/template_handlers/erb'
+require 'action_view/template_handlers/rjs'
+
+module ActionView #:nodoc:
+ module TemplateHandlers #:nodoc:
+ def self.extended(base)
+ base.register_default_template_handler :erb, TemplateHandlers::ERB
+ base.register_template_handler :rjs, TemplateHandlers::RJS
+ base.register_template_handler :builder, TemplateHandlers::Builder
+
+ # TODO: Depreciate old template extensions
+ base.register_template_handler :rhtml, TemplateHandlers::ERB
+ base.register_template_handler :rxml, TemplateHandlers::Builder
+ end
+
+ @@template_handlers = {}
+ @@default_template_handlers = nil
+
+ # Register a class that knows how to handle template files with the given
+ # extension. This can be used to implement new template types.
+ # The constructor for the class must take the ActiveView::Base instance
+ # as a parameter, and the class must implement a +render+ method that
+ # takes the contents of the template to render as well as the Hash of
+ # local assigns available to the template. The +render+ method ought to
+ # return the rendered template as a string.
+ def register_template_handler(extension, klass)
+ @@template_handlers[extension.to_sym] = klass
+ end
+
+ def template_handler_extensions
+ @@template_handlers.keys.map(&:to_s).sort
+ end
+
+ def register_default_template_handler(extension, klass)
+ register_template_handler(extension, klass)
+ @@default_template_handlers = klass
+ end
+
+ def handler_class_for_extension(extension)
+ (extension && @@template_handlers[extension.to_sym]) || @@default_template_handlers
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template_handlers/compilable.rb b/actionpack/lib/action_view/template_handlers/compilable.rb
index 28c72172a0..1aef81ba1a 100644
--- a/actionpack/lib/action_view/template_handlers/compilable.rb
+++ b/actionpack/lib/action_view/template_handlers/compilable.rb
@@ -106,7 +106,7 @@ module ActionView
locals_code << "#{key} = local_assigns[:#{key}]\n"
end
- "def #{render_symbol}(local_assigns)\nold_output_buffer = @output_buffer;#{locals_code}#{body}\nensure\n@output_buffer = old_output_buffer\nend"
+ "def #{render_symbol}(local_assigns)\nold_output_buffer = output_buffer;#{locals_code}#{body}\nensure\nself.output_buffer = old_output_buffer\nend"
end
# Return true if the given template was compiled for a superset of the keys in local_assigns
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 16fedd9732..1a3c93c283 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -37,6 +37,8 @@ module ActionView
if helper_class && !self.class.ancestors.include?(helper_class)
self.class.send(:include, helper_class)
end
+
+ self.output_buffer = ''
end
class TestController < ActionController::Base
@@ -48,6 +50,9 @@ module ActionView
end
end
+ protected
+ attr_accessor :output_buffer
+
private
def method_missing(selector, *args)
controller = TestController.new
diff --git a/actionpack/lib/action_view/view_load_paths.rb b/actionpack/lib/action_view/view_load_paths.rb
new file mode 100644
index 0000000000..e873d96aa0
--- /dev/null
+++ b/actionpack/lib/action_view/view_load_paths.rb
@@ -0,0 +1,103 @@
+module ActionView #:nodoc:
+ class ViewLoadPaths < Array #:nodoc:
+ def self.type_cast(obj)
+ obj.is_a?(String) ? LoadPath.new(obj) : obj
+ end
+
+ class LoadPath #:nodoc:
+ attr_reader :path, :paths
+ delegate :to_s, :inspect, :to => :path
+
+ def initialize(path)
+ @path = path.freeze
+ reload!
+ end
+
+ def eql?(view_path)
+ view_path.is_a?(ViewPath) && @path == view_path.path
+ end
+
+ def hash
+ @path.hash
+ end
+
+ # Rebuild load path directory cache
+ def reload!
+ @paths = {}
+
+ files.each do |file|
+ @paths[file.path] = file
+ @paths[file.path_without_extension] ||= file
+ end
+
+ @paths.freeze
+ end
+
+ # Tries to find the extension for the template name.
+ # If it does not it exist, tries again without the format extension
+ # find_template_file_for_partial_path('users/show') => 'html.erb'
+ # find_template_file_for_partial_path('users/legacy') => 'rhtml'
+ def find_template_file_for_partial_path(file)
+ @paths[file.path] || @paths[file.path_without_extension] || @paths[file.path_without_format_and_extension]
+ end
+
+ private
+ # Get all the files and directories in the path
+ def files_in_path
+ Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")
+ end
+
+ # Create an array of all the files within the path
+ def files
+ files_in_path.map do |file|
+ TemplateFile.from_full_path(@path, file) unless File.directory?(file)
+ end.compact
+ end
+ end
+
+ def initialize(*args)
+ super(*args).map! { |obj| self.class.type_cast(obj) }
+ end
+
+ def reload!
+ each { |path| path.reload! }
+ end
+
+ def <<(obj)
+ super(self.class.type_cast(obj))
+ end
+
+ def push(*objs)
+ delete_paths!(objs)
+ super(*objs.map { |obj| self.class.type_cast(obj) })
+ end
+
+ def unshift(*objs)
+ delete_paths!(objs)
+ super(*objs.map { |obj| self.class.type_cast(obj) })
+ end
+
+ def template_exists?(file)
+ find_load_path_for_path(file) ? true : false
+ end
+
+ def find_load_path_for_path(file)
+ find { |path| path.paths[file.to_s] }
+ end
+
+ def find_template_file_for_path(file)
+ file = TemplateFile.from_path(file)
+ each do |path|
+ if f = path.find_template_file_for_partial_path(file)
+ return f
+ end
+ end
+ nil
+ end
+
+ private
+ def delete_paths!(paths)
+ paths.each { |p1| delete_if { |p2| p1.to_s == p2.to_s } }
+ end
+ end
+end