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.rb104
-rw-r--r--actionpack/lib/action_view/helpers.rb39
-rw-r--r--actionpack/lib/action_view/helpers/active_record_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb24
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb96
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb54
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb2
-rw-r--r--actionpack/lib/action_view/locale/en-US.rb53
-rw-r--r--actionpack/lib/action_view/locale/en-US.yml91
-rw-r--r--actionpack/lib/action_view/partials.rb137
-rw-r--r--actionpack/lib/action_view/paths.rb28
-rw-r--r--actionpack/lib/action_view/renderable.rb11
-rw-r--r--actionpack/lib/action_view/renderable_partial.rb6
-rw-r--r--actionpack/lib/action_view/template.rb2
-rw-r--r--actionpack/lib/action_view/template_error.rb2
-rw-r--r--actionpack/lib/action_view/test_case.rb4
18 files changed, 438 insertions, 235 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index ad59d92086..c65048bfa0 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -158,6 +158,7 @@ module ActionView #:nodoc:
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
class Base
include ERB::Util
+ extend ActiveSupport::Memoizable
attr_accessor :base_path, :assigns, :template_extension
attr_accessor :controller
@@ -169,6 +170,7 @@ module ActionView #:nodoc:
class << self
delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB'
+ delegate :logger, :to => 'ActionController::Base'
end
def self.cache_template_loading=(*args)
@@ -202,27 +204,28 @@ module ActionView #:nodoc:
end
include CompiledTemplates
- def self.helper_modules #:nodoc:
- helpers = []
- Dir.entries(File.expand_path("#{File.dirname(__FILE__)}/helpers")).sort.each do |file|
- next unless file =~ /^([a-z][a-z_]*_helper).rb$/
- require "action_view/helpers/#{$1}"
- helper_module_name = $1.camelize
- if Helpers.const_defined?(helper_module_name)
- helpers << Helpers.const_get(helper_module_name)
- end
- end
- return helpers
- end
-
def self.process_view_paths(value)
ActionView::PathSet.new(Array(value))
end
+ attr_reader :helpers
+
+ class ProxyModule < Module
+ def initialize(receiver)
+ @receiver = receiver
+ end
+
+ def include(*args)
+ super(*args)
+ @receiver.extend(*args)
+ end
+ end
+
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
@assigns = assigns_for_first_render
@assigns_added = nil
@controller = controller
+ @helpers = ProxyModule.new(self)
self.view_paths = view_paths
end
@@ -238,7 +241,7 @@ module ActionView #:nodoc:
local_assigns ||= {}
if options.is_a?(String)
- render_file(options, nil, local_assigns)
+ render(:file => options, :locals => local_assigns)
elsif options == :update
update_page(&block)
elsif options.is_a?(Hash)
@@ -246,31 +249,34 @@ module ActionView #:nodoc:
if partial_layout = options.delete(:layout)
if block_given?
- wrap_content_for_layout capture(&block) do
+ begin
+ @_proc_for_layout = block
concat(render(options.merge(:partial => partial_layout)))
+ ensure
+ @_proc_for_layout = nil
end
else
- wrap_content_for_layout render(options) do
+ begin
+ original_content_for_layout, @content_for_layout = @content_for_layout, render(options)
render(options.merge(:partial => partial_layout))
+ ensure
+ @content_for_layout = original_content_for_layout
end
end
elsif options[:file]
- render_file(options[:file], nil, options[:locals])
- elsif options[:partial] && options[:collection]
- render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals], options[:as])
+ if options[:use_full_path]
+ ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller)
+ end
+
+ pick_template(options[:file]).render_template(self, options[:locals])
elsif options[:partial]
- render_partial(options[:partial], options[:object], options[:locals])
+ render_partial(options)
elsif options[:inline]
- render_inline(options[:inline], options[:locals], options[:type])
+ InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals])
end
end
end
- # Returns true is the file may be rendered implicitly.
- def file_public?(template_path)#:nodoc:
- template_path.split('/').last[0,1] != '_'
- end
-
# The format to be used when choosing between multiple templates with
# the same name but differing formats. See +Request#template_format+
# for more details.
@@ -322,7 +328,7 @@ module ActionView #:nodoc:
else
template = Template.new(template_path, view_paths)
- if self.class.warn_cache_misses && logger = ActionController::Base.logger
+ if self.class.warn_cache_misses && 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 " +
@@ -333,47 +339,9 @@ module ActionView #:nodoc:
template
end
end
-
- extend ActiveSupport::Memoizable
memoize :pick_template
private
- # 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.is_a?(String) && !template_path.include?("/")
- raise ActionViewError, <<-END_ERROR
- Due to changes in ActionMailer, you need to provide the mailer_name along with the template name.
-
- render "user_mailer/signup"
- render :file => "user_mailer/signup"
-
- If you are rendering a subtemplate, you must now use controller-like partial syntax:
-
- render :partial => 'signup' # no mailer_name necessary
- END_ERROR
- end
-
- template = pick_template(template_path)
- template.render_template(self, local_assigns)
- end
-
- def render_inline(text, local_assigns = {}, type = nil)
- InlineTemplate.new(text, type).render(self, local_assigns)
- end
-
- def wrap_content_for_layout(content)
- original_content_for_layout, @content_for_layout = @content_for_layout, content
- yield
- ensure
- @content_for_layout = original_content_for_layout
- end
-
# Evaluate the local assigns and pushes them to the view.
def evaluate_assigns
unless @assigns_added
@@ -392,11 +360,5 @@ module ActionView #:nodoc:
controller.response.content_type ||= content_type
end
end
-
- def execute(method, local_assigns = {})
- send(method, local_assigns) do |*names|
- instance_variable_get "@content_for_#{names.first || 'layout'}"
- end
- end
end
end
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
new file mode 100644
index 0000000000..05e1cf990a
--- /dev/null
+++ b/actionpack/lib/action_view/helpers.rb
@@ -0,0 +1,39 @@
+Dir.entries(File.expand_path("#{File.dirname(__FILE__)}/helpers")).sort.each do |file|
+ next unless file =~ /^([a-z][a-z_]*_helper).rb$/
+ require "action_view/helpers/#{$1}"
+end
+
+module ActionView #:nodoc:
+ module Helpers #:nodoc:
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ include SanitizeHelper::ClassMethods
+ end
+
+ include ActiveRecordHelper
+ include AssetTagHelper
+ include AtomFeedHelper
+ include BenchmarkHelper
+ include CacheHelper
+ include CaptureHelper
+ include DateHelper
+ include DebugHelper
+ include FormCountryHelper
+ include FormHelper
+ include FormOptionsHelper
+ include FormTagHelper
+ include NumberHelper
+ include PrototypeHelper
+ include RecordIdentificationHelper
+ include RecordTagHelper
+ include SanitizeHelper
+ include ScriptaculousHelper
+ include TagHelper
+ include TextHelper
+ include TranslationHelper
+ include UrlHelper
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb
index fce03ff605..c339e10701 100644
--- a/actionpack/lib/action_view/helpers/active_record_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_record_helper.rb
@@ -189,15 +189,15 @@ module ActionView
end
options[:object_name] ||= params.first
- I18n.with_options :locale => options[:locale], :scope => [:active_record, :error] do |locale|
+ I18n.with_options :locale => options[:locale], :scope => [:activerecord, :errors, :template] do |locale|
header_message = if options.include?(:header_message)
options[:header_message]
else
object_name = options[:object_name].to_s.gsub('_', ' ')
- object_name = I18n.t(object_name, :default => object_name)
- locale.t :header_message, :count => count, :object_name => object_name
+ object_name = I18n.t(object_name, :default => object_name, :scope => [:activerecord, :models], :count => 1)
+ locale.t :header, :count => count, :model => object_name
end
- message = options.include?(:message) ? options[:message] : locale.t(:message)
+ message = options.include?(:message) ? options[:message] : locale.t(:body)
error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join
contents = ''
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index c2b4f51c9c..623ed1e8df 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -612,7 +612,7 @@ module ActionView
end
def join_asset_file_contents(paths)
- paths.collect { |path| File.read(File.join(ASSETS_DIR, path.split("?").first)) }.join("\n\n")
+ paths.collect { |path| File.read(asset_file_path(path)) }.join("\n\n")
end
def write_asset_file_contents(joined_asset_path, asset_paths)
@@ -621,10 +621,14 @@ module ActionView
# Set mtime to the latest of the combined files to allow for
# consistent ETag without a shared filesystem.
- mt = asset_paths.map { |p| File.mtime(File.join(ASSETS_DIR, p)) }.max
+ mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
File.utime(mt, mt, joined_asset_path)
end
+ def asset_file_path(path)
+ File.join(ASSETS_DIR, path.split('?').first)
+ end
+
def collect_asset_files(*path)
dir = path.first
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index ebb1cb34bc..e65d5d1f60 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -17,7 +17,7 @@ module ActionView
# # GET /posts.atom
# def index
# @posts = Post.find(:all)
- #
+ #
# respond_to do |format|
# format.html
# format.atom
@@ -29,12 +29,12 @@ module ActionView
# atom_feed do |feed|
# feed.title("My great blog!")
# feed.updated((@posts.first.created_at))
- #
+ #
# for post in @posts
# feed.entry(post) do |entry|
# entry.title(post.title)
# entry.content(post.body, :type => 'html')
- #
+ #
# entry.author do |author|
# author.name("DHH")
# end
@@ -47,8 +47,9 @@ module ActionView
# * <tt>:language</tt>: Defaults to "en-US".
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
- # * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
- # created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
+ # * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}"
+ # * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
+ # created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
# 2005 is used (as an "I don't care" value).
#
# Other namespaces can be added to the root element:
@@ -81,7 +82,7 @@ module ActionView
else
options[:schema_date] = "2005" # The Atom spec copyright date
end
-
+
xml = options[:xml] || eval("xml", block.binding)
xml.instruct!
@@ -89,10 +90,10 @@ module ActionView
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
xml.feed(feed_opts) do
- xml.id("tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
+ xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.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)
-
+
yield AtomFeedBuilder.new(xml, self, options)
end
end
@@ -102,7 +103,7 @@ module ActionView
def initialize(xml, view, feed_options = {})
@xml, @view, @feed_options = xml, view, feed_options
end
-
+
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
def updated(date_or_time = nil)
@xml.updated((date_or_time || Time.now.utc).xmlschema)
@@ -115,9 +116,10 @@ module ActionView
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
+ # * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
def entry(record, options = {})
- @xml.entry do
- @xml.id("tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
+ @xml.entry do
+ @xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
@xml.published((options[:published] || record.created_at).xmlschema)
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 9cb2ed9bca..e452b04b31 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -588,9 +588,7 @@ module ActionView
private
def include_helpers_from_context
- @context.extended_by.each do |mod|
- extend mod unless mod.name =~ /^ActionView::Helpers/
- end
+ extend @context.helpers if @context.respond_to?(:helpers)
extend GeneratorMethods
end
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index c3c03394ee..435ba936e1 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -1,22 +1,27 @@
require 'action_view/helpers/tag_helper'
-require 'html/document'
+
+begin
+ require 'html/document'
+rescue LoadError
+ html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner"
+ if File.directory?(html_scanner_path)
+ $:.unshift html_scanner_path
+ require 'html/document'
+ end
+end
module ActionView
module Helpers #:nodoc:
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
# These helper methods extend ActionView making them callable within your template files.
module SanitizeHelper
- def self.included(base)
- base.extend(ClassMethods)
- end
-
# This +sanitize+ helper will html encode all tags and strip all attributes that aren't specifically allowed.
# It also strips href/src tags with invalid protocols, like javascript: especially. It does its best to counter any
# tricks that hackers may use, like throwing in unicode/ascii/hex values to get past the javascript: filters. Check out
# the extensive test suite.
#
# <%= sanitize @article.body %>
- #
+ #
# You can add or remove tags/attributes if you want to customize it a bit. See ActionView::Base for full docs on the
# available options. You can add tags/attributes for single uses of +sanitize+ by passing either the <tt>:attributes</tt> or <tt>:tags</tt> options:
#
@@ -27,27 +32,27 @@ module ActionView
# Custom Use (only the mentioned tags and attributes are allowed, nothing else)
#
# <%= sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style)
- #
+ #
# Add table tags to the default allowed tags
- #
+ #
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
# end
- #
+ #
# Remove tags to the default allowed tags
- #
+ #
# Rails::Initializer.run do |config|
# config.after_initialize do
# ActionView::Base.sanitized_allowed_tags.delete 'div'
# end
# end
- #
+ #
# Change allowed default attributes
- #
+ #
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style'
# end
- #
+ #
# Please note that sanitizing user-provided text does not guarantee that the
# resulting markup is valid (conforming to a document type) or even well-formed.
# The output may still contain e.g. unescaped '<', '>', '&' characters and
@@ -62,8 +67,8 @@ module ActionView
self.class.white_list_sanitizer.sanitize_css(style)
end
- # Strips all HTML tags from the +html+, including comments. This uses the
- # html-scanner tokenizer and so its HTML parsing ability is limited by
+ # Strips all HTML tags from the +html+, including comments. This uses the
+ # html-scanner tokenizer and so its HTML parsing ability is limited by
# that of html-scanner.
#
# ==== Examples
@@ -73,10 +78,10 @@ module ActionView
#
# strip_tags("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
# # => Bold no more! See more here...
- #
+ #
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
# # => Welcome to my website!
- def strip_tags(html)
+ def strip_tags(html)
self.class.full_sanitizer.sanitize(html)
end
@@ -96,21 +101,48 @@ module ActionView
end
module ClassMethods #:nodoc:
- def self.extended(base)
- class << base
- attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
-
- # we want these to be class methods on ActionView::Base, they'll get mattr_readers for these below.
- helper_def = [:sanitized_protocol_separator, :sanitized_uri_attributes, :sanitized_bad_tags, :sanitized_allowed_tags,
- :sanitized_allowed_attributes, :sanitized_allowed_css_properties, :sanitized_allowed_css_keywords,
- :sanitized_shorthand_css_properties, :sanitized_allowed_protocols, :sanitized_protocol_separator=].collect! do |prop|
- prop = prop.to_s
- "def #{prop}(#{:value if prop =~ /=$/}) white_list_sanitizer.#{prop.sub /sanitized_/, ''} #{:value if prop =~ /=$/} end"
- end.join("\n")
- eval helper_def
- end
- end
-
+ attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
+
+ def sanitized_protocol_separator
+ white_list_sanitizer.protocol_separator
+ end
+
+ def sanitized_uri_attributes
+ white_list_sanitizer.uri_attributes
+ end
+
+ def sanitized_bad_tags
+ white_list_sanitizer.bad_tags
+ end
+
+ def sanitized_allowed_tags
+ white_list_sanitizer.allowed_tags
+ end
+
+ def sanitized_allowed_attributes
+ white_list_sanitizer.allowed_attributes
+ end
+
+ def sanitized_allowed_css_properties
+ white_list_sanitizer.allowed_css_properties
+ end
+
+ def sanitized_allowed_css_keywords
+ white_list_sanitizer.allowed_css_keywords
+ end
+
+ def sanitized_shorthand_css_properties
+ white_list_sanitizer.shorthand_css_properties
+ end
+
+ def sanitized_allowed_protocols
+ white_list_sanitizer.allowed_protocols
+ end
+
+ def sanitized_protocol_separator=(value)
+ white_list_sanitizer.protocol_separator = value
+ end
+
# Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with
# any object that responds to +sanitize+.
#
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index f9096d0029..a5d43b90f4 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -1,5 +1,14 @@
require 'action_view/helpers/tag_helper'
-require 'html/document'
+
+begin
+ require 'html/document'
+rescue LoadError
+ html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner"
+ if File.directory?(html_scanner_path)
+ $:.unshift html_scanner_path
+ require 'html/document'
+ end
+end
module ActionView
module Helpers #:nodoc:
@@ -439,8 +448,10 @@ module ActionView
# array every time it is called. This can be used for example, to alternate
# classes for table rows. You can use named cycles to allow nesting in loops.
# Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
- # named cycle. You can manually reset a cycle by calling reset_cycle and passing the
- # name of the cycle.
+ # named cycle. The default name for a cycle without a +:name+ key is
+ # <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle
+ # and passing the name of the cycle. The current cycle string can be obtained
+ # anytime using the current_cycle method.
#
# ==== Examples
# # Alternate CSS classes for even and odd numbers...
@@ -487,6 +498,23 @@ module ActionView
return cycle.to_s
end
+ # Returns the current cycle string after a cycle has been started. Useful
+ # for complex table highlighing or any other design need which requires
+ # the current cycle string in more than one place.
+ #
+ # ==== Example
+ # # Alternate background colors
+ # @items = [1,2,3,4]
+ # <% @items.each do |item| %>
+ # <div style="background-color:<%= cycle("red","white","blue") %>">
+ # <span style="background-color:<%= current_cycle %><%= item %></span>
+ # </div>
+ # <% end %>
+ def current_cycle(name = "default")
+ cycle = get_cycle(name)
+ cycle.current_value unless cycle.nil?
+ end
+
# Resets a cycle so that it starts from the first element the next time
# it is called. Pass in +name+ to reset a named cycle.
#
@@ -523,11 +551,29 @@ module ActionView
@index = 0
end
+ def current_value
+ @values[previous_index].to_s
+ end
+
def to_s
value = @values[@index].to_s
- @index = (@index + 1) % @values.size
+ @index = next_index
return value
end
+
+ private
+
+ def next_index
+ step_index(1)
+ end
+
+ def previous_index
+ step_index(-1)
+ end
+
+ def step_index(n)
+ (@index + n) % @values.size
+ end
end
private
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 60ac5c8790..de4c1d7689 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -11,10 +11,12 @@ module ActionView
keys = I18n.send :normalize_translation_keys, e.locale, e.key, e.options[:scope]
content_tag('span', keys.join(', '), :class => 'translation_missing')
end
+ alias :t :translate
def localize(*args)
I18n.localize *args
end
+ alias :l :localize
end
end
end \ No newline at end of file
diff --git a/actionpack/lib/action_view/locale/en-US.rb b/actionpack/lib/action_view/locale/en-US.rb
deleted file mode 100644
index 2c3676dca8..0000000000
--- a/actionpack/lib/action_view/locale/en-US.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-I18n.backend.store_translations :'en-US', {
- :datetime => {
- :distance_in_words => {
- :half_a_minute => 'half a minute',
- :less_than_x_seconds => ['less than 1 second', 'less than {{count}} seconds'],
- :x_seconds => ['1 second', '{{count}} seconds'],
- :less_than_x_minutes => ['less than a minute', 'less than {{count}} minutes'],
- :x_minutes => ['1 minute', '{{count}} minutes'],
- :about_x_hours => ['about 1 hour', 'about {{count}} hours'],
- :x_days => ['1 day', '{{count}} days'],
- :about_x_months => ['about 1 month', 'about {{count}} months'],
- :x_months => ['1 month', '{{count}} months'],
- :about_x_years => ['about 1 year', 'about {{count}} year'],
- :over_x_years => ['over 1 year', 'over {{count}} years']
- }
- },
- :number => {
- :format => {
- :precision => 3,
- :separator => '.',
- :delimiter => ','
- },
- :currency => {
- :format => {
- :unit => '$',
- :precision => 2,
- :format => '%u%n'
- }
- },
- :human => {
- :format => {
- :precision => 1,
- :delimiter => ''
- }
- },
- :percentage => {
- :format => {
- :delimiter => ''
- }
- },
- :precision => {
- :format => {
- :delimiter => ''
- }
- }
- },
- :active_record => {
- :error => {
- :header_message => ["1 error prohibited this {{object_name}} from being saved", "{{count}} errors prohibited this {{object_name}} from being saved"],
- :message => "There were problems with the following fields:"
- }
- }
-}
diff --git a/actionpack/lib/action_view/locale/en-US.yml b/actionpack/lib/action_view/locale/en-US.yml
new file mode 100644
index 0000000000..818f2f93bd
--- /dev/null
+++ b/actionpack/lib/action_view/locale/en-US.yml
@@ -0,0 +1,91 @@
+"en-US":
+ number:
+ # Used in number_with_delimiter()
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
+ format:
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
+ separator: "."
+ # Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
+ delimiter: ","
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
+ precision: 3
+
+ # Used in number_to_currency()
+ currency:
+ format:
+ # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
+ format: "%u%n"
+ unit: "$"
+ # These three are to override number.format and are optional
+ separator: "."
+ delimiter: ","
+ precision: 2
+
+ # Used in number_to_percentage()
+ percentage:
+ format:
+ # These three are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ # precision:
+
+ # Used in number_to_precision()
+ precision:
+ format:
+ # These three are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ # precision:
+
+ # Used in number_to_human_size()
+ human:
+ format:
+ # These three are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ precision: 1
+
+ # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
+ datetime:
+ distance_in_words:
+ half_a_minute: "half a minute"
+ less_than_x_seconds:
+ one: "less than 1 second"
+ other: "less than {{count}} seconds"
+ x_seconds:
+ one: "1 second"
+ other: "{{count}} seconds"
+ less_than_x_minutes:
+ one: "less than a minute"
+ other: "less than {{count}} minutes"
+ x_minutes:
+ one: "1 minute"
+ other: "{{count}} minutes"
+ about_x_hours:
+ one: "about 1 hour"
+ other: "about {{count}} hours"
+ x_days:
+ one: "1 day"
+ other: "{{count}} days"
+ about_x_months:
+ one: "about 1 month"
+ other: "about {{count}} months"
+ x_months:
+ one: "1 month"
+ other: "{{count}} months"
+ about_x_years:
+ one: "about 1 year"
+ other: "about {{count}} years"
+ over_x_years:
+ one: "over 1 year"
+ other: "over {{count}} years"
+
+ activerecord:
+ errors:
+ template:
+ header:
+ one: "1 error prohibited this {{model}} from being saved"
+ other: "{{count}} errors prohibited this {{model}} from being saved"
+ # The variable :count is also available
+ body: "There were problems with the following fields:"
+
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index 894b88534c..443c49b870 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -1,14 +1,15 @@
module ActionView
- # There's also a convenience method for rendering sub templates within the current controller that depends on a single object
- # (we call this kind of sub templates for partials). It relies on the fact that partials should follow the naming convention of being
- # prefixed with an underscore -- as to separate them from regular templates that could be rendered on their own.
+ # There's also a convenience method for rendering sub templates within the current controller that depends on a
+ # single object (we call this kind of sub templates for partials). It relies on the fact that partials should
+ # follow the naming convention of being prefixed with an underscore -- as to separate them from regular
+ # templates that could be rendered on their own.
#
# In a template for Advertiser#account:
#
# <%= render :partial => "account" %>
#
- # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable +account+ to
- # the template for display.
+ # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable
+ # +account+ to the template for display.
#
# In another template for Advertiser#buy, we could have:
#
@@ -18,24 +19,24 @@ module ActionView
# <%= render :partial => "ad", :locals => { :ad => ad } %>
# <% end %>
#
- # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then render
- # "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
+ # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then
+ # render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
#
# == Rendering a collection of partials
#
- # The example of partial use describes a familiar pattern where a template needs to iterate over an array and render a sub
- # template for each of the elements. This pattern has been implemented as a single method that accepts an array and renders
- # a partial by the same name as the elements contained within. So the three-lined example in "Using partials" can be rewritten
- # with a single line:
+ # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
+ # render a sub template for each of the elements. This pattern has been implemented as a single method that
+ # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined
+ # example in "Using partials" can be rewritten with a single line:
#
# <%= render :partial => "ad", :collection => @advertisements %>
#
- # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An iteration counter
- # will automatically be made available to the template with a name of the form +partial_name_counter+. In the case of the
- # example above, the template would be fed +ad_counter+.
+ # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An
+ # iteration counter will automatically be made available to the template with a name of the form
+ # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
#
- # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also just keep domain objects,
- # like Active Records, in there.
+ # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
+ # just keep domain objects, like Active Records, in there.
#
# == Rendering shared partials
#
@@ -47,8 +48,9 @@ module ActionView
#
# == Rendering partials with layouts
#
- # Partials can have their own layouts applied to them. These layouts are different than the ones that are specified globally
- # for the entire action, but they work in a similar fashion. Imagine a list with two types of users:
+ # Partials can have their own layouts applied to them. These layouts are different than the ones that are
+ # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
+ # of users:
#
# <%# app/views/users/index.html.erb &>
# Here's the administrator:
@@ -68,7 +70,7 @@ module ActionView
#
# <%# app/views/users/_editor.html.erb &>
# <div id="editor">
- # Deadline: $<%= user.deadline %>
+ # Deadline: <%= user.deadline %>
# <%= yield %>
# </div>
#
@@ -82,7 +84,7 @@ module ActionView
#
# Here's the editor:
# <div id="editor">
- # Deadline: $<%= user.deadline %>
+ # Deadline: <%= user.deadline %>
# Name: <%= user.name %>
# </div>
#
@@ -101,42 +103,83 @@ module ActionView
# </div>
#
# As you can see, the <tt>:locals</tt> hash is shared between both the partial and its layout.
+ #
+ # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
+ # an array to layout and treat it as an enumerable.
+ #
+ # <%# app/views/users/_user.html.erb &>
+ # <div class="user">
+ # Budget: $<%= user.budget %>
+ # <%= yield user %>
+ # </div>
+ #
+ # <%# app/views/users/index.html.erb &>
+ # <% render :layout => @users do |user| %>
+ # Title: <%= user.title %>
+ # <% end %>
+ #
+ # This will render the layout for each user and yield to the block, passing the user, each time.
+ #
+ # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
+ #
+ # <%# app/views/users/_user.html.erb &>
+ # <div class="user">
+ # <%= yield user, :header %>
+ # Budget: $<%= user.budget %>
+ # <%= yield user, :footer %>
+ # </div>
+ #
+ # <%# app/views/users/index.html.erb &>
+ # <% render :layout => @users do |user, section| %>
+ # <%- case section when :header -%>
+ # Title: <%= user.title %>
+ # <%- when :footer -%>
+ # Deadline: <%= user.deadline %>
+ # <%- end -%>
+ # <% end %>
module Partials
extend ActiveSupport::Memoizable
private
- def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc:
- local_assigns ||= {}
+ def render_partial(options = {}) #:nodoc:
+ local_assigns = options[:locals] || {}
- case partial_path
+ case partial_path = options[:partial]
when String, Symbol, NilClass
- pick_template(find_partial_path(partial_path)).render_partial(self, object_assigns, local_assigns)
+ if options.has_key?(:collection)
+ render_partial_collection(options)
+ else
+ _pick_partial_template(partial_path).render_partial(self, options[:object], local_assigns)
+ end
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))
+ local_assigns.merge!(builder_partial_path.to_sym => partial_path)
+ render_partial(:partial => builder_partial_path, :object => options[:object], :locals => local_assigns)
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
- if partial_path.any?
- collection = partial_path
- render_partial_collection(nil, collection, nil, local_assigns)
- else
- ""
- end
+ render_partial_collection(options.except(:partial).merge(:collection => partial_path))
else
- render_partial(ActionController::RecordIdentifier.partial_path(partial_path, controller.class.controller_path), partial_path, local_assigns)
+ object = partial_path
+ render_partial(
+ :partial => ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path),
+ :object => object,
+ :locals => local_assigns
+ )
end
end
- def render_partial_collection(partial_path, collection, partial_spacer_template = nil, local_assigns = {}, as = nil) #:nodoc:
- return " " if collection.empty?
+ def render_partial_collection(options = {}) #:nodoc:
+ return nil if options[:collection].blank?
- local_assigns = local_assigns ? local_assigns.clone : {}
- spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : ''
+ partial = options[:partial]
+ spacer = options[:spacer_template] ? render(:partial => options[:spacer_template]) : ''
+ local_assigns = options[:locals] ? options[:locals].clone : {}
+ as = options[:as]
index = 0
- collection.map do |object|
- _partial_path ||= partial_path || ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
- path = find_partial_path(_partial_path)
- template = pick_template(path)
+ options[:collection].map do |object|
+ _partial_path ||= partial ||
+ ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
+ template = _pick_partial_template(_partial_path)
local_assigns[template.counter_name] = index
result = template.render_partial(self, object, local_assigns, as)
index += 1
@@ -144,15 +187,17 @@ module ActionView
end.join(spacer)
end
- def find_partial_path(partial_path)
+ def _pick_partial_template(partial_path) #:nodoc:
if partial_path.include?('/')
- File.join(File.dirname(partial_path), "_#{File.basename(partial_path)}")
- elsif respond_to?(:controller)
- "#{controller.class.controller_path}/_#{partial_path}"
+ path = File.join(File.dirname(partial_path), "_#{File.basename(partial_path)}")
+ elsif controller
+ path = "#{controller.class.controller_path}/_#{partial_path}"
else
- "_#{partial_path}"
+ path = "_#{partial_path}"
end
+
+ pick_template(path)
end
- memoize :find_partial_path
+ memoize :_pick_partial_template
end
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index d97f963540..d6bf2137af 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -1,9 +1,9 @@
module ActionView #:nodoc:
- class PathSet < ActiveSupport::TypedArray #: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 " +
+ Base.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 " +
@@ -15,6 +15,30 @@ module ActionView #:nodoc:
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) })
+ end
+
+ def unshift(*objs)
+ super(*objs.map { |obj| self.class.type_cast(obj) })
+ end
+
class Path #:nodoc:
def self.eager_load_templates!
@eager_load_templates = true
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index 89ac500717..fa45edd436 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -31,7 +31,14 @@ module ActionView
view.send(:evaluate_assigns)
view.send(:set_controller_content_type, mime_type) if respond_to?(:mime_type)
- view.send(:execute, method_name(local_assigns), local_assigns)
+
+ view.send(method_name(local_assigns), local_assigns) do |*names|
+ if proc = view.instance_variable_get("@_proc_for_layout")
+ view.capture(*names, &proc)
+ else
+ view.instance_variable_get("@content_for_#{names.first || 'layout'}")
+ end
+ end
end
def method_name(local_assigns)
@@ -65,7 +72,7 @@ module ActionView
end_src
begin
- logger = ActionController::Base.logger
+ logger = defined?(ActionController) && Base.logger
logger.debug "Compiling template #{render_symbol}" if logger
ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb
index 342850f0f0..5203e57ead 100644
--- a/actionpack/lib/action_view/renderable_partial.rb
+++ b/actionpack/lib/action_view/renderable_partial.rb
@@ -16,7 +16,11 @@ module ActionView
memoize :counter_name
def render(view, local_assigns = {})
- ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do
+ if defined? ActionController
+ ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do
+ super
+ end
+ else
super
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 5dc6708431..64597b3d39 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,3 +1,5 @@
+require 'action_controller/mime_type'
+
module ActionView #:nodoc:
class Template
extend TemplateHandlers
diff --git a/actionpack/lib/action_view/template_error.rb b/actionpack/lib/action_view/template_error.rb
index 35fc07bdb2..2368662f31 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
+ @base_path = template.base_path.to_s
@assigns, @source, @original_exception = assigns.dup, template.source, original_exception
@file_path = template.filename
@backtrace = compute_backtrace
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 1a3c93c283..adbb37fd09 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -25,9 +25,7 @@ module ActionView
end
end
- ActionView::Base.helper_modules.each do |helper_module|
- include helper_module
- end
+ include ActionView::Helpers
include ActionController::PolymorphicRoutes
include ActionController::RecordIdentifier