aboutsummaryrefslogtreecommitdiffstats
path: root/actionview/lib/action_view
diff options
context:
space:
mode:
Diffstat (limited to 'actionview/lib/action_view')
-rw-r--r--actionview/lib/action_view/base.rb6
-rw-r--r--actionview/lib/action_view/dependency_tracker.rb92
-rw-r--r--actionview/lib/action_view/digestor.rb64
-rw-r--r--actionview/lib/action_view/gem_version.rb15
-rw-r--r--actionview/lib/action_view/helpers.rb6
-rw-r--r--actionview/lib/action_view/helpers/active_model_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb35
-rw-r--r--actionview/lib/action_view/helpers/atom_feed_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb18
-rw-r--r--actionview/lib/action_view/helpers/csrf_helper.rb7
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb67
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb19
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb20
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb69
-rw-r--r--actionview/lib/action_view/helpers/rendering_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/tag_helper.rb14
-rw-r--r--actionview/lib/action_view/helpers/tags.rb64
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_helpers.rb2
-rw-r--r--actionview/lib/action_view/helpers/tags/label.rb3
-rw-r--r--actionview/lib/action_view/helpers/text_helper.rb9
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb30
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb11
-rw-r--r--actionview/lib/action_view/layouts.rb24
-rw-r--r--actionview/lib/action_view/log_subscriber.rb16
-rw-r--r--actionview/lib/action_view/lookup_context.rb18
-rw-r--r--actionview/lib/action_view/railtie.rb9
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb2
-rw-r--r--actionview/lib/action_view/renderer/streaming_template_renderer.rb2
-rw-r--r--actionview/lib/action_view/renderer/template_renderer.rb10
-rw-r--r--actionview/lib/action_view/rendering.rb20
-rw-r--r--actionview/lib/action_view/routing_url_for.rb2
-rw-r--r--actionview/lib/action_view/template.rb4
-rw-r--r--actionview/lib/action_view/template/error.rb7
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb4
-rw-r--r--actionview/lib/action_view/template/html.rb34
-rw-r--r--actionview/lib/action_view/template/resolver.rb34
-rw-r--r--actionview/lib/action_view/template/text.rb2
-rw-r--r--actionview/lib/action_view/template/types.rb2
-rw-r--r--actionview/lib/action_view/testing/resolvers.rb14
-rw-r--r--actionview/lib/action_view/vendor/html-scanner/html/node.rb4
-rw-r--r--actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb2
-rw-r--r--actionview/lib/action_view/vendor/html-scanner/html/selector.rb2
-rw-r--r--actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb2
-rw-r--r--actionview/lib/action_view/version.rb11
45 files changed, 520 insertions, 270 deletions
diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb
index caade8f43b..455ce531ae 100644
--- a/actionview/lib/action_view/base.rb
+++ b/actionview/lib/action_view/base.rb
@@ -1,5 +1,5 @@
require 'active_support/core_ext/module/attr_internal'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/ordered_options'
require 'action_view/log_subscriber'
require 'action_view/helpers'
@@ -153,6 +153,10 @@ module ActionView #:nodoc:
# Specify default_formats that can be rendered.
cattr_accessor :default_formats
+ # Specify whether an error should be raised for missing translations
+ cattr_accessor :raise_on_missing_translations
+ @@raise_on_missing_translations = false
+
class_attribute :_routes
class_attribute :logger
diff --git a/actionview/lib/action_view/dependency_tracker.rb b/actionview/lib/action_view/dependency_tracker.rb
index b2e8334077..0ccf2515c5 100644
--- a/actionview/lib/action_view/dependency_tracker.rb
+++ b/actionview/lib/action_view/dependency_tracker.rb
@@ -1,7 +1,7 @@
require 'thread_safe'
module ActionView
- class DependencyTracker
+ class DependencyTracker # :nodoc:
@trackers = ThreadSafe::Cache.new
def self.find_dependencies(name, template)
@@ -23,24 +23,52 @@ module ActionView
@trackers.delete(handler)
end
- class ERBTracker
+ class ERBTracker # :nodoc:
EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
+ # A valid ruby identifier - suitable for class, method and specially variable names
+ IDENTIFIER = /
+ [[:alpha:]_] # at least one uppercase letter, lowercase letter or underscore
+ [[:word:]]* # followed by optional letters, numbers or underscores
+ /x
+
+ # Any kind of variable name. e.g. @instance, @@class, $global or local.
+ # Possibly following a method call chain
+ VARIABLE_OR_METHOD_CHAIN = /
+ (?:\$|@{1,2})? # optional global, instance or class variable indicator
+ (?:#{IDENTIFIER}\.)* # followed by an optional chain of zero-argument method calls
+ (?<dynamic>#{IDENTIFIER}) # and a final valid identifier, captured as DYNAMIC
+ /x
+
+ # A simple string literal. e.g. "School's out!"
+ STRING = /
+ (?<quote>['"]) # an opening quote
+ (?<static>.*?) # with anything inside, captured as STATIC
+ \k<quote> # and a matching closing quote
+ /x
+
+ # Part of any hash containing the :partial key
+ PARTIAL_HASH_KEY = /
+ (?:\bpartial:|:partial\s*=>) # partial key in either old or new style hash syntax
+ \s* # followed by optional spaces
+ /x
+
# Matches:
- # render partial: "comments/comment", collection: commentable.comments
- # render "comments/comments"
- # render 'comments/comments'
- # render('comments/comments')
+ # partial: "comments/comment", collection: @all_comments => "comments/comment"
+ # (object: @single_comment, partial: "comments/comment") => "comments/comment"
#
- # render(@topic) => render("topics/topic")
- # render(topics) => render("topics/topic")
- # render(message.topics) => render("topics/topic")
- RENDER_DEPENDENCY = /
- render\s* # render, followed by optional whitespace
- \(? # start an optional parenthesis for the render call
- (partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture
- ([@a-z"'][@\w\/\."']+) # the template name itself -- 2nd capture
- /x
+ # "comments/comments"
+ # 'comments/comments'
+ # ('comments/comments')
+ #
+ # (@topic) => "topics/topic"
+ # topics => "topics/topic"
+ # (message.topics) => "topics/topic"
+ RENDER_ARGUMENTS = /\A
+ (?:\s*\(?\s*) # optional opening paren surrounded by spaces
+ (?:.*?#{PARTIAL_HASH_KEY})? # optional hash, up to the partial key declaration
+ (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
+ /xm
def self.call(name, template)
new(name, template).dependencies
@@ -68,19 +96,33 @@ module ActionView
end
def render_dependencies
- source.scan(RENDER_DEPENDENCY).
- collect(&:second).uniq.
+ render_dependencies = []
+ render_calls = source.split(/\brender\b/).drop(1)
- # render(@topic) => render("topics/topic")
- # render(topics) => render("topics/topic")
- # render(message.topics) => render("topics/topic")
- collect { |name| name.sub(/\A@?([a-z_]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }.
+ render_calls.each do |arguments|
+ arguments.scan(RENDER_ARGUMENTS) do
+ add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic])
+ add_static_dependency(render_dependencies, Regexp.last_match[:static])
+ end
+ end
- # render("headline") => render("message/headline")
- collect { |name| name.include?("/") ? name : "#{directory}/#{name}" }.
+ render_dependencies.uniq
+ end
+
+ def add_dynamic_dependency(dependencies, dependency)
+ if dependency
+ dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
+ end
+ end
- # replace quotes from string renders
- collect { |name| name.gsub(/["']/, "") }
+ def add_static_dependency(dependencies, dependency)
+ if dependency
+ if dependency.include?('/')
+ dependencies << dependency
+ else
+ dependencies << "#{directory}/#{dependency}"
+ end
+ end
end
def explicit_dependencies
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 5570e2a8dc..40d493da64 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -9,23 +9,53 @@ module ActionView
@@digest_monitor = Monitor.new
class << self
- def digest(name, format, finder, options = {})
- details_key = finder.details_key.hash
+ # Supported options:
+ #
+ # * <tt>name</tt> - Template name
+ # * <tt>format</tt> - Template format
+ # * <tt>variant</tt> - Variant of +format+ (optional)
+ # * <tt>finder</tt> - An instance of ActionView::LookupContext
+ # * <tt>dependencies</tt> - An array of dependent views
+ # * <tt>partial</tt> - Specifies whether the template is a partial
+ def digest(options_or_deprecated_name, *deprecated_args)
+ options = _options_for_digest(options_or_deprecated_name, *deprecated_args)
+
+ details_key = options[:finder].details_key.hash
dependencies = Array.wrap(options[:dependencies])
- cache_key = ([name, details_key, format] + dependencies).join('.')
+ cache_key = ([options[:name], details_key, options[:format], options[:variant]].compact + dependencies).join('.')
# this is a correctly done double-checked locking idiom
# (ThreadSafe::Cache's lookups have volatile semantics)
@@cache[cache_key] || @@digest_monitor.synchronize do
@@cache.fetch(cache_key) do # re-check under lock
- compute_and_store_digest(cache_key, name, format, finder, options)
+ compute_and_store_digest(cache_key, options)
end
end
end
+ def _options_for_digest(options_or_deprecated_name, *deprecated_args)
+ if !options_or_deprecated_name.is_a?(Hash)
+ ActiveSupport::Deprecation.warn \
+ 'ActionView::Digestor.digest changed its method signature from ' \
+ '#digest(name, format, finder, options) to #digest(options), ' \
+ 'with options now including :name, :format, :finder, and ' \
+ ':variant. Please update your code to pass a Hash argument. ' \
+ 'Support for the old method signature will be removed in Rails 5.0.'
+
+ _options_for_digest (deprecated_args[2] || {}).merge \
+ name: options_or_deprecated_name,
+ format: deprecated_args[0],
+ finder: deprecated_args[1]
+ else
+ options_or_deprecated_name.assert_valid_keys(:name, :format, :variant, :finder, :dependencies, :partial)
+ options_or_deprecated_name
+ end
+ end
+
private
- def compute_and_store_digest(cache_key, name, format, finder, options) # called under @@digest_monitor lock
- klass = if options[:partial] || name.include?("/_")
+
+ def compute_and_store_digest(cache_key, options) # called under @@digest_monitor lock
+ klass = if options[:partial] || options[:name].include?("/_")
# Prevent re-entry or else recursive templates will blow the stack.
# There is no need to worry about other threads seeing the +false+ value,
# as they will then have to wait for this thread to let go of the @@digest_monitor lock.
@@ -35,20 +65,22 @@ module ActionView
Digestor
end
- digest = klass.new(name, format, finder, options).digest
+ digest = klass.new(options).digest
# Store the actual digest if config.cache_template_loading is true
@@cache[cache_key] = stored_digest = digest if ActionView::Resolver.caching?
digest
ensure
# something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache
- @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
+ @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
end
end
- attr_reader :name, :format, :finder, :options
+ attr_reader :name, :format, :variant, :finder, :options
- def initialize(name, format, finder, options={})
- @name, @format, @finder, @options = name, format, finder, options
+ def initialize(options_or_deprecated_name, *deprecated_args)
+ options = self.class._options_for_digest(options_or_deprecated_name, *deprecated_args)
+ @options = options.except(:name, :format, :variant, :finder)
+ @name, @format, @variant, @finder = options.values_at(:name, :format, :variant, :finder)
end
def digest
@@ -68,7 +100,7 @@ module ActionView
def nested_dependencies
dependencies.collect do |dependency|
- dependencies = PartialDigestor.new(dependency, format, finder).nested_dependencies
+ dependencies = PartialDigestor.new(name: dependency, format: format, finder: finder).nested_dependencies
dependencies.any? ? { dependency => dependencies } : dependency
end
end
@@ -88,7 +120,11 @@ module ActionView
end
def template
- @template ||= finder.find(logical_name, [], partial?, formats: [ format ])
+ @template ||= begin
+ finder.disable_cache do
+ finder.find(logical_name, [], partial?, [], formats: [format], variants: [variant])
+ end
+ end
end
def source
@@ -97,7 +133,7 @@ module ActionView
def dependency_digest
template_digests = dependencies.collect do |template_name|
- Digestor.digest(template_name, format, finder, partial: true)
+ Digestor.digest(name: template_name, format: format, finder: finder, partial: true)
end
(template_digests + injected_dependencies).join("-")
diff --git a/actionview/lib/action_view/gem_version.rb b/actionview/lib/action_view/gem_version.rb
new file mode 100644
index 0000000000..9266e55c47
--- /dev/null
+++ b/actionview/lib/action_view/gem_version.rb
@@ -0,0 +1,15 @@
+module ActionView
+ # Returns the version of the currently loaded ActionView as a <tt>Gem::Version</tt>
+ def self.gem_version
+ Gem::Version.new VERSION::STRING
+ end
+
+ module VERSION
+ MAJOR = 4
+ MINOR = 2
+ TINY = 0
+ PRE = "alpha"
+
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
+ end
+end
diff --git a/actionview/lib/action_view/helpers.rb b/actionview/lib/action_view/helpers.rb
index 8a78685ae1..787e9d67b2 100644
--- a/actionview/lib/action_view/helpers.rb
+++ b/actionview/lib/action_view/helpers.rb
@@ -27,6 +27,12 @@ module ActionView #:nodoc:
autoload :TextHelper
autoload :TranslationHelper
autoload :UrlHelper
+ autoload :Tags
+
+ def self.eager_load!
+ super
+ Tags.eager_load!
+ end
extend ActiveSupport::Concern
diff --git a/actionview/lib/action_view/helpers/active_model_helper.rb b/actionview/lib/action_view/helpers/active_model_helper.rb
index 901f433c70..d5222e3616 100644
--- a/actionview/lib/action_view/helpers/active_model_helper.rb
+++ b/actionview/lib/action_view/helpers/active_model_helper.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/enumerable'
module ActionView
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index e2430140c2..aa49f1edc1 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -103,7 +103,7 @@ module ActionView
}.join("\n").html_safe
end
- # Returns a link tag that browsers and news readers can use to auto-detect
+ # Returns a link tag that browsers and feed readers can use to auto-detect
# an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
# <tt>:atom</tt>. Control the link options in url_for format using the
# +url_options+. You can modify the LINK tag itself in +tag_options+.
@@ -153,14 +153,14 @@ module ActionView
#
# ==== Examples
#
- # favicon_link_tag '/myicon.ico'
+ # favicon_link_tag 'myicon.ico'
# # => <link href="/assets/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
#
# Mobile Safari looks for a different <link> tag, pointing to an image that
# will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
# The following call would generate such a tag:
#
- # favicon_link_tag '/mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
+ # favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
# # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
def favicon_link_tag(source='favicon.ico', options={})
tag('link', {
@@ -176,7 +176,7 @@ module ActionView
# ==== Options
#
# You can add HTML attributes using the +options+. The +options+ supports
- # three additional keys for convenience and conformance:
+ # two additional keys for convenience and conformance:
#
# * <tt>:alt</tt> - If no alt text is given, the file name part of the
# +source+ is used (capitalized and without the extension)
@@ -207,11 +207,7 @@ module ActionView
options[:alt] = options.fetch(:alt){ image_alt(src) }
end
- if size = options.delete(:size)
- options[:width], options[:height] = size.split("x") if size =~ %r{\A\d+x\d+\z}
- options[:width] = options[:height] = size if size =~ %r{\A\d+\z}
- end
-
+ options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
tag("img", options)
end
@@ -248,9 +244,9 @@ module ActionView
#
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
# before the video loads. The path is calculated like the +src+ of +image_tag+.
- # * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
- # width="30" and height="45". <tt>:size</tt> will be ignored if the
- # value is not in the correct format.
+ # * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
+ # width="30" and height="45", and "50" becomes width="50" and height="50".
+ # <tt>:size</tt> will be ignored if the value is not in the correct format.
#
# ==== Examples
#
@@ -264,6 +260,8 @@ module ActionView
# # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
# video_tag("/trailers/hd.avi", size: "16x16")
# # => <video src="/trailers/hd.avi" width="16" height="16" />
+ # video_tag("/trailers/hd.avi", size: "16")
+ # # => <video height="16" src="/trailers/hd.avi" width="16" />
# video_tag("/trailers/hd.avi", height: '32', width: '32')
# # => <video height="32" src="/trailers/hd.avi" width="32" />
# video_tag("trailer.ogg", "trailer.flv")
@@ -275,10 +273,7 @@ module ActionView
def video_tag(*sources)
multiple_sources_tag('video', sources) do |options|
options[:poster] = path_to_image(options[:poster]) if options[:poster]
-
- if size = options.delete(:size)
- options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
- end
+ options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
end
end
@@ -314,6 +309,14 @@ module ActionView
content_tag(type, nil, options)
end
end
+
+ def extract_dimensions(size)
+ if size =~ %r{\A\d+x\d+\z}
+ size.split('x')
+ elsif size =~ %r{\A\d+\z}
+ [size, size]
+ end
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb
index 42b1dd8933..af70a4242a 100644
--- a/actionview/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb
@@ -64,7 +64,7 @@ module ActionView
# 'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
# feed.title("My great blog!")
# feed.updated((@posts.first.created_at))
- # feed.tag!(openSearch:totalResults, 10)
+ # feed.tag!('openSearch:totalResults', 10)
#
# @posts.each do |post|
# feed.entry(post) do |entry|
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index b3af1d4da4..3177d18c4d 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -165,10 +165,20 @@ module ActionView
def fragment_name_with_digest(name) #:nodoc:
if @virtual_path
- [
- *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
- Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context, dependencies: view_cache_dependencies)
- ]
+ variant = request.variant.is_a?(Array) ? request.variant.first : request.variant
+
+ options = {
+ name: @virtual_path,
+ format: formats.last.to_sym,
+ variant: variant,
+ finder: lookup_context,
+ dependencies: view_cache_dependencies
+ }
+
+ names = Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name)
+ digest = Digestor.digest(options)
+
+ [*names, digest]
else
name
end
diff --git a/actionview/lib/action_view/helpers/csrf_helper.rb b/actionview/lib/action_view/helpers/csrf_helper.rb
index eeb0ed94b9..5af92c4ff2 100644
--- a/actionview/lib/action_view/helpers/csrf_helper.rb
+++ b/actionview/lib/action_view/helpers/csrf_helper.rb
@@ -12,8 +12,11 @@ module ActionView
# These are used to generate the dynamic forms that implement non-remote links with
# <tt>:method</tt>.
#
- # Note that regular forms generate hidden fields, and that Ajax calls are whitelisted,
- # so they do not use these tags.
+ # You don't need to use these tags for regular forms as they generate their own hidden fields.
+ #
+ # For AJAX requests other than GETs, extract the "csrf-token" from the meta-tag and send as the
+ # "X-CSRF-Token" HTTP header. If you are using jQuery with jquery-rails this happens automatically.
+ #
def csrf_meta_tags
if protect_against_forgery?
[
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index d7e8e99200..1738df9cac 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -19,6 +19,10 @@ module ActionView
# the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
# of \date[month].
module DateHelper
+ MINUTES_IN_YEAR = 525600
+ MINUTES_IN_QUARTER_YEAR = 131400
+ MINUTES_IN_THREE_QUARTERS_YEAR = 394200
+
# Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
# Pass <tt>include_seconds: true</tt> if you want more detailed approximations when distance < 1 min, 29 secs.
# Distances are reported based on the following table:
@@ -50,19 +54,19 @@ module ActionView
# distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
# distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
# distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
- # distance_of_time_in_words(from_time, from_time + 15.seconds, include_seconds: true) # => less than 20 seconds
+ # distance_of_time_in_words(from_time, from_time + 15.seconds, include_seconds: true) # => less than 20 seconds
# distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
# distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
- # distance_of_time_in_words(from_time, from_time + 45.seconds, include_seconds: true) # => less than a minute
- # distance_of_time_in_words(from_time, from_time - 45.seconds, include_seconds: true) # => less than a minute
+ # distance_of_time_in_words(from_time, from_time + 45.seconds, include_seconds: true) # => less than a minute
+ # distance_of_time_in_words(from_time, from_time - 45.seconds, include_seconds: true) # => less than a minute
# distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
# distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
# distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
# distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
#
# to_time = Time.now + 6.years + 19.days
- # distance_of_time_in_words(from_time, to_time, include_seconds: true) # => about 6 years
- # distance_of_time_in_words(to_time, from_time, include_seconds: true) # => about 6 years
+ # distance_of_time_in_words(from_time, to_time, include_seconds: true) # => about 6 years
+ # distance_of_time_in_words(to_time, from_time, include_seconds: true) # => about 6 years
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
def distance_of_time_in_words(from_time, to_time = 0, options = {})
options = {
@@ -115,16 +119,16 @@ module ActionView
# e.g. if there are 20 leap year days between 2 dates having the same day
# and month then the based on 365 days calculation
# the distance in years will come out to over 80 years when in written
- # english it would read better as about 80 years.
+ # English it would read better as about 80 years.
minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
else
minutes_with_offset = distance_in_minutes
end
- remainder = (minutes_with_offset % 525600)
- distance_in_years = (minutes_with_offset.div 525600)
- if remainder < 131400
+ remainder = (minutes_with_offset % MINUTES_IN_YEAR)
+ distance_in_years = (minutes_with_offset.div MINUTES_IN_YEAR)
+ if remainder < MINUTES_IN_QUARTER_YEAR
locale.t(:about_x_years, :count => distance_in_years)
- elsif remainder < 394200
+ elsif remainder < MINUTES_IN_THREE_QUARTERS_YEAR
locale.t(:over_x_years, :count => distance_in_years)
else
locale.t(:almost_x_years, :count => distance_in_years + 1)
@@ -169,9 +173,16 @@ module ActionView
# "2 - February" instead of "February").
# * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
# Note: You can also use Rails' i18n functionality for this.
+ # * <tt>:month_format_string</tt> - Set to a format string. The string gets passed keys +:number+ (integer)
+ # and +:name+ (string). A format string would be something like "%{name} (%<number>02d)" for example.
+ # See <tt>Kernel.sprintf</tt> for documentation on format sequences.
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
- # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
- # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
+ # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt>if
+ # you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to
+ # the current selected year minus 5.
+ # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if
+ # you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to
+ # the current selected year plus 5.
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
# first of the given month in order to not create invalid dates like 31 February.
@@ -846,24 +857,36 @@ module ActionView
I18n.translate(key, :locale => @options[:locale])
end
- # Lookup month name for number.
- # month_name(1) => "January"
+ # Looks up month names by number (1-based):
+ #
+ # month_name(1) # => "January"
+ #
+ # If the <tt>:use_month_numbers</tt> option is passed:
+ #
+ # month_name(1) # => 1
+ #
+ # If the <tt>:use_two_month_numbers</tt> option is passed:
+ #
+ # month_name(1) # => '01'
+ #
+ # If the <tt>:add_month_numbers</tt> option is passed:
+ #
+ # month_name(1) # => "1 - January"
#
- # If <tt>:use_month_numbers</tt> option is passed
- # month_name(1) => 1
+ # If the <tt>:month_format_string</tt> option is passed:
#
- # If <tt>:use_two_month_numbers</tt> option is passed
- # month_name(1) => '01'
+ # month_name(1) # => "January (01)"
#
- # If <tt>:add_month_numbers</tt> option is passed
- # month_name(1) => "1 - January"
+ # depending on the format string.
def month_name(number)
if @options[:use_month_numbers]
number
elsif @options[:use_two_digit_numbers]
- sprintf "%02d", number
+ '%02d' % number
elsif @options[:add_month_numbers]
"#{number} - #{month_names[number]}"
+ elsif format_string = @options[:month_format_string]
+ format_string % {number: number, name: month_names[number]}
else
month_names[number]
end
@@ -1062,7 +1085,7 @@ module ActionView
# Wraps ActionView::Helpers::DateHelper#datetime_select for form builders:
#
# <%= form_for @person do |f| %>
- # <%= f.time_select :last_request_at %>
+ # <%= f.datetime_select :last_request_at %>
# <%= f.submit %>
# <% end %>
#
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 38d969ed0c..1ff090f244 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -3,9 +3,8 @@ require 'action_view/helpers/date_helper'
require 'action_view/helpers/tag_helper'
require 'action_view/helpers/form_tag_helper'
require 'action_view/helpers/active_model_helper'
-require 'action_view/helpers/tags'
require 'action_view/model_naming'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/inflections'
@@ -52,7 +51,7 @@ module ActionView
# The HTML generated for this would be (modulus formatting):
#
# <form action="/people" class="new_person" id="new_person" method="post">
- # <div style="margin:0;padding:0;display:inline">
+ # <div style="display:none">
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
# </div>
# <label for="person_first_name">First name</label>:
@@ -82,7 +81,7 @@ module ActionView
# the code above as is would yield instead:
#
# <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
- # <div style="margin:0;padding:0;display:inline">
+ # <div style="display:none">
# <input name="_method" type="hidden" value="patch" />
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
# </div>
@@ -316,7 +315,7 @@ module ActionView
# The HTML generated for this would be:
#
# <form action='http://www.example.com' method='post' data-remote='true'>
- # <div style='margin:0;padding:0;display:inline'>
+ # <div style='display:none'>
# <input name='_method' type='hidden' value='patch' />
# </div>
# ...
@@ -334,7 +333,7 @@ module ActionView
# The HTML generated for this would be:
#
# <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
- # <div style='margin:0;padding:0;display:inline'>
+ # <div style='display:none'>
# <input name='_method' type='hidden' value='patch' />
# </div>
# ...
@@ -458,7 +457,7 @@ module ActionView
# doesn't create the form tags themselves. This makes fields_for suitable
# for specifying additional model objects in the same form.
#
- # Although the usage and purpose of +field_for+ is similar to +form_for+'s,
+ # Although the usage and purpose of +fields_for+ is similar to +form_for+'s,
# its method signature is slightly different. Like +form_for+, it yields
# a FormBuilder object associated with a particular model object to a block,
# and within the block allows methods to be called on the builder to
@@ -763,8 +762,8 @@ module ActionView
# text_field(:post, :title, class: "create_input")
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
#
- # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }")
- # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }"/>
+ # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
+ # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>
#
# text_field(:snippet, :code, size: 20, class: 'code_input')
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
@@ -1269,7 +1268,7 @@ module ActionView
# doesn't create the form tags themselves. This makes fields_for suitable
# for specifying additional model objects in the same form.
#
- # Although the usage and purpose of +field_for+ is similar to +form_for+'s,
+ # Although the usage and purpose of +fields_for+ is similar to +form_for+'s,
# its method signature is slightly different. Like +form_for+, it yields
# a FormBuilder object associated with a particular model object to a block,
# and within the block allows methods to be called on the builder to
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index 4347983bad..f625a9ff49 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -260,7 +260,7 @@ module ActionView
Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render
end
- # Return select and option tags for the given object and method, using
+ # Returns select and option tags for the given object and method, using
# #time_zone_options_for_select to generate the list of option tags.
#
# In addition to the <tt>:include_blank</tt> option documented above,
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 142c27ace0..0bbe08166b 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -465,17 +465,23 @@ module ActionView
# # <strong>Ask me!</strong>
# # </button>
#
- # button_tag "Checkout", data: { disable_with => "Please wait..." }
+ # button_tag "Checkout", data: { disable_with: "Please wait..." }
# # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
#
def button_tag(content_or_options = nil, options = nil, &block)
- options = content_or_options if block_given? && content_or_options.is_a?(Hash)
- options ||= {}
- options = options.stringify_keys
+ if content_or_options.is_a? Hash
+ options = content_or_options
+ else
+ options ||= {}
+ end
- options.reverse_merge! 'name' => 'button', 'type' => 'submit'
+ options = { 'name' => 'button', 'type' => 'submit' }.merge!(options.stringify_keys)
- content_tag :button, content_or_options || 'Button', options, &block
+ if block_given?
+ content_tag :button, options, &block
+ else
+ content_tag :button, content_or_options || 'Button', options
+ end
end
# Displays an image which when clicked will submit the form.
@@ -722,7 +728,7 @@ module ActionView
enforce_utf8 = html_options.delete("enforce_utf8") { true }
tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag
- content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
+ content_tag(:div, tags, :style => 'display:none')
end
def form_tag_html(html_options)
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index fda7038a5d..7157a95146 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -100,17 +100,12 @@ module ActionView
#
# number_to_currency(-1234567890.50, negative_format: "(%u%n)")
# # => ($1,234,567,890.50)
- # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "")
- # # => &pound;1234567890,50
- # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "", format: "%n %u")
- # # => 1234567890,50 &pound;
+ # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
+ # # => R$1234567890,50
+ # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
+ # # => 1234567890,50 R$
def number_to_currency(number, options = {})
- return unless number
- options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
- wrap_with_output_safety_handling(number, options.delete(:raise)) {
- ActiveSupport::NumberHelper.number_to_currency(number, options)
- }
+ delegate_number_helper_method(:number_to_currency, number, options)
end
# Formats a +number+ as a percentage string (e.g., 65%). You can
@@ -150,12 +145,7 @@ module ActionView
#
# number_to_percentage("98a", raise: true) # => InvalidNumberError
def number_to_percentage(number, options = {})
- return unless number
- options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
- wrap_with_output_safety_handling(number, options.delete(:raise)) {
- ActiveSupport::NumberHelper.number_to_percentage(number, options)
- }
+ delegate_number_helper_method(:number_to_percentage, number, options)
end
# Formats a +number+ with grouped thousands using +delimiter+
@@ -188,11 +178,7 @@ module ActionView
#
# number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
def number_with_delimiter(number, options = {})
- options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
- wrap_with_output_safety_handling(number, options.delete(:raise)) {
- ActiveSupport::NumberHelper.number_to_delimited(number, options)
- }
+ delegate_number_helper_method(:number_to_delimited, number, options)
end
# Formats a +number+ with the specified level of
@@ -237,11 +223,7 @@ module ActionView
# number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
# # => 1.111,23
def number_with_precision(number, options = {})
- options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
- wrap_with_output_safety_handling(number, options.delete(:raise)) {
- ActiveSupport::NumberHelper.number_to_rounded(number, options)
- }
+ delegate_number_helper_method(:number_to_rounded, number, options)
end
# Formats the bytes in +number+ into a more understandable
@@ -293,11 +275,7 @@ module ActionView
# number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
# number_to_human_size(524288000, precision: 5) # => "500 MB"
def number_to_human_size(number, options = {})
- options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
- wrap_with_output_safety_handling(number, options.delete(:raise)) {
- ActiveSupport::NumberHelper.number_to_human_size(number, options)
- }
+ delegate_number_helper_method(:number_to_human_size, number, options)
end
# Pretty prints (formats and approximates) a number in a way it
@@ -399,21 +377,36 @@ module ActionView
# number_to_human(0.34, units: :distance) # => "34 centimeters"
#
def number_to_human(number, options = {})
- options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
+ delegate_number_helper_method(:number_to_human, number, options)
+ end
+
+ private
+
+ def delegate_number_helper_method(method, number, options)
+ return unless number
+ options = escape_unsafe_options(options.symbolize_keys)
wrap_with_output_safety_handling(number, options.delete(:raise)) {
- ActiveSupport::NumberHelper.number_to_human(number, options)
+ ActiveSupport::NumberHelper.public_send(method, number, options)
}
end
- private
-
- def escape_unsafe_delimiters_and_separators(options)
- options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator] && !options[:separator].html_safe?
- options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter] && !options[:delimiter].html_safe?
+ def escape_unsafe_options(options)
+ options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
+ options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
+ options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
+ options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
+ options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
+ options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
options
end
+ def escape_units(units)
+ Hash[units.map do |k, v|
+ [k, ERB::Util.html_escape(v)]
+ end]
+ end
+
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
valid_float = valid_float?(number)
raise InvalidNumberError, number if raise_on_invalid && !valid_float
diff --git a/actionview/lib/action_view/helpers/rendering_helper.rb b/actionview/lib/action_view/helpers/rendering_helper.rb
index 458086de96..ebfc35a4c7 100644
--- a/actionview/lib/action_view/helpers/rendering_helper.rb
+++ b/actionview/lib/action_view/helpers/rendering_helper.rb
@@ -12,6 +12,14 @@ module ActionView
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
# * <tt>:text</tt> - Renders the text passed in out.
+ # * <tt>:plain</tt> - Renders the text passed in out. Setting the content
+ # type as <tt>text/plain</tt>.
+ # * <tt>:html</tt> - Renders the html safe string passed in out, otherwise
+ # performs html escape on the string first. Setting the content type as
+ # <tt>text/html</tt>.
+ # * <tt>:body</tt> - Renders the text passed in, and inherits the content
+ # type of <tt>text/html</tt> from <tt>ActionDispatch::Response</tt>
+ # object.
#
# If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
# as the locals hash.
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb
index 732f35643a..a9f3b0ffbc 100644
--- a/actionview/lib/action_view/helpers/tag_helper.rb
+++ b/actionview/lib/action_view/helpers/tag_helper.rb
@@ -42,7 +42,8 @@ module ActionView
# For example, a key +user_id+ would render as <tt>data-user-id</tt> and
# thus accessed as <tt>dataset.userId</tt>.
#
- # Values are encoded to JSON, with the exception of strings and symbols.
+ # Values are encoded to JSON, with the exception of strings, symbols and
+ # BigDecimals.
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
# from 1.4.3.
#
@@ -56,6 +57,9 @@ module ActionView
# tag("input", type: 'text', disabled: true)
# # => <input type="text" disabled="disabled" />
#
+ # tag("input", type: 'text', class: ["strong", "highlight"])
+ # # => <input class="strong highlight" type="text" />
+ #
# tag("img", src: "open & shut.png")
# # => <img src="open &amp; shut.png" />
#
@@ -75,7 +79,7 @@ module ActionView
# Set escape to false to disable attribute value escaping.
#
# ==== Options
- # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
+ # The +options+ hash can be used with attributes with no value like (<tt>disabled</tt> and
# <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
# symbols or strings for the attribute names.
#
@@ -84,6 +88,8 @@ module ActionView
# # => <p>Hello world!</p>
# content_tag(:div, content_tag(:p, "Hello world!"), class: "strong")
# # => <div class="strong"><p>Hello world!</p></div>
+ # content_tag(:div, "Hello world!", class: ["strong", "highlight"])
+ # # => <div class="strong highlight">Hello world!</div>
# content_tag("select", options, multiple: true)
# # => <select multiple="multiple">...options...</select>
#
@@ -114,7 +120,7 @@ module ActionView
# cdata_section("hello]]>world")
# # => <![CDATA[hello]]]]><![CDATA[>world]]>
def cdata_section(content)
- splitted = content.gsub(']]>', ']]]]><![CDATA[>')
+ splitted = content.to_s.gsub(']]>', ']]]]><![CDATA[>')
"<![CDATA[#{splitted}]]>".html_safe
end
@@ -151,7 +157,7 @@ module ActionView
attrs << tag_option(key, value, escape)
end
end
- " #{attrs.sort! * ' '}".html_safe unless attrs.empty?
+ " #{attrs.sort! * ' '}" unless attrs.empty?
end
def data_tag_option(key, value, escape)
diff --git a/actionview/lib/action_view/helpers/tags.rb b/actionview/lib/action_view/helpers/tags.rb
index a05e16979a..45c75d10c0 100644
--- a/actionview/lib/action_view/helpers/tags.rb
+++ b/actionview/lib/action_view/helpers/tags.rb
@@ -3,37 +3,39 @@ module ActionView
module Tags #:nodoc:
extend ActiveSupport::Autoload
- autoload :Base
- autoload :CheckBox
- autoload :CollectionCheckBoxes
- autoload :CollectionRadioButtons
- autoload :CollectionSelect
- autoload :ColorField
- autoload :DateField
- autoload :DateSelect
- autoload :DatetimeField
- autoload :DatetimeLocalField
- autoload :DatetimeSelect
- autoload :EmailField
- autoload :FileField
- autoload :GroupedCollectionSelect
- autoload :HiddenField
- autoload :Label
- autoload :MonthField
- autoload :NumberField
- autoload :PasswordField
- autoload :RadioButton
- autoload :RangeField
- autoload :SearchField
- autoload :Select
- autoload :TelField
- autoload :TextArea
- autoload :TextField
- autoload :TimeField
- autoload :TimeSelect
- autoload :TimeZoneSelect
- autoload :UrlField
- autoload :WeekField
+ eager_autoload do
+ autoload :Base
+ autoload :CheckBox
+ autoload :CollectionCheckBoxes
+ autoload :CollectionRadioButtons
+ autoload :CollectionSelect
+ autoload :ColorField
+ autoload :DateField
+ autoload :DateSelect
+ autoload :DatetimeField
+ autoload :DatetimeLocalField
+ autoload :DatetimeSelect
+ autoload :EmailField
+ autoload :FileField
+ autoload :GroupedCollectionSelect
+ autoload :HiddenField
+ autoload :Label
+ autoload :MonthField
+ autoload :NumberField
+ autoload :PasswordField
+ autoload :RadioButton
+ autoload :RangeField
+ autoload :SearchField
+ autoload :Select
+ autoload :TelField
+ autoload :TextArea
+ autoload :TextField
+ autoload :TimeField
+ autoload :TimeSelect
+ autoload :TimeZoneSelect
+ autoload :UrlField
+ autoload :WeekField
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
index 787039c82e..991f32cea2 100644
--- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
@@ -18,7 +18,7 @@ module ActionView
end
def label(label_html_options={}, &block)
- html_options = label_html_options.merge(@input_html_options)
+ html_options = @input_html_options.slice(:index, :namespace).merge(label_html_options)
@template_object.label(@object_name, @sanitized_attribute_name, @text, html_options, &block)
end
end
diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb
index 180aa9ac27..6335e3dd4d 100644
--- a/actionview/lib/action_view/helpers/tags/label.rb
+++ b/actionview/lib/action_view/helpers/tags/label.rb
@@ -30,14 +30,13 @@ module ActionView
add_default_name_and_id_for_value(tag_value, name_and_id)
options.delete("index")
options.delete("namespace")
- options.delete("multiple")
options["for"] = name_and_id["id"] unless options.key?("for")
if block_given?
content = @template_object.capture(&block)
else
content = if @content.blank?
- @object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1')
+ @object_name.gsub!(/\[(.*)_attributes\]\[\d+\]/, '.\1')
method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name
if object.respond_to?(:to_model)
diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb
index c23d605c5f..7cfbca5b6f 100644
--- a/actionview/lib/action_view/helpers/text_helper.rb
+++ b/actionview/lib/action_view/helpers/text_helper.rb
@@ -31,6 +31,8 @@ module ActionView
include SanitizeHelper
include TagHelper
+ include OutputSafetyHelper
+
# The preferred method of outputting text in your views is to use the
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
# do not operate as expected in an eRuby code block. If you absolutely must
@@ -80,6 +82,9 @@ module ActionView
# # => "And they f... (continued)"
#
# truncate("<p>Once upon a time in a world far far away</p>")
+ # # => "&lt;p&gt;Once upon a time in a wo..."
+ #
+ # truncate("<p>Once upon a time in a world far far away</p>", escape: false)
# # => "<p>Once upon a time in a wo..."
#
# truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
@@ -268,7 +273,7 @@ module ActionView
content_tag(wrapper_tag, nil, html_options)
else
paragraphs.map! { |paragraph|
- content_tag(wrapper_tag, paragraph, html_options, options[:sanitize])
+ content_tag(wrapper_tag, raw(paragraph), html_options)
}.join("\n\n").html_safe
end
end
@@ -314,7 +319,7 @@ module ActionView
options = values.extract_options!
name = options.fetch(:name, 'default')
- values.unshift(first_value)
+ values.unshift(*first_value)
cycle = get_cycle(name)
unless cycle && cycle.values == values
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index ad8eb47f1f..0bc40874d9 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -1,24 +1,14 @@
require 'action_view/helpers/tag_helper'
require 'i18n/exceptions'
-module I18n
- class ExceptionHandler
- include Module.new {
- def call(exception, locale, key, options)
- exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super
- end
- }
- end
-end
-
module ActionView
# = Action View Translation Helpers
module Helpers
module TranslationHelper
# Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
#
- # First, it'll pass the <tt>rescue_format: :html</tt> option to I18n so that any
- # thrown +MissingTranslation+ messages will be turned into inline spans that
+ # First, it will ensure that any thrown +MissingTranslation+ messages will be turned
+ # into inline spans that:
#
# * have a "translation-missing" class set,
# * contain the missing key as a title attribute and
@@ -44,8 +34,17 @@ module ActionView
# naming convention helps to identify translations that include HTML tags so that
# you know what kind of output to expect when you call translate in a template.
def translate(key, options = {})
- options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
options[:default] = wrap_translate_defaults(options[:default]) if options[:default]
+
+ # If the user has specified rescue_format then pass it all through, otherwise use
+ # raise and do the work ourselves
+ options[:raise] ||= ActionView::Base.raise_on_missing_translations
+
+ raise_error = options[:raise] || options.key?(:rescue_format)
+ unless raise_error
+ options[:raise] = true
+ end
+
if html_safe_translation_key?(key)
html_safe_options = options.dup
options.except(*I18n::RESERVED_KEYS).each do |name, value|
@@ -59,6 +58,11 @@ module ActionView
else
I18n.translate(scope_key_by_partial(key), options)
end
+ rescue I18n::MissingTranslationData => e
+ raise e if raise_error
+
+ keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
+ content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
end
alias :t :translate
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index 8b78bd32e0..51379d433f 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -82,7 +82,7 @@ module ActionView
# to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
# disabled clicking the link will have no effect. If you are relying on the
# POST behavior, you should check for it in your controller's action by using
- # the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>:patch</tt>, or <tt>put?</tt>.
+ # the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>patch?</tt>, or <tt>put?</tt>.
# * <tt>remote: true</tt> - This will allow the unobtrusive JavaScript
# driver to make an Ajax request to the URL in question instead of following
# the link. The drivers each provide mechanisms for listening for the
@@ -232,6 +232,11 @@ module ActionView
# # <div><input value="New" type="submit" /></div>
# # </form>"
#
+ # <%= button_to "New", new_articles_path %>
+ # # => "<form method="post" action="/articles/new" class="button_to">
+ # # <div><input value="New" type="submit" /></div>
+ # # </form>"
+ #
# <%= button_to [:make_happy, @user] do %>
# Make happy <strong><%= @user.name %></strong>
# <% end %>
@@ -314,8 +319,8 @@ module ActionView
inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
if params
- params.each do |name, value|
- inner_tags.safe_concat tag(:input, type: "hidden", name: name, value: value.to_param)
+ params.each do |param_name, value|
+ inner_tags.safe_concat tag(:input, type: "hidden", name: param_name, value: value.to_param)
end
end
content_tag('form', content_tag('div', inner_tags), form_options)
diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb
index d8de1d95df..9ee05bd816 100644
--- a/actionview/lib/action_view/layouts.rb
+++ b/actionview/lib/action_view/layouts.rb
@@ -221,7 +221,7 @@ module ActionView
# This module is mixed in if layout conditions are provided. This means
# that if no layout conditions are used, this method is not used
module LayoutConditions # :nodoc:
- private
+ private
# Determines whether the current action has a layout definition by
# checking the action name against the :only and :except conditions
@@ -269,15 +269,6 @@ module ActionView
_write_layout_method
end
- # If no layout is supplied, look for a template named the return
- # value of this method.
- #
- # ==== Returns
- # * <tt>String</tt> - A template name
- def _implied_layout_name # :nodoc:
- controller_path
- end
-
# Creates a _layout method to be called by _default_layout .
#
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
@@ -335,6 +326,17 @@ module ActionView
private :_layout
RUBY
end
+
+ private
+
+ # If no layout is supplied, look for a template named the return
+ # value of this method.
+ #
+ # ==== Returns
+ # * <tt>String</tt> - A template name
+ def _implied_layout_name # :nodoc:
+ controller_path
+ end
end
def _normalize_options(options) # :nodoc:
@@ -418,7 +420,7 @@ module ActionView
end
def _include_layout?(options)
- (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
+ (options.keys & [:body, :text, :plain, :html, :inline, :partial]).empty? || options.key?(:layout)
end
end
end
diff --git a/actionview/lib/action_view/log_subscriber.rb b/actionview/lib/action_view/log_subscriber.rb
index 354a136894..6c8d9cb5bf 100644
--- a/actionview/lib/action_view/log_subscriber.rb
+++ b/actionview/lib/action_view/log_subscriber.rb
@@ -5,7 +5,12 @@ module ActionView
#
# Provides functionality so that Rails can output logs from Action View.
class LogSubscriber < ActiveSupport::LogSubscriber
- VIEWS_PATTERN = /^app\/views\//.freeze
+ VIEWS_PATTERN = /^app\/views\//
+
+ def initialize
+ @root = nil
+ super
+ end
def render_template(event)
return unless logger.info?
@@ -23,8 +28,15 @@ module ActionView
protected
+ EMPTY = ''
def from_rails_root(string)
- string.sub("#{Rails.root}/", "").sub(VIEWS_PATTERN, "")
+ string = string.sub(rails_root, EMPTY)
+ string.sub!(VIEWS_PATTERN, EMPTY)
+ string
+ end
+
+ def rails_root
+ @root ||= "#{Rails.root}/"
end
end
end
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index f9d5b97fe3..855fed0190 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -1,6 +1,7 @@
require 'thread_safe'
require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/module/attribute_accessors'
+require 'action_view/template/resolver'
module ActionView
# = Action View Lookup Context
@@ -52,6 +53,7 @@ module ActionView
locales
end
register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
+ register_detail(:variants) { [] }
register_detail(:handlers){ Template::Handlers.extensions }
class DetailsKey #:nodoc:
@@ -62,6 +64,13 @@ module ActionView
@details_keys = ThreadSafe::Cache.new
def self.get(details)
+ if details[:formats]
+ details = details.dup
+ syms = Set.new Mime::SET.symbols
+ details[:formats] = details[:formats].select { |v|
+ syms.include? v
+ }
+ end
@details_keys[details] ||= new
end
@@ -150,7 +159,14 @@ module ActionView
def detail_args_for(options)
return @details, details_key if options.empty? # most common path.
user_details = @details.merge(options)
- [user_details, DetailsKey.get(user_details)]
+
+ if @cache
+ details_key = DetailsKey.get(user_details)
+ else
+ details_key = nil
+ end
+
+ [user_details, details_key]
end
# Support legacy foo.erb names even though we now ignore .erb
diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb
index c2783f6377..81f9c40b85 100644
--- a/actionview/lib/action_view/railtie.rb
+++ b/actionview/lib/action_view/railtie.rb
@@ -36,19 +36,12 @@ module ActionView
end
end
- initializer "action_view.setup_action_pack", before: :add_view_paths do |app|
+ initializer "action_view.setup_action_pack" do |app|
ActiveSupport.on_load(:action_controller) do
- ActionController::Base.superclass.send(:include, ActionView::Layouts)
ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
end
end
- initializer "action_view.setup_action_mailer", before: :add_view_paths do |app|
- ActiveSupport.on_load(:action_mailer) do
- ActionMailer::Base.send(:include, ActionView::Layouts)
- end
- end
-
rake_tasks do
load "action_view/tasks/dependencies.rake"
end
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index 821026268a..36f17f01fd 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -159,7 +159,7 @@ module ActionView
# </div>
#
# If a collection is given, the layout will be rendered once for each item in
- # the collection. Just think these two snippets have the same output:
+ # the collection. For example, these two snippets have the same output:
#
# <%# app/views/users/_user.html.erb %>
# Name: <%= user.name %>
diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
index 9cf6eb0c65..3ab2cd36fc 100644
--- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb
+++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
@@ -58,7 +58,7 @@ module ActionView
def delayed_render(buffer, template, layout, view, locals)
# Wrap the given buffer in the StreamingBuffer and pass it to the
- # underlying template handler. Now, everytime something is concatenated
+ # underlying template handler. Now, every time something is concatenated
# to the buffer, it is not appended to an array, but streamed straight
# to the client.
output = ActionView::StreamingBuffer.new(buffer)
diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb
index 668831dff3..be17097428 100644
--- a/actionview/lib/action_view/renderer/template_renderer.rb
+++ b/actionview/lib/action_view/renderer/template_renderer.rb
@@ -21,8 +21,14 @@ module ActionView
def determine_template(options) #:nodoc:
keys = options.fetch(:locals, {}).keys
- if options.key?(:text)
+ if options.key?(:body)
+ Template::Text.new(options[:body])
+ elsif options.key?(:text)
Template::Text.new(options[:text], formats.first)
+ elsif options.key?(:plain)
+ Template::Text.new(options[:plain])
+ elsif options.key?(:html)
+ Template::HTML.new(options[:html], formats.first)
elsif options.key?(:file)
with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
elsif options.key?(:inline)
@@ -35,7 +41,7 @@ module ActionView
find_template(options[:template], options[:prefixes], false, keys, @details)
end
else
- raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option."
+ raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file, :plain, :text or :body option."
end
end
diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb
index 18a0788f8e..017302d40f 100644
--- a/actionview/lib/action_view/rendering.rb
+++ b/actionview/lib/action_view/rendering.rb
@@ -77,13 +77,6 @@ module ActionView
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
end
- # Find and renders a template based on the options given.
- # :api: private
- def _render_template(options) #:nodoc:
- lookup_context.rendered_format = nil if options[:formats]
- view_renderer.render(view_context, options)
- end
-
def render_to_body(options = {})
_process_options(options)
_render_template(options)
@@ -95,8 +88,19 @@ module ActionView
private
+ # Find and render a template based on the options given.
+ # :api: private
+ def _render_template(options) #:nodoc:
+ variant = options[:variant]
+
+ lookup_context.rendered_format = nil if options[:formats]
+ lookup_context.variants = variant if variant
+
+ view_renderer.render(view_context, options)
+ end
+
# Assign the rendered format to lookup context.
- def _process_format(format) #:nodoc:
+ def _process_format(format, options = {}) #:nodoc:
super
lookup_context.formats = [format.to_sym]
lookup_context.rendered_format = lookup_context.formats.first
diff --git a/actionview/lib/action_view/routing_url_for.rb b/actionview/lib/action_view/routing_url_for.rb
index f10e7e88ba..33be06cbf7 100644
--- a/actionview/lib/action_view/routing_url_for.rb
+++ b/actionview/lib/action_view/routing_url_for.rb
@@ -83,6 +83,8 @@ module ActionView
super
when :back
_back_url
+ when Array
+ polymorphic_path(options, options.extract_options!)
else
polymorphic_path(options)
end
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index 9b0619f1aa..9d39d02a37 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -90,13 +90,14 @@ module ActionView
eager_autoload do
autoload :Error
autoload :Handlers
+ autoload :HTML
autoload :Text
autoload :Types
end
extend Template::Handlers
- attr_accessor :locals, :formats, :virtual_path
+ attr_accessor :locals, :formats, :variants, :virtual_path
attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
@@ -122,6 +123,7 @@ module ActionView
@virtual_path = details[:virtual_path]
@updated_at = details[:updated_at] || Time.now
@formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
+ @variants = [details[:variant]]
@compile_mutex = Mutex.new
end
diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb
index a89d51221e..743ef6de0a 100644
--- a/actionview/lib/action_view/template/error.rb
+++ b/actionview/lib/action_view/template/error.rb
@@ -41,6 +41,9 @@ module ActionView
'template'
end
+ if partial && path.present?
+ path = path.sub(%r{([^/]+)$}, "_\\1")
+ end
searched_paths = prefixes.map { |prefix| [prefix, path].join("/") }
out = "Missing #{template_type} #{searched_paths.join(", ")} with #{details.inspect}. Searched in:\n"
@@ -56,13 +59,13 @@ module ActionView
class Error < ActionViewError #:nodoc:
SOURCE_CODE_RADIUS = 3
- attr_reader :original_exception, :backtrace
+ attr_reader :original_exception
def initialize(template, original_exception)
super(original_exception.message)
@template, @original_exception = template, original_exception
@sub_templates = nil
- @backtrace = original_exception.backtrace
+ set_backtrace(original_exception.backtrace)
end
def file_name
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index c8a0059596..4523060442 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -18,7 +18,7 @@ module ActionView
src << "@output_buffer.safe_append='"
src << "\n" * @newline_pending if @newline_pending > 0
src << escape_text(text)
- src << "';"
+ src << "'.freeze;"
@newline_pending = 0
end
@@ -67,7 +67,7 @@ module ActionView
def flush_newline_if_pending(src)
if @newline_pending > 0
- src << "@output_buffer.safe_append='#{"\n" * @newline_pending}';"
+ src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
@newline_pending = 0
end
end
diff --git a/actionview/lib/action_view/template/html.rb b/actionview/lib/action_view/template/html.rb
new file mode 100644
index 0000000000..0321f819b5
--- /dev/null
+++ b/actionview/lib/action_view/template/html.rb
@@ -0,0 +1,34 @@
+module ActionView #:nodoc:
+ # = Action View HTML Template
+ class Template
+ class HTML #:nodoc:
+ attr_accessor :type
+
+ def initialize(string, type = nil)
+ @string = string.to_s
+ @type = Types[type] || type if type
+ @type ||= Types[:html]
+ end
+
+ def identifier
+ 'html template'
+ end
+
+ def inspect
+ 'html template'
+ end
+
+ def to_str
+ ERB::Util.h(@string)
+ end
+
+ def render(*args)
+ to_str
+ end
+
+ def formats
+ [@type.respond_to?(:ref) ? @type.ref : @type.to_s]
+ end
+ end
+ end
+end
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index 3304605c1a..403824bd8e 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -1,6 +1,6 @@
require "pathname"
require "active_support/core_ext/class"
-require "active_support/core_ext/class/attribute_accessors"
+require "active_support/core_ext/module/attribute_accessors"
require "action_view/template"
require "thread"
require "thread_safe"
@@ -154,7 +154,8 @@ module ActionView
cached = nil
templates.each do |t|
t.locals = locals
- t.formats = details[:formats] || [:html] if t.formats.empty?
+ t.formats = details[:formats] || [:html] if t.formats.empty?
+ t.variants = details[:variants] || [] if t.variants.empty?
t.virtual_path ||= (cached ||= build_path(*path_info))
end
end
@@ -162,8 +163,8 @@ module ActionView
# An abstract class that implements a Resolver with path semantics.
class PathResolver < Resolver #:nodoc:
- EXTENSIONS = [:locale, :formats, :handlers]
- DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}"
+ EXTENSIONS = { :locale => ".", :formats => ".", :variants => "+", :handlers => "." }
+ DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
def initialize(pattern=nil)
@pattern = pattern || DEFAULT_PATTERN
@@ -189,13 +190,15 @@ module ActionView
}
template_paths.map { |template|
- handler, format = extract_handler_and_format(template, formats)
- contents = File.binread template
+ handler, format, variant = extract_handler_and_format_and_variant(template, formats)
+ contents = File.binread(template)
Template.new(contents, File.expand_path(template), handler,
:virtual_path => path.virtual,
:format => format,
- :updated_at => mtime(template))
+ :variant => variant,
+ :updated_at => mtime(template)
+ )
}
end
@@ -228,7 +231,7 @@ module ActionView
# Extract handler and formats from path. If a format cannot be a found neither
# from the path, or the handler, we should return the array of formats given
# to the resolver.
- def extract_handler_and_format(path, default_formats)
+ def extract_handler_and_format_and_variant(path, default_formats)
pieces = File.basename(path).split(".")
pieces.shift
@@ -240,8 +243,10 @@ module ActionView
end
handler = Template.handler_for_extension(extension)
- format = pieces.last && Template::Types[pieces.last]
- [handler, format]
+ format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
+ format &&= Template::Types[format]
+
+ [handler, format, variant]
end
end
@@ -303,12 +308,13 @@ module ActionView
# An Optimized resolver for Rails' most common case.
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
def build_query(path, details)
- exts = EXTENSIONS.map { |ext| details[ext] }
query = escape_entry(File.join(@path, path))
- query + exts.map { |ext|
- "{#{ext.compact.uniq.map { |e| ".#{e}," }.join}}"
- }.join
+ exts = EXTENSIONS.map do |ext, prefix|
+ "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
+ end.join
+
+ query + exts
end
end
diff --git a/actionview/lib/action_view/template/text.rb b/actionview/lib/action_view/template/text.rb
index 859c7bc3ce..04f5b8d17a 100644
--- a/actionview/lib/action_view/template/text.rb
+++ b/actionview/lib/action_view/template/text.rb
@@ -27,7 +27,7 @@ module ActionView #:nodoc:
end
def formats
- [@type.to_sym]
+ [@type.respond_to?(:ref) ? @type.ref : @type.to_s]
end
end
end
diff --git a/actionview/lib/action_view/template/types.rb b/actionview/lib/action_view/template/types.rb
index db77cb5d19..b84e0281ae 100644
--- a/actionview/lib/action_view/template/types.rb
+++ b/actionview/lib/action_view/template/types.rb
@@ -1,5 +1,5 @@
require 'set'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/attribute_accessors'
module ActionView
class Template
diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb
index 7afa2fa613..dfb7d463b4 100644
--- a/actionview/lib/action_view/testing/resolvers.rb
+++ b/actionview/lib/action_view/testing/resolvers.rb
@@ -21,7 +21,7 @@ module ActionView #:nodoc:
def query(path, exts, formats)
query = ""
- EXTENSIONS.each do |ext|
+ EXTENSIONS.each_key do |ext|
query << '(' << exts[ext].map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
end
query = /^(#{Regexp.escape(path)})#{query}$/
@@ -30,9 +30,13 @@ module ActionView #:nodoc:
@hash.each do |_path, array|
source, updated_at = array
next unless _path =~ query
- handler, format = extract_handler_and_format(_path, formats)
+ handler, format, variant = extract_handler_and_format_and_variant(_path, formats)
templates << Template.new(source, _path, handler,
- :virtual_path => path.virtual, :format => format, :updated_at => updated_at)
+ :virtual_path => path.virtual,
+ :format => format,
+ :variant => variant,
+ :updated_at => updated_at
+ )
end
templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
@@ -41,8 +45,8 @@ module ActionView #:nodoc:
class NullResolver < PathResolver
def query(path, exts, formats)
- handler, format = extract_handler_and_format(path, formats)
- [ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format)]
+ handler, format, variant = extract_handler_and_format_and_variant(path, formats)
+ [ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format, :variant => variant)]
end
end
diff --git a/actionview/lib/action_view/vendor/html-scanner/html/node.rb b/actionview/lib/action_view/vendor/html-scanner/html/node.rb
index 7e7cd4f7b6..27f0f2f6f8 100644
--- a/actionview/lib/action_view/vendor/html-scanner/html/node.rb
+++ b/actionview/lib/action_view/vendor/html-scanner/html/node.rb
@@ -71,12 +71,12 @@ module HTML #:nodoc:
@line, @position = line, pos
end
- # Return a textual representation of the node.
+ # Returns a textual representation of the node.
def to_s
@children.join()
end
- # Return false (subclasses must override this to provide specific matching
+ # Returns false (subclasses must override this to provide specific matching
# behavior.) +conditions+ may be of any type.
def match(conditions)
false
diff --git a/actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb b/actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb
index 30b6b8b141..ed34eecf55 100644
--- a/actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb
+++ b/actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb
@@ -1,6 +1,6 @@
require 'set'
require 'cgi'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/attribute_accessors'
module HTML
class Sanitizer
diff --git a/actionview/lib/action_view/vendor/html-scanner/html/selector.rb b/actionview/lib/action_view/vendor/html-scanner/html/selector.rb
index 7f8609c408..dfdd724b9b 100644
--- a/actionview/lib/action_view/vendor/html-scanner/html/selector.rb
+++ b/actionview/lib/action_view/vendor/html-scanner/html/selector.rb
@@ -488,7 +488,7 @@ module HTML
end
- # Return the next element after this one. Skips sibling text nodes.
+ # Returns the next element after this one. Skips sibling text nodes.
#
# With the +name+ argument, returns the next element with that name,
# skipping other sibling elements.
diff --git a/actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb b/actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb
index 8ac8d34430..adf4e45930 100644
--- a/actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb
+++ b/actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb
@@ -30,7 +30,7 @@ module HTML #:nodoc:
@current_line = 1
end
- # Return the next token in the sequence, or +nil+ if there are no more tokens in
+ # Returns the next token in the sequence, or +nil+ if there are no more tokens in
# the stream.
def next
return nil if @scanner.eos?
diff --git a/actionview/lib/action_view/version.rb b/actionview/lib/action_view/version.rb
index 094dd474df..f55d3fdaef 100644
--- a/actionview/lib/action_view/version.rb
+++ b/actionview/lib/action_view/version.rb
@@ -1,11 +1,8 @@
+require_relative 'gem_version'
+
module ActionView
- # Returns the version of the currently loaded ActionView as a Gem::Version
+ # Returns the version of the currently loaded ActionView as a <tt>Gem::Version</tt>
def self.version
- Gem::Version.new "4.1.0.beta"
- end
-
- module VERSION #:nodoc:
- MAJOR, MINOR, TINY, PRE = ActionView.version.segments
- STRING = ActionView.version.to_s
+ gem_version
end
end