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.rb111
-rw-r--r--actionpack/lib/action_view/helpers.rb1
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb13
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/deprecated_block_helpers.rb52
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb31
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb86
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb5
-rw-r--r--actionpack/lib/action_view/lookup_context.rb151
-rw-r--r--actionpack/lib/action_view/paths.rb85
-rw-r--r--actionpack/lib/action_view/railtie.rb4
-rw-r--r--actionpack/lib/action_view/render/layouts.rb65
-rw-r--r--actionpack/lib/action_view/render/partials.rb48
-rw-r--r--actionpack/lib/action_view/render/rendering.rb115
-rw-r--r--actionpack/lib/action_view/template.rb32
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb54
-rw-r--r--actionpack/lib/action_view/template/resolver.rb136
-rw-r--r--actionpack/lib/action_view/template/text.rb13
-rw-r--r--actionpack/lib/action_view/test_case.rb7
27 files changed, 604 insertions, 492 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 2d2b53a6ce..ffe3060404 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -173,102 +173,43 @@ module ActionView #:nodoc:
module Subclasses
end
- include Helpers, Rendering, Partials, ::ERB::Util
+ include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
+ extend ActiveSupport::Memoizable
- def config
- self.config = DEFAULT_CONFIG unless @config
- @config
- end
-
- def config=(config)
- @config = ActiveSupport::OrderedOptions.new.merge(config)
- end
-
- extend ActiveSupport::Memoizable
-
- attr_accessor :base_path, :assigns, :template_extension
- attr_internal :captures
-
- def reset_formats(formats)
- old_formats, self.formats = self.formats, formats
- reset_hash_key
- yield if block_given?
- ensure
- if block_given?
- self.formats = old_formats
- reset_hash_key
- end
- end
-
- def reset_hash_key
- if defined?(AbstractController::HashKey)
- # This is expensive, but we need to reset this when the format is updated,
- # which currently only happens
- Thread.current[:format_locale_key] =
- AbstractController::HashKey.get(self.class, :formats => formats, :locale => [I18n.locale])
- end
- end
-
- def formats
- controller ? controller.formats : @formats
- end
-
- def formats=(val)
- if controller
- controller.formats = val
- else
- @formats = val
- end
- end
-
- class << self
- delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
- delegate :logger, :to => 'ActionController::Base', :allow_nil => true
- end
+ ActionView.run_base_hooks(self)
- @@debug_rjs = false
- ##
- # :singleton-method:
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
cattr_accessor :debug_rjs
+ @@debug_rjs = false
- # Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
- # Automatically reloading templates are not thread safe and should only be used in development mode.
- @@cache_template_loading = nil
- cattr_accessor :cache_template_loading
+ class_attribute :helpers
+ attr_reader :helpers
- # :nodoc:
- def self.xss_safe?
- true
+ class << self
+ delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
+ delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
- def self.cache_template_loading?
- ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading)
- end
+ attr_accessor :base_path, :assigns, :template_extension, :lookup_context
+ attr_internal :captures, :request, :layout, :controller, :template, :config
- attr_internal :request, :layout
-
- def controller_path
- @controller_path ||= controller && controller.controller_path
- end
+ delegate :find, :exists?, :formats, :formats=, :locale, :locale=,
+ :view_paths, :view_paths=, :with_fallbacks, :update_details, :to => :lookup_context
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
delegate :logger, :to => :controller, :allow_nil => true
- delegate :find, :to => :view_paths
-
- include Context
+ def self.xss_safe? #:nodoc:
+ true
+ end
def self.process_view_paths(value)
ActionView::PathSet.new(Array(value))
end
- class_attribute :helpers
- attr_reader :helpers
-
def self.for_controller(controller)
@views ||= {}
@@ -296,26 +237,26 @@ module ActionView #:nodoc:
klass = self
end
- klass.new(controller.class.view_paths, {}, controller)
+ klass.new(controller.lookup_context, {}, controller)
end
- def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
+ def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
@config = nil
- @formats = formats
@assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
@helpers = self.class.helpers || Module.new
@_controller = controller
- @_content_for = Hash.new {|h,k| h[k] = ActiveSupport::SafeBuffer.new }
+ @_config = ActiveSupport::InheritableOptions.new(controller.config) if controller
+ @_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
- self.view_paths = view_paths
- end
- attr_internal :controller, :template
- attr_reader :view_paths
+ @lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
+ lookup_context : ActionView::LookupContext.new(lookup_context)
+ @lookup_context.formats = formats if formats
+ end
- def view_paths=(paths)
- @view_paths = self.class.process_view_paths(paths)
+ def controller_path
+ @controller_path ||= controller && controller.controller_path
end
def punctuate_body!(part)
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index b4f649385a..e359b0bdac 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -10,6 +10,7 @@ module ActionView #:nodoc:
autoload :CsrfHelper, 'action_view/helpers/csrf_helper'
autoload :DateHelper, 'action_view/helpers/date_helper'
autoload :DebugHelper, 'action_view/helpers/debug_helper'
+ autoload :DeprecatedBlockHelpers, 'action_view/helpers/deprecated_block_helpers'
autoload :FormHelper, 'action_view/helpers/form_helper'
autoload :FormOptionsHelper, 'action_view/helpers/form_options_helper'
autoload :FormTagHelper, 'action_view/helpers/form_tag_helper'
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index ed83b8a8b2..4e12cdab54 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -5,9 +5,11 @@ require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/kernel/reporting'
module ActionView
- class Base
- @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
- cattr_accessor :field_error_proc
+ ActionView.base_hook do
+ class ActionView::Base
+ @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
+ cattr_accessor :field_error_proc
+ end
end
module Helpers
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 96976ce45f..de3d61ebbe 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -133,13 +133,6 @@ module ActionView
# change. You can use something like Live HTTP Headers for Firefox to verify
# that the cache is indeed working.
module AssetTagHelper
- assets_dir = defined?(Rails.public_path) ? Rails.public_path : "public"
- ActionView::DEFAULT_CONFIG = {
- :assets_dir => assets_dir,
- :javascripts_dir => "#{assets_dir}/javascripts",
- :stylesheets_dir => "#{assets_dir}/stylesheets",
- }
-
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
# Returns a link tag that browsers and news readers can use to auto-detect
@@ -648,8 +641,8 @@ module ActionView
source = rewrite_asset_path(source)
if has_request && include_host
- unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
- source = "#{ActionController::Base.relative_url_root}#{source}"
+ unless source =~ %r{^#{controller.config.relative_url_root}/}
+ source = "#{controller.config.relative_url_root}#{source}"
end
end
end
@@ -677,7 +670,7 @@ module ActionView
# or the value returned from invoking the proc if it's a proc or the value from
# invoking call if it's an object responding to call.
def compute_asset_host(source)
- if host = ActionController::Base.asset_host
+ if host = config.asset_host
if host.is_a?(Proc) || host.respond_to?(:call)
case host.is_a?(Proc) ? host.arity : host.method(:call).arity
when 2
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index a26a8c9b4b..58c3a8752e 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -114,7 +114,7 @@ module ActionView
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
xml.feed(feed_opts) do
- xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
+ xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 8c48300ed3..75fc2fddeb 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -30,14 +30,10 @@ module ActionView
# <b><%= @greeting %></b>
# </body></html>
#
- def capture(*args, &block)
- # Return captured buffer in erb.
- if block_called_from_erb?(block)
- with_output_buffer { block.call(*args) }
- else
- # Return block result otherwise, but protect buffer also.
- with_output_buffer { return block.call(*args) }
- end
+ def capture(*args)
+ value = nil
+ buffer = with_output_buffer { value = yield *args }
+ buffer.presence || value
end
# Calling content_for stores a block of markup in an identifier for later use.
@@ -143,7 +139,7 @@ module ActionView
# Defaults to a new empty string.
def with_output_buffer(buf = nil) #:nodoc:
unless buf
- buf = ActiveSupport::SafeBuffer.new
+ buf = ActionView::OutputBuffer.new
buf.force_encoding(output_buffer.encoding) if buf.respond_to?(:force_encoding)
end
self.output_buffer, old_buffer = buf, output_buffer
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 89ac682c18..42018ee261 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -1,5 +1,6 @@
require "date"
require 'action_view/helpers/tag_helper'
+require 'active_support/core_ext/hash/slice'
module ActionView
module Helpers
@@ -865,7 +866,7 @@ module ActionView
:id => input_id_from_type(type),
:name => input_name_from_type(type),
:value => value
- }) + "\n").html_safe
+ }.merge(@html_options.slice(:disabled))) + "\n").html_safe
end
# Returns the name attribute for the input tag
diff --git a/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb b/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb
new file mode 100644
index 0000000000..3d0657e873
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb
@@ -0,0 +1,52 @@
+module ActionView
+ module Helpers
+ module DeprecatedBlockHelpers
+ extend ActiveSupport::Concern
+
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::TextHelper
+ include ActionView::Helpers::JavaScriptHelper
+ include ActionView::Helpers::FormHelper
+
+ def content_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def javascript_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def form_for(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def form_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def fields_for(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def field_set_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
+
+ if RUBY_VERSION < '1.9.0'
+ # Check whether we're called from an erb template.
+ # We'd return a string in any other case, but erb <%= ... %>
+ # can't take an <% end %> later on, so we have to use <% ... %>
+ # and implicitly concat.
+ def block_called_from_erb?(block)
+ block && eval(BLOCK_CALLED_FROM_ERB, block)
+ end
+ else
+ def block_called_from_erb?(block)
+ block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index ace3bcfde3..48df26efaa 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -92,6 +92,10 @@ module ActionView
# link:classes/ActionView/Helpers/DateHelper.html, and
# link:classes/ActionView/Helpers/ActiveRecordHelper.html
module FormHelper
+ extend ActiveSupport::Concern
+
+ include FormTagHelper
+
# Creates a form and a scope around a specific model object that is used
# as a base for questioning about values for the fields.
#
@@ -309,9 +313,9 @@ module ActionView
options[:html][:remote] = true if options.delete(:remote)
- safe_concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
- fields_for(object_name, *(args << options), &proc)
- safe_concat('</form>')
+ output = form_tag(options.delete(:url) || {}, options.delete(:html) || {})
+ output << fields_for(object_name, *(args << options), &proc)
+ output.safe_concat('</form>')
end
def apply_form_for_options!(object_or_array, options) #:nodoc:
@@ -528,7 +532,10 @@ module ActionView
end
builder = options[:builder] || ActionView::Base.default_form_builder
- yield builder.new(object_name, object, self, options, block)
+
+ with_output_buffer do
+ yield builder.new(object_name, object, self, options, block)
+ end
end
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
@@ -1183,9 +1190,11 @@ module ActionView
if association.is_a?(Array)
explicit_child_index = options[:child_index]
- association.map do |child|
- fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block)
- end.join
+ output = ActiveSupport::SafeBuffer.new
+ association.each do |child|
+ output << fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block)
+ end
+ output
elsif association
fields_for_nested_model(name, association, options, block)
end
@@ -1211,8 +1220,10 @@ module ActionView
end
end
- class Base
- cattr_accessor :default_form_builder
- @@default_form_builder = ::ActionView::Helpers::FormBuilder
+ ActionView.base_hook do
+ class ActionView::Base
+ cattr_accessor :default_form_builder
+ @@default_form_builder = ::ActionView::Helpers::FormBuilder
+ 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 7f74be27cb..7039ecd233 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -97,7 +97,9 @@ module ActionView
# </select>
#
module FormOptionsHelper
+ # ERB::Util can mask some helpers like textilize. Make sure to include them.
include ERB::Util
+ include TextHelper
# Create a select tag and a series of contained option tags for the provided object and method.
# The option currently held by the object will be selected, provided that the object is available.
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 7dcaee7e34..573733ffea 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -10,6 +10,11 @@ module ActionView
# NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
# <tt>:disabled => true</tt> will give <tt>disabled="disabled"</tt>.
module FormTagHelper
+ extend ActiveSupport::Concern
+
+ include UrlHelper
+ include TextHelper
+
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
# ActionController::Base#url_for. The method for the form defaults to POST.
#
@@ -441,10 +446,10 @@ module ActionView
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
def field_set_tag(legend = nil, options = nil, &block)
content = capture(&block)
- safe_concat(tag(:fieldset, options, true))
- safe_concat(content_tag(:legend, legend)) unless legend.blank?
- concat(content)
- safe_concat("</fieldset>")
+ output = tag(:fieldset, options, true)
+ output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
+ output.concat(content)
+ output.safe_concat("</fieldset>")
end
private
@@ -477,9 +482,10 @@ module ActionView
def form_tag_in_block(html_options, &block)
content = capture(&block)
- safe_concat(form_tag_html(html_options))
- concat(content)
- safe_concat("</form>")
+ output = ActiveSupport::SafeBuffer.new
+ output.safe_concat(form_tag_html(html_options))
+ output << content
+ output.safe_concat("</form>")
end
def token_tag
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 2c73ff88f7..8dab3094dd 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -83,13 +83,7 @@ module ActionView
content_or_options_with_block
end
- tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
-
- if block_called_from_erb?(block)
- safe_concat(tag)
- else
- tag
- end
+ content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
end
def javascript_cdata_section(content) #:nodoc:
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 3d3502a08b..46e41bc406 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -28,27 +28,25 @@ module ActionView
# number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
# => +1.123.555.1234 x 1343
def number_to_phone(number, options = {})
- number = number.to_s.strip unless number.nil?
+ return nil if number.nil?
+
+ number = number.to_s.strip
options = options.symbolize_keys
area_code = options[:area_code] || nil
delimiter = options[:delimiter] || "-"
extension = options[:extension].to_s.strip || nil
country_code = options[:country_code] || nil
- begin
- str = ""
- str << "+#{country_code}#{delimiter}" unless country_code.blank?
- str << if area_code
- number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
- else
- number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
- number.starts_with?('-') ? number.slice!(1..-1) : number
- end
- str << " x #{extension}" unless extension.blank?
- str
- rescue
- number
+ str = ""
+ str << "+#{country_code}#{delimiter}" unless country_code.blank?
+ str << if area_code
+ number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
+ else
+ number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
+ number.starts_with?('-') ? number.slice!(1..-1) : number
end
+ str << " x #{extension}" unless extension.blank?
+ str
end
# Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
@@ -87,13 +85,14 @@ module ActionView
format = options[:format] || defaults[:format]
separator = '' if precision == 0
- begin
- format.gsub(/%n/, number_with_precision(number,
- :precision => precision,
- :delimiter => delimiter,
- :separator => separator)
- ).gsub(/%u/, unit).html_safe
- rescue
+ value = number_with_precision(number,
+ :precision => precision,
+ :delimiter => delimiter,
+ :separator => separator)
+
+ if value
+ format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
+ else
number
end
end
@@ -122,14 +121,11 @@ module ActionView
separator = options[:separator] || defaults[:separator]
delimiter = options[:delimiter] || defaults[:delimiter]
- begin
- number_with_precision(number,
- :precision => precision,
- :separator => separator,
- :delimiter => delimiter) + "%"
- rescue
- number
- end
+ value = number_with_precision(number,
+ :precision => precision,
+ :separator => separator,
+ :delimiter => delimiter)
+ value ? value + "%" : number
end
# Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
@@ -168,11 +164,11 @@ module ActionView
delimiter ||= (options[:delimiter] || defaults[:delimiter])
separator ||= (options[:separator] || defaults[:separator])
- begin
- parts = number.to_s.split('.')
+ parts = number.to_s.split('.')
+ if parts[0]
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
parts.join(separator)
- rescue
+ else
number
end
end
@@ -216,11 +212,17 @@ module ActionView
delimiter ||= (options[:delimiter] || defaults[:delimiter])
begin
+ value = Float(number)
+ rescue ArgumentError, TypeError
+ value = nil
+ end
+
+ if value
rounded_number = BigDecimal.new((Float(number) * (10 ** precision)).to_s).round.to_f / 10 ** precision
number_with_delimiter("%01.#{precision}f" % rounded_number,
:separator => separator,
:delimiter => delimiter)
- rescue
+ else
number
end
end
@@ -293,17 +295,13 @@ module ActionView
unit_key = STORAGE_UNITS[exponent]
unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
- begin
- escaped_separator = Regexp.escape(separator)
- formatted_number = number_with_precision(number,
- :precision => precision,
- :separator => separator,
- :delimiter => delimiter
- ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
- storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
- rescue
- number
- end
+ escaped_separator = Regexp.escape(separator)
+ formatted_number = number_with_precision(number,
+ :precision => precision,
+ :separator => separator,
+ :delimiter => delimiter
+ ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
+ storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
end
end
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 67a7586699..be49b5cc28 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -182,7 +182,7 @@ module ActionView
def initialize(context, &block) #:nodoc:
context._evaluate_assigns_and_ivars
@context, @lines = context, []
- @context.reset_formats([:js, :html]) do
+ @context.update_details(:formats => [:js, :html]) do
include_helpers_from_context
@context.with_output_buffer(@lines) do
@context.instance_exec(self, &block)
@@ -583,7 +583,7 @@ module ActionView
end
def with_formats(*args)
- @context ? @context.reset_formats(args) { yield } : yield
+ @context ? @context.update_details(:formats => args) { yield } : yield
end
def javascript_object_for(object)
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 513d72880c..d9d2588a2a 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -7,6 +7,9 @@ module ActionView
module TagHelper
include ERB::Util
+ extend ActiveSupport::Concern
+ include CaptureHelper
+
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
autoplay controls loop selected hidden scoped async
defer reversed ismap seemless muted required
@@ -69,13 +72,7 @@ module ActionView
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
if block_given?
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
- content_tag = content_tag_string(name, capture(&block), options, escape)
-
- if block_called_from_erb?(block)
- safe_concat(content_tag)
- else
- content_tag
- end
+ content_tag_string(name, capture(&block), options, escape)
else
content_tag_string(name, content_or_options_with_block, options, escape)
end
@@ -109,21 +106,6 @@ module ActionView
end
private
- BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
-
- if RUBY_VERSION < '1.9.0'
- # Check whether we're called from an erb template.
- # We'd return a string in any other case, but erb <%= ... %>
- # can't take an <% end %> later on, so we have to use <% ... %>
- # and implicitly concat.
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block)
- end
- else
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
- end
- end
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index d9607c0095..148f2868e9 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -544,10 +544,11 @@ module ActionView
# submitted url doesn't have any either. This lets the function
# work with things like ?order=asc
if url_string.index("?")
- request_uri = request.request_uri
+ request_uri = request.fullpath
else
- request_uri = request.request_uri.split('?').first
+ request_uri = request.path
end
+
if url_string =~ /^\w+:\/\//
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
else
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
new file mode 100644
index 0000000000..91885c7370
--- /dev/null
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -0,0 +1,151 @@
+require 'active_support/core_ext/object/try'
+require 'active_support/core_ext/object/blank'
+
+module ActionView
+ # LookupContext is the object responsible to hold all information required to lookup
+ # templates, i.e. view paths and details. The LookupContext is also responsible to
+ # generate a key, given to view paths, used in the resolver cache lookup. Since
+ # this key is generated just once during the request, it speeds up all cache accesses.
+ class LookupContext #:nodoc:
+ mattr_accessor :fallbacks
+ @@fallbacks = [FileSystemResolver.new(""), FileSystemResolver.new("/")]
+
+ mattr_accessor :registered_details
+ self.registered_details = {}
+
+ def self.register_detail(name, options = {})
+ registered_details[name] = lambda do |value|
+ value = Array(value.presence || yield)
+ value |= [nil] unless options[:allow_nil] == false
+ value
+ end
+ end
+
+ register_detail(:formats) { Mime::SET.symbols }
+ register_detail(:locale) { [I18n.locale] }
+
+ class DetailsKey #:nodoc:
+ attr_reader :details
+ alias :eql? :equal?
+
+ @details_keys = Hash.new
+
+ def self.get(details)
+ @details_keys[details] ||= new(details)
+ end
+
+ def initialize(details)
+ @details, @hash = details, details.hash
+ end
+ end
+
+ def initialize(view_paths, details = {})
+ @details_key = nil
+ self.view_paths = view_paths
+ self.details = details
+ end
+
+ module ViewPaths
+ attr_reader :view_paths
+
+ # Whenever setting view paths, makes a copy so we can manipulate then in
+ # instance objects as we wish.
+ def view_paths=(paths)
+ @view_paths = ActionView::Base.process_view_paths(paths)
+ end
+
+ def find(name, prefix = nil, partial = false)
+ @view_paths.find(name, prefix, partial || false, details, details_key)
+ end
+
+ def find_all(name, prefix = nil, partial = false)
+ @view_paths.find_all(name, prefix, partial || false, details, details_key)
+ end
+
+ def exists?(name, prefix = nil, partial = false)
+ @view_paths.exists?(name, prefix, partial || false, details, details_key)
+ end
+
+ # Add fallbacks to the view paths. Useful in cases you are rendering a file.
+ def with_fallbacks
+ added_resolvers = 0
+ self.class.fallbacks.each do |resolver|
+ next if view_paths.include?(resolver)
+ view_paths.push(resolver)
+ added_resolvers += 1
+ end
+ yield
+ ensure
+ added_resolvers.times { view_paths.pop }
+ end
+ end
+
+ module Details
+ attr_reader :details
+
+ def details=(details)
+ @details = normalize_details(details)
+ @details_key = nil if @details_key && @details_key.details != @details
+ end
+
+ def details_key
+ @details_key ||= DetailsKey.get(@details)
+ end
+
+ # Shortcut to read formats from details.
+ def formats
+ @details[:formats].compact
+ end
+
+ # Shortcut to set formats in details.
+ def formats=(value)
+ self.details = @details.merge(:formats => value)
+ end
+
+ # Shortcut to read locale.
+ def locale
+ I18n.locale
+ end
+
+ # Shortcut to set locale in details and I18n.
+ def locale=(value)
+ I18n.locale = value
+
+ unless I18n.config.respond_to?(:lookup_context)
+ self.details = @details.merge(:locale => value)
+ end
+ end
+
+ # Update the details keys by merging the given hash into the current
+ # details hash. If a block is given, the details are modified just during
+ # the execution of the block and reverted to the previous value after.
+ def update_details(new_details)
+ old_details = @details
+ self.details = old_details.merge(new_details)
+
+ if block_given?
+ begin
+ yield
+ ensure
+ self.details = old_details
+ end
+ end
+ end
+
+ protected
+
+ def normalize_details(details)
+ details = details.dup
+ # TODO: Refactor this concern out of the resolver
+ details.delete(:formats) if details[:formats] == [:"*/*"]
+ self.class.registered_details.each do |k, v|
+ details[k] = v.call(details[k])
+ end
+ details.freeze
+ end
+ end
+
+ include Details
+ include ViewPaths
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 6e55d69d00..35927d09d1 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -1,80 +1,39 @@
module ActionView #:nodoc:
class PathSet < Array #:nodoc:
- def self.type_cast(obj, cache = nil)
- # TODO: Clean this up
- if obj.is_a?(String)
- if cache.nil?
- cache = !defined?(Rails.application) || Rails.application.config.cache_classes
+ %w(initialize << concat insert push unshift).each do |method|
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{method}(*args)
+ super
+ typecast!
end
- FileSystemResolverWithFallback.new(obj, :cache => cache)
- else
- obj
- end
- end
-
- def initialize(*args)
- super(*args).map! { |obj| self.class.type_cast(obj) }
- end
-
- def <<(obj)
- super(self.class.type_cast(obj))
- end
-
- def concat(array)
- super(array.map! { |obj| self.class.type_cast(obj) })
- end
-
- def insert(index, obj)
- super(index, self.class.type_cast(obj))
- end
-
- def push(*objs)
- super(*objs.map { |obj| self.class.type_cast(obj) })
+ METHOD
end
- def unshift(*objs)
- super(*objs.map { |obj| self.class.type_cast(obj) })
+ def find(path, prefix = nil, partial = false, details = {}, key = nil)
+ template = find_all(path, prefix, partial, details, key).first
+ raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template
+ template
end
- def find(path, details = {}, prefix = nil, partial = false)
- template_path = path
-
- each do |load_path|
- if template = load_path.find(template_path, details, prefix, partial)
- return template
- end
+ def find_all(*args)
+ each do |resolver|
+ templates = resolver.find_all(*args)
+ return templates unless templates.empty?
end
-
- raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path}", details, partial)
+ []
end
-
- def exists?(path, extension = nil, prefix = nil, partial = false)
- template_path = path.sub(/^\//, '')
- each do |load_path|
- return true if template = load_path.find(template_path, extension, prefix, partial)
- end
- false
+ def exists?(*args)
+ find_all(*args).any?
end
- def find_template(original_template_path, format = nil, html_fallback = true)
- return original_template_path if original_template_path.respond_to?(:render)
- template_path = original_template_path.sub(/^\//, '')
+ protected
- each do |load_path|
- if template = load_path.find(template_path, format)
- return template
- # Try to find html version if the format is javascript
- elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"]
- return template
- elsif format == :js && html_fallback && template = load_path["#{template_path}.html"]
- return template
- end
+ def typecast!
+ each_with_index do |path, i|
+ next unless path.is_a?(String)
+ self[i] = FileSystemResolver.new(path)
end
-
- return Template.new(original_template_path, original_template_path.to_s =~ /\A\// ? "" : ".") if File.file?(original_template_path)
-
- raise MissingTemplate.new(self, original_template_path, format)
end
end
end
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 03f18ac172..2e5d115630 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -10,7 +10,9 @@ module ActionView
initializer "action_view.cache_asset_timestamps" do |app|
unless app.config.cache_classes
- ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ ActionView.base_hook do
+ ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ end
end
end
end
diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb
new file mode 100644
index 0000000000..8688de3d18
--- /dev/null
+++ b/actionpack/lib/action_view/render/layouts.rb
@@ -0,0 +1,65 @@
+require 'active_support/core_ext/object/try'
+
+module ActionView
+ module Layouts
+
+ # You can think of a layout as a method that is called with a block. _layout_for
+ # returns the contents that are yielded to the layout. If the user calls yield
+ # :some_name, the block, by default, returns content_for(:some_name). If the user
+ # calls yield, the default block returns content_for(:layout).
+ #
+ # The user can override this default by passing a block to the layout.
+ #
+ # ==== Example
+ #
+ # # The template
+ # <% render :layout => "my_layout" do %>Content<% end %>
+ #
+ # # The layout
+ # <html><% yield %></html>
+ #
+ # In this case, instead of the default block, which would return content_for(:layout),
+ # this method returns the block that was passed in to render layout, and the response
+ # would be <html>Content</html>.
+ #
+ # Finally, the block can take block arguments, which can be passed in by yield.
+ #
+ # ==== Example
+ #
+ # # The template
+ # <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %>
+ #
+ # # The layout
+ # <html><% yield Struct.new(:name).new("David") %></html>
+ #
+ # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
+ # and the Struct specified in the layout would be passed into the block. The result
+ # would be <html>Hello David</html>.
+ def _layout_for(name = nil, &block) #:nodoc:
+ if !block || name
+ @_content_for[name || :layout]
+ else
+ capture(&block)
+ end
+ end
+
+ # This is the method which actually finds the layout using details in the lookup
+ # context object. If no layout is found, it checkes if at least a layout with
+ # the given name exists across all details before raising the error.
+ def _find_layout(layout) #:nodoc:
+ begin
+ layout =~ /^\// ?
+ with_fallbacks { find(layout) } : find(layout)
+ rescue ActionView::MissingTemplate => e
+ update_details(:formats => nil) do
+ raise unless exists?(layout)
+ end
+ end
+ end
+
+ # Contains the logic that actually renders the layout.
+ def _render_layout(layout, locals, &block) #:nodoc:
+ layout.render(self, locals){ |*name| _layout_for(*name, &block) }
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 8b6dce0c1c..950c9d2cd8 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -174,17 +174,11 @@ module ActionView
class PartialRenderer
PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
- TEMPLATES = Hash.new {|h,k| h[k] = {} }
-
- attr_reader :template
def initialize(view_context, options, block)
@view = view_context
@partial_names = PARTIAL_NAMES[@view.controller.class]
- key = Thread.current[:format_locale_key]
- @templates = TEMPLATES[key] if key
-
setup(options, block)
end
@@ -228,6 +222,7 @@ module ActionView
if !@block && (layout = @options[:layout])
content = @view._render_layout(find_template(layout), @locals){ content }
end
+
content
end
end
@@ -244,9 +239,9 @@ module ActionView
end
def collection_with_template(template = @template)
- segments, locals, as = [], @locals, @options[:as] || template.variable_name
+ segments, locals, as, template = [], @locals, @options[:as] || @template.variable_name, @template
- counter_name = template.counter_name
+ counter_name = template.counter_name
locals[counter_name] = -1
@collection.each do |object|
@@ -256,7 +251,6 @@ module ActionView
segments << template.render(@view, locals)
end
- @template = template
segments
end
@@ -277,7 +271,7 @@ module ActionView
end
def render_partial(object = @object)
- locals, view = @locals, @view
+ locals, view, template = @locals, @view, @template
object ||= locals[template.variable_name]
locals[@options[:as] || template.variable_name] = object
@@ -288,6 +282,7 @@ module ActionView
end
private
+
def collection
if @object.respond_to?(:to_ary)
@object
@@ -296,20 +291,10 @@ module ActionView
end
end
- def find_template(path = @path)
- unless @templates
- path && _find_template(path)
- else
- path && @templates[path] ||= _find_template(path)
- end
- end
-
- def _find_template(path)
- if controller = @view.controller
- prefix = controller.controller_path unless path.include?(?/)
- end
-
- @view.find(path, {:formats => @view.formats}, prefix, true)
+ def find_template(path=@path)
+ return path unless path.is_a?(String)
+ prefix = @view.controller_path unless path.include?(?/)
+ @view.find(path, prefix, true)
end
def partial_path(object = @object)
@@ -324,21 +309,8 @@ module ActionView
end
end
- def render_partial(options)
- _evaluate_assigns_and_ivars
-
- details = options[:_details]
-
- # Is this needed
- self.formats = details[:formats] if details
- renderer = PartialRenderer.new(self, options, nil)
- text = renderer.render
- options[:_template] = renderer.template
- text
- end
-
def _render_partial(options, &block) #:nodoc:
- if defined? @renderer
+ if defined?(@renderer)
@renderer.setup(options, block)
else
@renderer = PartialRenderer.new(self, options, block)
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 64cc0caf11..47ea70f5ad 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -15,27 +15,11 @@ module ActionView
def render(options = {}, locals = {}, &block) #:nodoc:
case options
when Hash
- layout = options[:layout]
- options[:locals] ||= {}
-
if block_given?
- return safe_concat(_render_partial(options.merge(:partial => layout), &block))
- elsif options.key?(:partial)
- return _render_partial(options)
- end
-
- template = if options[:file]
- find(options[:file], details_for_render)
- elsif options[:inline]
- handler = Template.handler_class_for_extension(options[:type] || "erb")
- Template.new(options[:inline], "inline template", handler, {})
- elsif options[:text]
- Template::Text.new(options[:text])
- end
-
- if template
- layout = find(layout, details_for_render) if layout
- _render_template(template, layout, :locals => options[:locals])
+ content = _render_partial(options.merge(:partial => options[:layout]), &block)
+ safe_concat(content)
+ else
+ _render(options)
end
when :update
update_page(&block)
@@ -44,65 +28,57 @@ module ActionView
end
end
- def details_for_render
- controller.try(:details_for_render) || {:formats => formats}
+ # This is the API to render a ViewContext's template from a controller.
+ def render_template(options, &block)
+ _evaluate_assigns_and_ivars
+
+ # TODO Layout for partials should be handled here, because inside the
+ # partial renderer it looks for the layout as a partial.
+ if options.key?(:partial) && options[:layout]
+ options[:layout] = _find_layout(options[:layout])
+ end
+
+ _render(options, &block)
end
- # You can think of a layout as a method that is called with a block. _layout_for
- # returns the contents that are yielded to the layout. If the user calls yield
- # :some_name, the block, by default, returns content_for(:some_name). If the user
- # calls yield, the default block returns content_for(:layout).
- #
- # The user can override this default by passing a block to the layout.
- #
- # ==== Example
- #
- # # The template
- # <% render :layout => "my_layout" do %>Content<% end %>
- #
- # # The layout
- # <html><% yield %></html>
- #
- # In this case, instead of the default block, which would return content_for(:layout),
- # this method returns the block that was passed in to render layout, and the response
- # would be <html>Content</html>.
- #
- # Finally, the block can take block arguments, which can be passed in by yield.
- #
- # ==== Example
- #
- # # The template
- # <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %>
- #
- # # The layout
- # <html><% yield Struct.new(:name).new("David") %></html>
- #
- # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
- # and the Struct specified in the layout would be passed into the block. The result
- # would be <html>Hello David</html>.
- def _layout_for(name = nil, &block)
- return @_content_for[name || :layout] if !block_given? || name
- capture(&block)
+ # This method holds the common render logic for both controllers and
+ # views rendering stacks.
+ def _render(options) #:nodoc:
+ if options.key?(:partial)
+ _render_partial(options)
+ else
+ template = _determine_template(options)
+ yield template if block_given?
+ _render_template(template, options[:layout], options)
+ end
end
- # This is the API to render a ViewContext's template from a controller.
- #
- # Internal Options:
- # _template:: The Template object to render
- # _layout:: The layout, if any, to wrap the Template in
- def render_template(options)
- _evaluate_assigns_and_ivars
- template, layout = options.values_at(:_template, :_layout)
- _render_template(template, layout, options)
+ # Determine the template to be rendered using the given options.
+ def _determine_template(options) #:nodoc:
+ if options.key?(:inline)
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ Template.new(options[:inline], "inline template", handler, {})
+ elsif options.key?(:text)
+ Template::Text.new(options[:text], self.formats.try(:first))
+ elsif options.key?(:_template)
+ options[:_template]
+ elsif options.key?(:file)
+ with_fallbacks { find(options[:file], options[:prefix]) }
+ elsif options.key?(:template)
+ find(options[:template], options[:prefix])
+ end
end
- def _render_template(template, layout = nil, options = {})
+ # Renders the given template. An string representing the layout can be
+ # supplied as well.
+ def _render_template(template, layout = nil, options = {}) #:nodoc:
locals = options[:locals] || {}
+ layout = _find_layout(layout) if layout
ActiveSupport::Notifications.instrument("action_view.render_template",
:identifier => template.identifier, :layout => layout.try(:identifier)) do
- content = template.render(self, locals)
+ content = template.render(self, locals) { |*name| _layout_for(*name) }
@_content_for[:layout] = content
if layout
@@ -114,8 +90,5 @@ module ActionView
end
end
- def _render_layout(layout, locals, &block)
- layout.render(self, locals){ |*name| _layout_for(*name, &block) }
- end
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index cd6b1930a1..b4fdb49d3b 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -16,23 +16,22 @@ module ActionView
end
extend Template::Handlers
- attr_reader :source, :identifier, :handler, :mime_type, :formats, :details
+
+ attr_reader :source, :identifier, :handler, :virtual_path, :formats
def initialize(source, identifier, handler, details)
@source = source
@identifier = identifier
@handler = handler
- @details = details
+
+ @partial = details[:partial]
+ @virtual_path = details[:virtual_path]
@method_names = {}
- format = details.delete(:format) || begin
- # TODO: Clean this up
- handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
- end
- @mime_type = Mime::Type.lookup_by_extension(format.to_s)
- @formats = [format.to_sym]
- @formats << :html if format == :js
- @details[:formats] = Array.wrap(format.to_sym)
+ format = details[:format]
+ format ||= handler.default_format.to_sym if handler.respond_to?(:default_format)
+ format ||= :html
+ @formats = [format.to_sym]
end
def render(view, locals, &block)
@@ -47,19 +46,20 @@ module ActionView
end
end
- # TODO: Figure out how to abstract this
+ def mime_type
+ @mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
+ end
+
def variable_name
- @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ @variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
end
- # TODO: Figure out how to abstract this
def counter_name
@counter_name ||= "#{variable_name}_counter".to_sym
end
- # TODO: kill hax
def partial?
- @details[:partial]
+ @partial
end
def inspect
@@ -87,7 +87,7 @@ module ActionView
source = <<-end_src
def #{method_name}(local_assigns)
- _old_virtual_path, @_virtual_path = @_virtual_path, #{@details[:virtual_path].inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
+ _old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
ensure
@_virtual_path, self.output_buffer = _old_virtual_path, _old_output_buffer
end
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 4b7cec50f3..937694ce8e 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -3,10 +3,54 @@ require 'active_support/core_ext/string/output_safety'
require 'erubis'
module ActionView
+ class OutputBuffer
+ def initialize
+ @buffer = ActiveSupport::SafeBuffer.new
+ end
+
+ def safe_concat(value)
+ @buffer.safe_concat(value)
+ end
+
+ def <<(value)
+ @buffer << value.to_s
+ end
+
+ def length
+ @buffer.length
+ end
+
+ def [](*args)
+ @buffer[*args]
+ end
+
+ def to_s
+ @buffer.to_s
+ end
+
+ def to_str
+ @buffer.to_str
+ end
+
+ def empty?
+ @buffer.empty?
+ end
+
+ def html_safe?
+ @buffer.html_safe?
+ end
+
+ if "".respond_to?(:force_encoding)
+ def force_encoding(encoding)
+ @buffer.force_encoding(encoding)
+ end
+ end
+ end
+
module Template::Handlers
class Erubis < ::Erubis::Eruby
def add_preamble(src)
- src << "@output_buffer = ActiveSupport::SafeBuffer.new;"
+ src << "@output_buffer = ActionView::OutputBuffer.new;"
end
def add_text(src, text)
@@ -15,10 +59,10 @@ module ActionView
end
def add_expr_literal(src, code)
- if code =~ /\s*raw\s+(.*)/
- src << "@output_buffer.safe_concat((" << $1 << ").to_s);"
+ if code =~ /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
+ src << '@output_buffer << ' << code
else
- src << '@output_buffer << ((' << code << ').to_s);'
+ src << '@output_buffer << (' << code << ');'
end
end
@@ -42,7 +86,7 @@ module ActionView
self.erb_trim_mode = '-'
self.default_format = Mime::HTML
-
+
cattr_accessor :erb_implementation
self.erb_implementation = Erubis
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 20402f9d6d..a43597e728 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -4,64 +4,47 @@ require "active_support/core_ext/array/wrap"
require "action_view/template"
module ActionView
- # Abstract superclass
class Resolver
-
- class_inheritable_accessor(:registered_details)
- self.registered_details = {}
-
- def self.register_detail(name, options = {})
- registered_details[name] = lambda do |val|
- val = Array.wrap(val || yield)
- val |= [nil] unless options[:allow_nil] == false
- val
- end
- end
-
- register_detail(:locale) { [I18n.locale] }
- register_detail(:formats) { Mime::SET.symbols }
- register_detail(:handlers) do
- Template::Handlers.extensions
+ def initialize
+ @cached = Hash.new { |h1,k1| h1[k1] =
+ Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
end
- def initialize(options = {})
- @cache = options[:cache]
- @cached = {}
+ def clear_cache
+ @cached.clear
end
- # Normalizes the arguments and passes it on to find_template
def find(*args)
find_all(*args).first
end
- def find_all(name, details = {}, prefix = nil, partial = nil)
- details = normalize_details(details)
+ # Normalizes the arguments and passes it on to find_template.
+ def find_all(name, prefix=nil, partial=false, details={}, key=nil)
name, prefix = normalize_name(name, prefix)
+ details = details.merge(:handlers => default_handlers)
- cached([name, details, prefix, partial]) do
- find_templates(name, details, prefix, partial)
+ cached(key, prefix, name, partial) do
+ find_templates(name, prefix, partial, details)
end
end
private
+ def caching?
+ @caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes
+ end
+
+ def default_handlers
+ Template::Handlers.extensions + [nil]
+ end
+
# This is what child classes implement. No defaults are needed
# because Resolver guarantees that the arguments are present and
# normalized.
- def find_templates(name, details, prefix, partial)
+ def find_templates(name, prefix, partial, details)
raise NotImplementedError
end
- def normalize_details(details)
- details = details.dup
- # TODO: Refactor this concern out of the resolver
- details.delete(:formats) if details[:formats] == [:"*/*"]
- registered_details.each do |k, v|
- details[k] = v.call(details[k])
- end
- details
- end
-
# Support legacy foo.erb names even though we now ignore .erb
# as well as incorrectly putting part of the path in the template
# name instead of the prefix.
@@ -73,92 +56,73 @@ module ActionView
return parts.pop, [prefix, *parts].compact.join("/")
end
- def cached(key)
- return yield unless @cache
- return @cached[key] if @cached.key?(key)
- @cached[key] = yield
+ def cached(key, prefix, name, partial)
+ return yield unless key && caching?
+ scope = @cached[key][prefix][name]
+ if scope.key?(partial)
+ scope[partial]
+ else
+ scope[partial] = yield
+ end
end
end
class PathResolver < Resolver
-
EXTENSION_ORDER = [:locale, :formats, :handlers]
def to_s
@path.to_s
end
- alias to_path to_s
-
- def find_templates(name, details, prefix, partial)
- path = build_path(name, details, prefix, partial)
- query(path, EXTENSION_ORDER.map { |ext| details[ext] })
- end
+ alias :to_path :to_s
private
- def build_path(name, details, prefix, partial)
+ def find_templates(name, prefix, partial, details)
+ path = build_path(name, prefix, partial, details)
+ query(partial, path, EXTENSION_ORDER.map { |ext| details[ext] })
+ end
+
+ def build_path(name, prefix, partial, details)
path = ""
path << "#{prefix}/" unless prefix.empty?
path << (partial ? "_#{name}" : name)
path
end
- def query(path, exts)
+ def query(partial, path, exts)
query = File.join(@path, path)
+
exts.each do |ext|
query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}'
end
Dir[query].reject { |p| File.directory?(p) }.map do |p|
- Template.new(File.read(p), File.expand_path(p), *path_to_details(p))
+ handler, format = extract_handler_and_format(p)
+ Template.new(File.read(p), File.expand_path(p), handler,
+ :partial => partial, :virtual_path => path, :format => format)
end
end
- # # TODO: fix me
- # # :api: plugin
- def path_to_details(path)
- # [:erb, :format => :html, :locale => :en, :partial => true/false]
- if m = path.match(%r'((^|.*/)(_)?[\w-]+)((?:\.[\w-]+)*)\.(\w+)$')
- partial = m[3] == '_'
- details = (m[4]||"").split('.').reject { |e| e.empty? }
- handler = Template.handler_class_for_extension(m[5])
+ def extract_handler_and_format(path)
+ pieces = File.basename(path).split(".")
+ pieces.shift
- format = Mime[details.last] && details.pop.to_sym
- locale = details.last && details.pop.to_sym
-
- virtual_path = (m[1].gsub("#{@path}/", "") << details.join("."))
-
- return handler, :format => format, :locale => locale, :partial => partial,
- :virtual_path => virtual_path
- end
+ handler = Template.handler_class_for_extension(pieces.pop)
+ format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
+ [handler, format]
end
end
class FileSystemResolver < PathResolver
- def initialize(path, options = {})
+ def initialize(path)
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
- super(options)
+ super()
@path = Pathname.new(path).expand_path
end
- end
-
- # TODO: remove hack
- class FileSystemResolverWithFallback < Resolver
- def initialize(path, options = {})
- super(options)
- @paths = [FileSystemResolver.new(path, options), FileSystemResolver.new("", options), FileSystemResolver.new("/", options)]
- end
- def find_templates(*args)
- @paths.each do |p|
- template = p.find_templates(*args)
- return template unless template.empty?
- end
- []
- end
-
- def to_s
- @paths.first.to_s
+ def eql?(resolver)
+ self.class.equal?(resolver.class) && to_path == resolver.to_path
end
+ alias :== :eql?
end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 2abb352d4e..df394b0fb0 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -1,15 +1,10 @@
module ActionView #:nodoc:
class Template
class Text < String #:nodoc:
- HTML = Mime[:html]
-
- def initialize(string, content_type = HTML)
+ def initialize(string, content_type = nil)
super(string.to_s)
- @content_type = Mime[content_type] || content_type
- end
-
- def details
- {:formats => [@content_type.to_sym]}
+ @content_type = Mime[content_type] || content_type if content_type
+ @content_type ||= Mime::TEXT
end
def identifier
@@ -29,7 +24,7 @@ module ActionView #:nodoc:
end
def formats
- [mime_type]
+ [@content_type.to_sym]
end
def partial?
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index f639700eaf..1578ac9479 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -1,4 +1,5 @@
require 'action_controller/test_case'
+require 'action_view'
module ActionView
class Base
@@ -34,6 +35,8 @@ module ActionView
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
+ @request.env.delete('PATH_INFO')
+
@params = {}
end
end
@@ -60,6 +63,10 @@ module ActionView
make_test_case_available_to_view!
end
+ def config
+ @controller.config
+ end
+
def render(options = {}, local_assigns = {}, &block)
@rendered << output = _view.render(options, local_assigns, &block)
output