aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r--actionpack/lib/action_view/base.rb118
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb56
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb3
-rwxr-xr-xactionpack/lib/action_view/helpers/date_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb8
-rw-r--r--actionpack/lib/action_view/inline_template.rb17
-rw-r--r--actionpack/lib/action_view/partial_template.rb69
-rw-r--r--actionpack/lib/action_view/partials.rb58
-rw-r--r--actionpack/lib/action_view/paths.rb96
-rw-r--r--actionpack/lib/action_view/renderable.rb79
-rw-r--r--actionpack/lib/action_view/renderable_partial.rb19
-rw-r--r--actionpack/lib/action_view/renderer.rb29
-rw-r--r--actionpack/lib/action_view/template.rb135
-rw-r--r--actionpack/lib/action_view/template_error.rb4
-rw-r--r--actionpack/lib/action_view/template_file.rb88
-rw-r--r--actionpack/lib/action_view/template_handler.rb10
-rw-r--r--actionpack/lib/action_view/template_handlers/builder.rb13
-rw-r--r--actionpack/lib/action_view/template_handlers/compilable.rb118
-rw-r--r--actionpack/lib/action_view/template_handlers/rjs.rb8
-rw-r--r--actionpack/lib/action_view/view_load_paths.rb103
23 files changed, 461 insertions, 590 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 64e0ab575f..fb82443060 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -3,6 +3,12 @@ module ActionView #:nodoc:
end
class MissingTemplate < ActionViewError #:nodoc:
+ def initialize(paths, path, template_format = nil)
+ full_template_path = path.include?('.') ? path : "#{path}.erb"
+ display_paths = paths.join(':')
+ template_type = (path =~ /layouts/i) ? 'layout' : 'template'
+ super("Missing #{template_type} #{full_template_path} in view path #{display_paths}")
+ end
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
@@ -151,7 +157,6 @@ module ActionView #:nodoc:
#
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
class Base
- extend TemplateHandlers
include ERB::Util
attr_accessor :base_path, :assigns, :template_extension, :first_render
@@ -166,7 +171,7 @@ module ActionView #:nodoc:
delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB'
end
- # Specify whether file modification times should be checked to see if a template needs recompilation
+ # Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
@@cache_template_loading = false
cattr_accessor :cache_template_loading
@@ -180,6 +185,10 @@ module ActionView #:nodoc:
@@debug_rjs = false
cattr_accessor :debug_rjs
+ # A warning will be displayed whenever an action results in a cache miss on your view paths.
+ @@warn_cache_misses = false
+ cattr_accessor :warn_cache_misses
+
attr_internal :request
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
@@ -190,12 +199,6 @@ module ActionView #:nodoc:
end
include CompiledTemplates
- # Maps inline templates to their method names
- cattr_accessor :method_names
- @@method_names = {}
- # Map method names to the names passed in local assigns so far
- @@template_args = {}
-
# Cache public asset paths
cattr_reader :computed_public_paths
@@computed_public_paths = {}
@@ -213,6 +216,10 @@ module ActionView #:nodoc:
return helpers
end
+ def self.process_view_paths(value)
+ ActionView::PathSet.new(Array(value))
+ end
+
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
@assigns = assigns_for_first_render
@assigns_added = nil
@@ -223,12 +230,14 @@ module ActionView #:nodoc:
attr_reader :view_paths
def view_paths=(paths)
- @view_paths = ViewLoadPaths.new(Array(paths))
+ @view_paths = self.class.process_view_paths(paths)
end
# 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:
+ local_assigns ||= {}
+
if options.is_a?(String)
render_file(options, nil, local_assigns)
elsif options == :update
@@ -263,13 +272,9 @@ module ActionView #:nodoc:
template_path.split('/').last[0,1] != '_'
end
- # Returns a symbolized version of the <tt>:format</tt> parameter of the request,
- # or <tt>:html</tt> by default.
- #
- # EXCEPTION: If the <tt>:format</tt> parameter is not set, the Accept header will be examined for
- # whether it contains the JavaScript mime type as its first priority. If that's the case,
- # it will be used. This ensures that Ajax applications can use the same URL to support both
- # JavaScript and non-JavaScript users.
+ # The format to be used when choosing between multiple templates with
+ # the same name but differing formats. See +Request#template_format+
+ # for more details.
def template_format
return @template_format if @template_format
@@ -281,21 +286,50 @@ module ActionView #:nodoc:
end
def file_exists?(template_path)
- view_paths.template_exists?(template_file_from_name(template_path))
+ pick_template(template_path) ? true : false
+ rescue MissingTemplate
+ false
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('users/show')
+ # # => 'users/show.html.erb'
#
- # pick_template_extension('users/legacy')
- # # => "rhtml"
+ # pick_template('users/legacy')
+ # # => 'users/legacy.rhtml'
#
- def pick_template_extension(template_path)
- if template = template_file_from_name(template_path)
- template.extension
+ def pick_template(template_path)
+ path = template_path.sub(/^\//, '')
+ if m = path.match(/(.*)\.(\w+)$/)
+ template_file_name, template_file_extension = m[1], m[2]
+ else
+ template_file_name = path
+ end
+
+ # OPTIMIZE: Checks to lookup template in view path
+ if template = self.view_paths["#{template_file_name}.#{template_format}"]
+ template
+ elsif template = self.view_paths[template_file_name]
+ template
+ elsif first_render && template = self.view_paths["#{template_file_name}.#{first_render.extension}"]
+ template
+ elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"]
+ @template_format = :html
+ template
+ else
+ template = Template.new(template_path, view_paths)
+
+ if self.class.warn_cache_misses && logger = ActionController::Base.logger
+ logger.debug "[PERFORMANCE] Rendering a template that was " +
+ "not found in view path. Templates outside the view path are " +
+ "not cached and result in expensive disk operations. Move this " +
+ "file into #{view_paths.join(':')} or add the folder to your " +
+ "view path list"
+ end
+
+ template
end
end
@@ -303,6 +337,10 @@ module ActionView #:nodoc:
# Renders the template present at <tt>template_path</tt>. The hash in <tt>local_assigns</tt>
# is made available as local variables.
def render_file(template_path, use_full_path = nil, local_assigns = {}) #:nodoc:
+ unless use_full_path == nil
+ ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller)
+ end
+
if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/")
raise ActionViewError, <<-END_ERROR
Due to changes in ActionMailer, you need to provide the mailer_name along with the template name.
@@ -316,11 +354,12 @@ module ActionView #:nodoc:
END_ERROR
end
- Template.new(self, template_path, use_full_path, local_assigns).render_template
+ template = pick_template(template_path)
+ template.render_template(self, local_assigns)
end
def render_inline(text, local_assigns = {}, type = nil)
- InlineTemplate.new(self, text, local_assigns, type).render
+ InlineTemplate.new(text, type).render(self, local_assigns)
end
def wrap_content_for_layout(content)
@@ -343,33 +382,10 @@ module ActionView #:nodoc:
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
- def execute(template)
- send(template.method, template.locals) do |*names|
+ def execute(template, local_assigns = {})
+ send(template.method(local_assigns), local_assigns) do |*names|
instance_variable_get "@content_for_#{names.first || 'layout'}"
end
end
-
- def template_file_from_name(template_name)
- template_name = TemplateFile.from_path(template_name)
- pick_template(template_name) unless template_name.extension
- end
-
- def pick_template(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/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 0122de47af..bf13945844 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -209,6 +209,10 @@ module ActionView
# Note that the default javascript files will be included first. So Prototype and Scriptaculous are available to
# all subsequently included files.
#
+ # If you want Rails to search in all the subdirectories under javascripts, you should explicitly set <tt>:recursive</tt>:
+ #
+ # javascript_include_tag :all, :recursive => true
+ #
# == Caching multiple javascripts into one
#
# You can also cache multiple javascripts into one file, which requires less HTTP connections to download and can better be
@@ -235,18 +239,23 @@ module ActionView
#
# javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is true =>
# <script type="text/javascript" src="/javascripts/shop.js"></script>
+ #
+ # The <tt>:recursive</tt> option is also available for caching:
+ #
+ # javascript_include_tag :all, :cache => true, :recursive => true
def javascript_include_tag(*sources)
options = sources.extract_options!.stringify_keys
cache = options.delete("cache")
+ recursive = options.delete("recursive")
if ActionController::Base.perform_caching && cache
joined_javascript_name = (cache == true ? "all" : cache) + ".js"
joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)
- write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources)) unless File.exists?(joined_javascript_path)
+ write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path)
javascript_src_tag(joined_javascript_name, options)
else
- expand_javascript_sources(sources).collect { |source| javascript_src_tag(source, options) }.join("\n")
+ expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n")
end
end
@@ -332,13 +341,17 @@ module ActionView
# <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />
# <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />
#
- # You can also include all styles in the stylesheet directory using <tt>:all</tt> as the source:
+ # You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:
#
# stylesheet_link_tag :all # =>
# <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
# <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" type="text/css" />
# <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
#
+ # If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>:
+ #
+ # stylesheet_link_tag :all, :recursive => true
+ #
# == Caching multiple stylesheets into one
#
# You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
@@ -362,18 +375,23 @@ module ActionView
#
# stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when ActionController::Base.perform_caching is true =>
# <link href="/stylesheets/payment.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # The <tt>:recursive</tt> option is also available for caching:
+ #
+ # stylesheet_link_tag :all, :cache => true, :recursive => true
def stylesheet_link_tag(*sources)
options = sources.extract_options!.stringify_keys
cache = options.delete("cache")
+ recursive = options.delete("recursive")
if ActionController::Base.perform_caching && cache
joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"
joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name)
- write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources)) unless File.exists?(joined_stylesheet_path)
+ write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path)
stylesheet_tag(joined_stylesheet_name, options)
else
- expand_stylesheet_sources(sources).collect { |source| stylesheet_tag(source, options) }.join("\n")
+ expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n")
end
end
@@ -556,18 +574,19 @@ module ActionView
tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false)
end
- def compute_javascript_paths(sources)
- expand_javascript_sources(sources).collect { |source| compute_public_path(source, 'javascripts', 'js', false) }
+ def compute_javascript_paths(*args)
+ expand_javascript_sources(*args).collect { |source| compute_public_path(source, 'javascripts', 'js', false) }
end
- def compute_stylesheet_paths(sources)
- expand_stylesheet_sources(sources).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) }
+ def compute_stylesheet_paths(*args)
+ expand_stylesheet_sources(*args).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) }
end
- def expand_javascript_sources(sources)
+ def expand_javascript_sources(sources, recursive = false)
if sources.include?(:all)
- all_javascript_files = Dir[File.join(JAVASCRIPTS_DIR, '*.js')].collect { |file| File.basename(file).gsub(/\.\w+$/, '') }.sort
- @@all_javascript_sources ||= ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
+ all_javascript_files = collect_asset_files(JAVASCRIPTS_DIR, ('**' if recursive), '*.js')
+ @@all_javascript_sources ||= {}
+ @@all_javascript_sources[recursive] ||= ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
else
expanded_sources = sources.collect do |source|
determine_source(source, @@javascript_expansions)
@@ -577,9 +596,10 @@ module ActionView
end
end
- def expand_stylesheet_sources(sources)
+ def expand_stylesheet_sources(sources, recursive)
if sources.first == :all
- @@all_stylesheet_sources ||= Dir[File.join(STYLESHEETS_DIR, '*.css')].collect { |file| File.basename(file).gsub(/\.\w+$/, '') }.sort
+ @@all_stylesheet_sources ||= {}
+ @@all_stylesheet_sources[recursive] ||= collect_asset_files(STYLESHEETS_DIR, ('**' if recursive), '*.css')
else
sources.collect do |source|
determine_source(source, @@stylesheet_expansions)
@@ -604,6 +624,14 @@ module ActionView
FileUtils.mkdir_p(File.dirname(joined_asset_path))
File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }
end
+
+ def collect_asset_files(*path)
+ dir = path.first
+
+ Dir[File.join(*path.compact)].collect do |file|
+ file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
+ end.sort
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index c2aab5aa72..930c397785 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -32,7 +32,7 @@ module ActionView
# <i>Topics listed alphabetically</i>
# <% end %>
def cache(name = {}, options = nil, &block)
- handler = Base.handler_class_for_extension(current_render_extension.to_sym)
+ handler = Template.handler_class_for_extension(current_render_extension.to_sym)
handler.new(@controller).cache_fragment(block, name, options)
end
end
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 990c30b90d..720e2da8cc 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -34,9 +34,8 @@ module ActionView
# Return captured buffer in erb.
if block_called_from_erb?(block)
with_output_buffer { block.call(*args) }
-
- # Return block result otherwise, but protect buffer also.
else
+ # Return block result otherwise, but protect buffer also.
with_output_buffer { return block.call(*args) }
end
end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 17497481e6..0735ed07ee 100755
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -159,7 +159,10 @@ module ActionView
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a specified
# time-based attribute (identified by +method+) on an object assigned to the template (identified by +object+).
# You can include the seconds with <tt>:include_seconds</tt>.
- #
+ #
+ # This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
+ # <tt>:ignore_date</tt> is set to +true+.
+ #
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
# ==== Examples
@@ -655,7 +658,7 @@ module ActionView
order.reverse.each do |param|
# Send hidden fields for discarded elements once output has started
# This ensures AR can reconstruct valid dates using ParseDate
- next if discard[param] && date_or_time_select.empty?
+ next if discard[param] && (date_or_time_select.empty? || options[:ignore_date])
date_or_time_select.insert(0, self.send("select_#{param}", datetime, options_with_prefix(position[param], options.merge(:use_hidden => discard[param])), html_options))
date_or_time_select.insert(0,
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 9f6e550c09..576ca84bcc 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -397,7 +397,7 @@ module ActionView
# # Generates: <input name="create_btn" onclick="new Ajax.Request('/testing/create',
# # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
# # return false;" type="button" value="Create" />
- # <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
+ # <%= button_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
#
# # Submit to the remote action update and update the DIV succeed or fail based
# # on the success or failure of the request
@@ -405,11 +405,13 @@ module ActionView
# # Generates: <input name="update_btn" onclick="new Ajax.Updater({success:'succeed',failure:'fail'},
# # '/testing/update', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
# # return false;" type="button" value="Update" />
- # <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
+ # <%= button_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
# :update => { :success => "succeed", :failure => "fail" }
#
# <tt>options</tt> argument is the same as in form_remote_tag.
- def submit_to_remote(name, value, options = {})
+ #
+ # Note: This method used to be called submit_to_remote, but that's now just an alias for button_to_remote
+ def button_to_remote(name, value, options = {})
options[:with] ||= 'Form.serialize(this.form)'
options[:html] ||= {}
@@ -420,6 +422,7 @@ module ActionView
tag("input", options[:html], false)
end
+ alias_method :submit_to_remote, :button_to_remote
# Returns '<tt>eval(request.responseText)</tt>' which is the JavaScript function
# that +form_remote_tag+ can call in <tt>:complete</tt> to evaluate a multiple
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index a6c48737e9..3e3452b615 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -27,7 +27,7 @@ module ActionView
# %>
def concat(string, unused_binding = nil)
if unused_binding
- ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.")
+ ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.", caller)
end
output_buffer << string
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 89166fe19a..94e1f1d33a 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -63,17 +63,15 @@ module ActionView
# # calls @workshop.to_s
# # => /workshops/5
def url_for(options = {})
+ options ||= {}
case options
when Hash
- show_path = options[:host].nil? ? true : false
- options = { :only_path => show_path }.update(options.symbolize_keys)
+ options = { :only_path => options[:host].nil? }.update(options.symbolize_keys)
escape = options.key?(:escape) ? options.delete(:escape) : true
url = @controller.send(:url_for, options)
when String
escape = true
url = options
- when NilClass
- url = @controller.send(:url_for, nil)
else
escape = false
url = polymorphic_path(options)
@@ -468,7 +466,7 @@ module ActionView
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
if encode == "javascript"
- "document.write('#{content_tag("a", name || email_address, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
+ "document.write('#{content_tag("a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
string << sprintf("%%%x", c)
end
"<script type=\"#{Mime::JS}\">eval(unescape('#{string}'))</script>"
diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/inline_template.rb
index 19ab92ce1a..5e00cef13f 100644
--- a/actionpack/lib/action_view/inline_template.rb
+++ b/actionpack/lib/action_view/inline_template.rb
@@ -1,16 +1,19 @@
module ActionView #:nodoc:
class InlineTemplate #:nodoc:
- include Renderer
+ include Renderable
- def initialize(view, source, locals = {}, type = nil)
- @view = view
+ attr_reader :source, :extension, :method_segment
+ def initialize(source, type = nil)
@source = source
@extension = type
- @locals = locals || {}
-
- @method_key = @source
- @handler = Base.handler_class_for_extension(@extension).new(@view)
+ @method_segment = "inline_#{@source.hash.abs}"
end
+
+ private
+ # Always recompile inline templates
+ def recompile?(local_assigns)
+ true
+ end
end
end
diff --git a/actionpack/lib/action_view/partial_template.rb b/actionpack/lib/action_view/partial_template.rb
deleted file mode 100644
index 72f831e937..0000000000
--- a/actionpack/lib/action_view/partial_template.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-module ActionView #:nodoc:
- class PartialTemplate < Template #:nodoc:
- attr_reader :variable_name, :object, :as
-
- def initialize(view, partial_path, object = nil, locals = {}, as = nil)
- @view_controller = view.controller if view.respond_to?(:controller)
- @as = as
- set_path_and_variable_name!(partial_path)
- super(view, @path, nil, locals)
- add_object_to_local_assigns!(object)
-
- # This is needed here in order to compile template with knowledge of 'counter'
- initialize_counter!
-
- # Prepare early. This is a performance optimization for partial collections
- prepare!
- end
-
- def render
- ActionController::Base.benchmark("Rendered #{@path.path_without_format_and_extension}", Logger::DEBUG, false) do
- super
- end
- end
-
- def render_member(object)
- @locals[:object] = @locals[@variable_name] = object
- @locals[as] = object if as
-
- template = render_template
- @locals[@counter_name] += 1
- @locals.delete(as)
- @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] ||= object || @view_controller.instance_variable_get("@#{variable_name}")
- @locals[as] ||= @locals[:object] if as
- end
-
- 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
-end
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index 7c6c98d611..116d61e13b 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -104,10 +104,12 @@ module ActionView
module Partials
private
def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc:
+ local_assigns ||= {}
+
case partial_path
when String, Symbol, NilClass
- # Render the template
- ActionView::PartialTemplate.new(self, partial_path, object_assigns, local_assigns).render_template
+ variable_name, path = partial_pieces(partial_path)
+ pick_template(path).render_partial(self, variable_name, object_assigns, local_assigns)
when ActionView::Helpers::FormBuilder
builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '')
render_partial(builder_partial_path, object_assigns, (local_assigns || {}).merge(builder_partial_path.to_sym => partial_path))
@@ -128,31 +130,43 @@ module ActionView
local_assigns = local_assigns ? local_assigns.clone : {}
spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : ''
+ _partial_pieces = {}
+ _templates = {}
- if partial_path.nil?
- render_partial_collection_with_unknown_partial_path(collection, local_assigns, as)
- else
- render_partial_collection_with_known_partial_path(collection, partial_path, local_assigns, as)
- end.join(spacer)
- end
+ index = 0
+ collection.map do |object|
+ _partial_path ||= partial_path || ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
+ variable_name, path = _partial_pieces[_partial_path] ||= partial_pieces(_partial_path)
+ template = _templates[path] ||= pick_template(path)
- def render_partial_collection_with_known_partial_path(collection, partial_path, local_assigns, as)
- template = ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns, as)
- collection.map do |element|
- template.render_member(element)
- end
+ local_assigns["#{variable_name}_counter".to_sym] = index
+ local_assigns[:object] = local_assigns[variable_name] = object
+ local_assigns[as] = object if as
+
+ result = template.render_partial(self, variable_name, object, local_assigns)
+
+ local_assigns.delete(as)
+ local_assigns.delete(variable_name)
+ local_assigns.delete(:object)
+ index += 1
+
+ result
+ end.join(spacer)
end
- def render_partial_collection_with_unknown_partial_path(collection, local_assigns, as)
- templates = Hash.new
- i = 0
- collection.map do |element|
- partial_path = ActionController::RecordIdentifier.partial_path(element, controller.class.controller_path)
- template = templates[partial_path] ||= ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns, as)
- template.counter = i
- i += 1
- template.render_member(element)
+ def partial_pieces(partial_path)
+ if partial_path.include?('/')
+ variable_name = File.basename(partial_path)
+ path = "#{File.dirname(partial_path)}/_#{variable_name}"
+ elsif respond_to?(:controller)
+ variable_name = partial_path
+ path = "#{controller.class.controller_path}/_#{variable_name}"
+ else
+ variable_name = partial_path
+ path = "_#{variable_name}"
end
+ variable_name = variable_name.sub(/\..*$/, '').to_sym
+ return variable_name, path
end
end
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
new file mode 100644
index 0000000000..b0ab7d0c67
--- /dev/null
+++ b/actionpack/lib/action_view/paths.rb
@@ -0,0 +1,96 @@
+module ActionView #:nodoc:
+ class PathSet < Array #:nodoc:
+ def self.type_cast(obj)
+ if obj.is_a?(String)
+ if Base.warn_cache_misses && defined?(Rails) && Rails.initialized?
+ Rails.logger.debug "[PERFORMANCE] Processing view path during a " +
+ "request. This an expense disk operation that should be done at " +
+ "boot. You can manually process this view path with " +
+ "ActionView::Base.process_view_paths(#{obj.inspect}) and set it " +
+ "as your view path"
+ end
+ Path.new(obj)
+ else
+ obj
+ end
+ end
+
+ class Path #:nodoc:
+ attr_reader :path, :paths
+ delegate :to_s, :to_str, :inspect, :to => :path
+
+ def initialize(path)
+ @path = path.freeze
+ reload!
+ end
+
+ def ==(path)
+ to_str == path.to_str
+ end
+
+ def [](path)
+ @paths[path]
+ end
+
+ # Rebuild load path directory cache
+ def reload!
+ @paths = {}
+
+ templates_in_path do |template|
+ @paths[template.path] = template
+ @paths[template.path_without_extension] ||= template
+ end
+
+ @paths.freeze
+ end
+
+ private
+ def templates_in_path
+ (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
+ unless File.directory?(file)
+ template = Template.new(file.split("#{self}/").last, self)
+ # Eager load memoized methods and freeze cached template
+ template.freeze if Base.cache_template_loading
+ yield template
+ end
+ end
+ 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_path)
+ each do |path|
+ if template = path[template_path]
+ return template
+ end
+ end
+ nil
+ end
+
+ private
+ def delete_paths!(paths)
+ paths.each { |p1| delete_if { |p2| p1.to_s == p2.to_s } }
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
new file mode 100644
index 0000000000..2c4302146f
--- /dev/null
+++ b/actionpack/lib/action_view/renderable.rb
@@ -0,0 +1,79 @@
+module ActionView
+ module Renderable
+ # NOTE: The template that this mixin is beening include into is frozen
+ # So you can not set or modify any instance variables
+
+ def self.included(base)
+ @@mutex = Mutex.new
+ end
+
+ # NOTE: Exception to earlier notice. Ensure this is called before freeze
+ def handler
+ @handler ||= Template.handler_class_for_extension(extension)
+ end
+
+ # NOTE: Exception to earlier notice. Ensure this is called before freeze
+ def compiled_source
+ @compiled_source ||= handler.new(nil).compile(self) if handler.compilable?
+ end
+
+ def render(view, local_assigns = {})
+ view.first_render ||= self
+ view.send(:evaluate_assigns)
+ view.current_render_extension = extension
+ compile(local_assigns) if handler.compilable?
+ handler.new(view).render(self, local_assigns)
+ end
+
+ def method(local_assigns)
+ if local_assigns && local_assigns.any?
+ local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
+ end
+ ['_run', extension, method_segment, local_assigns_keys].compact.join('_').to_sym
+ end
+
+ private
+ # Compile and evaluate the template's code
+ def compile(local_assigns)
+ render_symbol = method(local_assigns)
+
+ @@mutex.synchronize do
+ return false unless recompile?(render_symbol)
+
+ locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
+
+ source = <<-end_src
+ def #{render_symbol}(local_assigns)
+ old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
+ ensure
+ self.output_buffer = old_output_buffer
+ end
+ end_src
+
+ begin
+ file_name = respond_to?(:filename) ? filename : 'compiled-template'
+ ActionView::Base::CompiledTemplates.module_eval(source, file_name, 0)
+ rescue Exception => e # errors from template code
+ if logger = ActionController::Base.logger
+ logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
+ logger.debug "Function body: #{source}"
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
+ end
+
+ raise ActionView::TemplateError.new(self, {}, e)
+ end
+ end
+ end
+
+ # Method to check whether template compilation is necessary.
+ # 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)
+ unless Base::CompiledTemplates.instance_methods.include?(symbol) && Base.cache_template_loading
+ true
+ else
+ false
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb
new file mode 100644
index 0000000000..6a17b50a14
--- /dev/null
+++ b/actionpack/lib/action_view/renderable_partial.rb
@@ -0,0 +1,19 @@
+module ActionView
+ module RenderablePartial
+ # NOTE: The template that this mixin is beening include into is frozen
+ # So you can not set or modify any instance variables
+
+ def render(view, local_assigns = {})
+ ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do
+ super
+ end
+ end
+
+ def render_partial(view, variable_name, object = nil, local_assigns = {}, as = nil)
+ object ||= view.controller.instance_variable_get("@#{variable_name}") if view.respond_to?(:controller)
+ local_assigns[:object] ||= local_assigns[variable_name] ||= object
+ local_assigns[as] ||= local_assigns[:object] if as
+ render_template(view, local_assigns)
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/renderer.rb b/actionpack/lib/action_view/renderer.rb
deleted file mode 100644
index e6c64d2749..0000000000
--- a/actionpack/lib/action_view/renderer.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-module ActionView
- module Renderer
- # TODO: Local assigns should not be tied to template instance
- attr_accessor :locals
-
- # TODO: These readers should be private
- attr_reader :filename, :source, :handler, :method_key, :method
-
- def render
- prepare!
- @handler.render(self)
- end
-
- private
- def prepare!
- unless @prepared
- @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
- end
-
- @prepared = true
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 8142232c8f..03f9234289 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,79 +1,112 @@
module ActionView #:nodoc:
- class Template #:nodoc:
- include Renderer
+ class Template
+ extend TemplateHandlers
+ include Renderable
- class << self
- # TODO: Deprecate
- delegate :register_template_handler, :to => 'ActionView::Base'
+ attr_accessor :filename, :load_path, :base_path, :name, :format, :extension
+ delegate :to_s, :to => :path
+
+ def initialize(template_path, load_paths = [])
+ template_path = template_path.dup
+ @base_path, @name, @format, @extension = split(template_path)
+ @base_path.to_s.gsub!(/\/$/, '') # Push to split method
+ @load_path, @filename = find_full_path(template_path, load_paths)
+
+ # Extend with partial super powers
+ extend RenderablePartial if @name =~ /^_/
end
- attr_reader :path, :extension
+ def freeze
+ # Eager load memoized methods
+ format_and_extension
+ path
+ path_without_extension
+ path_without_format_and_extension
+ source
+ method_segment
- def initialize(view, path, use_full_path = nil, locals = {})
- unless use_full_path == nil
- ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller)
- end
+ # Eager load memoized methods from Renderable
+ handler
+ compiled_source
- @view = view
- @paths = view.view_paths
+ instance_variables.each { |ivar| ivar.freeze }
- @original_path = path
- @path = TemplateFile.from_path(path)
- @view.first_render ||= @path.to_s
+ super
+ end
- set_extension_and_file_name
+ def format_and_extension
+ @format_and_extension ||= (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
+ end
+
+ def path
+ @path ||= [base_path, [name, format, extension].compact.join('.')].compact.join('/')
+ end
- @method_key = @filename
- @locals = locals || {}
- @handler = Base.handler_class_for_extension(@extension).new(@view)
+ def path_without_extension
+ @path_without_extension ||= [base_path, [name, format].compact.join('.')].compact.join('/')
end
- def render_template
- render
+ def path_without_format_and_extension
+ @path_without_format_and_extension ||= [base_path, name].compact.join('/')
+ end
+
+ def source
+ @source ||= File.read(@filename)
+ end
+
+ def method_segment
+ unless @method_segment
+ segment = File.expand_path(@filename)
+ segment.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT)
+ segment.gsub!(/([^a-zA-Z0-9_])/) { $1.ord }
+ @method_segment = segment
+ end
+
+ @method_segment
+ end
+
+ def render_template(view, local_assigns = {})
+ render(view, local_assigns)
rescue Exception => e
raise e unless filename
if TemplateError === e
e.sub_template_of(filename)
raise e
else
- raise TemplateError.new(self, @view.assigns, e)
+ raise TemplateError.new(self, view.assigns, e)
end
end
- def source
- @source ||= File.read(self.filename)
- end
-
- def base_path_for_exception
- (@paths.find_load_path_for_path(@path) || @paths.first).to_s
- end
-
private
- def set_extension_and_file_name
- @extension = @path.extension
-
- unless @extension
- @path = @view.send(:template_file_from_name, @path)
- raise_missing_template_exception unless @path
- @extension = @path.extension
- end
+ def valid_extension?(extension)
+ Template.template_handler_extensions.include?(extension)
+ end
- if p = @paths.find_template_file_for_path(path)
- @path = p
- @filename = @path.full_path
- @extension = @path.extension
- raise_missing_template_exception if @filename.blank?
- else
- @filename = @original_path
- raise_missing_template_exception unless File.exist?(@filename)
+ def find_full_path(path, load_paths)
+ load_paths = Array(load_paths) + [nil]
+ load_paths.each do |load_path|
+ file = [load_path, path].compact.join('/')
+ return load_path, file if File.exist?(file)
end
+ raise MissingTemplate.new(load_paths, path)
end
- 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}"
+ # Returns file split into an array
+ # [base_path, name, format, extension]
+ def split(file)
+ if m = file.match(/^(.*\/)?([^\.]+)\.?(\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
+ if valid_extension?(m[3]) # No format
+ [m[1], m[2], nil, m[3]]
+ else # No extension
+ [m[1], m[2], m[3], nil]
+ end
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_view/template_error.rb b/actionpack/lib/action_view/template_error.rb
index 65d80362b5..35fc07bdb2 100644
--- a/actionpack/lib/action_view/template_error.rb
+++ b/actionpack/lib/action_view/template_error.rb
@@ -7,7 +7,7 @@ module ActionView
attr_reader :original_exception
def initialize(template, assigns, original_exception)
- @base_path = template.base_path_for_exception
+ @base_path = template.base_path
@assigns, @source, @original_exception = assigns.dup, template.source, original_exception
@file_path = template.filename
@backtrace = compute_backtrace
@@ -105,6 +105,6 @@ module ActionView
end
if defined?(Exception::TraceSubstitutions)
- Exception::TraceSubstitutions << [/:in\s+`_run_(html|xml).*'\s*$/, '']
+ Exception::TraceSubstitutions << [/:in\s+`_run_.*'\s*$/, '']
Exception::TraceSubstitutions << [%r{^\s*#{Regexp.escape RAILS_ROOT}/}, ''] if defined?(RAILS_ROOT)
end
diff --git a/actionpack/lib/action_view/template_file.rb b/actionpack/lib/action_view/template_file.rb
deleted file mode 100644
index c38e8ed122..0000000000
--- a/actionpack/lib/action_view/template_file.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-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)
- path.is_a?(self) ? path : new(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)
- path = path.dup
-
- # Clear the forward slash in the beginning
- trim_forward_slash!(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_handler.rb b/actionpack/lib/action_view/template_handler.rb
index 39e578e586..1afea21f67 100644
--- a/actionpack/lib/action_view/template_handler.rb
+++ b/actionpack/lib/action_view/template_handler.rb
@@ -1,9 +1,5 @@
module ActionView
class TemplateHandler
- def self.line_offset
- 0
- end
-
def self.compilable?
false
end
@@ -12,7 +8,7 @@ module ActionView
@view = view
end
- def render(template)
+ def render(template, local_assigns = {})
end
def compile(template)
@@ -22,10 +18,6 @@ module ActionView
self.class.compilable?
end
- def line_offset
- self.class.line_offset
- end
-
# Called by CacheHelper#cache
def cache_fragment(block, name = {}, options = nil)
end
diff --git a/actionpack/lib/action_view/template_handlers/builder.rb b/actionpack/lib/action_view/template_handlers/builder.rb
index ee02ce1a6f..cbe53e11d8 100644
--- a/actionpack/lib/action_view/template_handlers/builder.rb
+++ b/actionpack/lib/action_view/template_handlers/builder.rb
@@ -5,17 +5,12 @@ module ActionView
class Builder < TemplateHandler
include Compilable
- def self.line_offset
- 2
- end
-
def compile(template)
- content_type_handler = (@view.send!(:controller).respond_to?(:response) ? "controller.response" : "controller")
-
- "#{content_type_handler}.content_type ||= Mime::XML\n" +
- "xml = ::Builder::XmlMarkup.new(:indent => 2)\n" +
+ # ActionMailer does not have a response
+ "controller.respond_to?(:response) && controller.response.content_type ||= Mime::XML;" +
+ "xml = ::Builder::XmlMarkup.new(:indent => 2);" +
template.source +
- "\nxml.target!\n"
+ ";xml.target!;"
end
def cache_fragment(block, name = {}, options = nil)
diff --git a/actionpack/lib/action_view/template_handlers/compilable.rb b/actionpack/lib/action_view/template_handlers/compilable.rb
index f436ebbe45..a0ebaefeef 100644
--- a/actionpack/lib/action_view/template_handlers/compilable.rb
+++ b/actionpack/lib/action_view/template_handlers/compilable.rb
@@ -1,21 +1,8 @@
module ActionView
module TemplateHandlers
module Compilable
-
def self.included(base)
base.extend ClassMethod
-
- # Map method names to their compile time
- base.cattr_accessor :compile_time
- base.compile_time = {}
-
- # Map method names to the names passed in local assigns so far
- base.cattr_accessor :template_args
- base.template_args = {}
-
- # Count the number of inline templates
- base.cattr_accessor :inline_template_count
- base.inline_template_count = 0
end
module ClassMethod
@@ -24,111 +11,10 @@ module ActionView
true
end
end
-
- def render(template)
- @view.send :execute, template
- end
-
- # Compile and evaluate the template's code
- def compile_template(template)
- return unless compile_template?(template)
-
- render_symbol = assign_method_name(template)
- render_source = create_template_source(template, render_symbol)
- line_offset = self.template_args[render_symbol].size + self.line_offset
-
- begin
- file_name = template.filename || 'compiled-template'
- ActionView::Base::CompiledTemplates.module_eval(render_source, file_name, -line_offset)
- rescue Exception => e # errors from template code
- if Base.logger
- Base.logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
- Base.logger.debug "Function body: #{render_source}"
- Base.logger.debug "Backtrace: #{e.backtrace.join("\n")}"
- end
-
- raise ActionView::TemplateError.new(template, @view.assigns, e)
- end
-
- self.compile_time[render_symbol] = Time.now
- # logger.debug "Compiled template #{file_name || template}\n ==> #{render_symbol}" if logger
- end
-
- private
-
- # Method to check whether template compilation is necessary.
- # The template will be compiled if the inline template or file has not been compiled yet,
- # if local_assigns has a new key, which isn't supported by the compiled code yet,
- # or if the file has changed on disk and checking file mods hasn't been disabled.
- def compile_template?(template)
- method_key = template.method_key
- render_symbol = @view.method_names[method_key]
-
- compile_time = self.compile_time[render_symbol]
- if compile_time && supports_local_assigns?(render_symbol, template.locals)
- if template.filename && !@view.cache_template_loading
- template_changed_since?(template.filename, compile_time)
- end
- else
- true
- end
- end
-
- def assign_method_name(template)
- @view.method_names[template.method_key] ||= compiled_method_name(template)
- end
- def compiled_method_name(template)
- ['_run', self.class.to_s.demodulize.underscore, compiled_method_name_file_path_segment(template.filename)].compact.join('_').to_sym
+ def render(template, local_assigns = {})
+ @view.send(:execute, template, local_assigns)
end
-
- def compiled_method_name_file_path_segment(file_name)
- if file_name
- s = File.expand_path(file_name)
- s.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT)
- s.gsub!(/([^a-zA-Z0-9_])/) { $1.ord }
- s
- else
- (self.inline_template_count += 1).to_s
- end
- end
-
- # Method to create the source code for a given template.
- def create_template_source(template, render_symbol)
- body = compile(template)
-
- self.template_args[render_symbol] ||= {}
- locals_keys = self.template_args[render_symbol].keys | template.locals.keys
- self.template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h }
-
- locals_code = ""
- locals_keys.each do |key|
- locals_code << "#{key} = local_assigns[:#{key}]\n"
- end
-
- <<-end_src
- def #{render_symbol}(local_assigns)
- old_output_buffer = output_buffer;#{locals_code}#{body}
- ensure
- self.output_buffer = old_output_buffer
- end
- end_src
- end
-
- # Return true if the given template was compiled for a superset of the keys in local_assigns
- def supports_local_assigns?(render_symbol, local_assigns)
- local_assigns.empty? ||
- ((args = self.template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) })
- end
-
- # Method to handle checking a whether a template has changed since last compile; isolated so that templates
- # not stored on the file system can hook and extend appropriately.
- def template_changed_since?(file_name, compile_time)
- lstat = File.lstat(file_name)
- compile_time < lstat.mtime ||
- (lstat.symlink? && compile_time < File.stat(file_name).mtime)
- end
-
end
end
end
diff --git a/actionpack/lib/action_view/template_handlers/rjs.rb b/actionpack/lib/action_view/template_handlers/rjs.rb
index 5854e33fed..3892bf1d6e 100644
--- a/actionpack/lib/action_view/template_handlers/rjs.rb
+++ b/actionpack/lib/action_view/template_handlers/rjs.rb
@@ -3,13 +3,9 @@ module ActionView
class RJS < TemplateHandler
include Compilable
- def self.line_offset
- 2
- end
-
def compile(template)
- "controller.response.content_type ||= Mime::JS\n" +
- "update_page do |page|\n#{template.source}\nend"
+ "controller.response.content_type ||= Mime::JS;" +
+ "update_page do |page|;#{template.source}\nend"
end
def cache_fragment(block, name = {}, options = nil) #:nodoc:
diff --git a/actionpack/lib/action_view/view_load_paths.rb b/actionpack/lib/action_view/view_load_paths.rb
deleted file mode 100644
index 6e439a009c..0000000000
--- a/actionpack/lib/action_view/view_load_paths.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-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, :to_str, :inspect, :to => :path
-
- def initialize(path)
- @path = path.freeze
- reload!
- end
-
- def ==(path)
- to_str == path.to_str
- 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
-
- def find_template_file_for_partial_path(template_path, template_format)
- @paths["#{template_path}.#{template_format}"] ||
- @paths[template_path] ||
- @paths[template_path.gsub(/\..*$/, '')]
- 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(template_path)
- template_path_without_extension, template_extension = path_and_extension(template_path.to_s)
- each do |path|
- if f = path.find_template_file_for_partial_path(template_path_without_extension, template_extension)
- return f
- end
- end
- nil
- end
-
- private
- def delete_paths!(paths)
- paths.each { |p1| delete_if { |p2| p1.to_s == p2.to_s } }
- 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
- end
-end