aboutsummaryrefslogtreecommitdiffstats
path: root/actionview
diff options
context:
space:
mode:
Diffstat (limited to 'actionview')
-rw-r--r--actionview/CHANGELOG.md55
-rwxr-xr-xactionview/bin/test4
-rw-r--r--actionview/lib/action_view/base.rb4
-rw-r--r--actionview/lib/action_view/dependency_tracker.rb38
-rw-r--r--actionview/lib/action_view/digestor.rb19
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb33
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb65
-rw-r--r--actionview/lib/action_view/helpers/capture_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb38
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb41
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb57
-rw-r--r--actionview/lib/action_view/helpers/javascript_helper.rb6
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/rendering_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/sanitize_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/tag_helper.rb27
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb12
-rw-r--r--actionview/lib/action_view/helpers/text_helper.rb25
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb9
-rw-r--r--actionview/lib/action_view/layouts.rb25
-rw-r--r--actionview/lib/action_view/lookup_context.rb15
-rw-r--r--actionview/lib/action_view/path_set.rb9
-rw-r--r--actionview/lib/action_view/railtie.rb14
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb4
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb2
-rw-r--r--actionview/lib/action_view/rendering.rb4
-rw-r--r--actionview/lib/action_view/routing_url_for.rb10
-rw-r--r--actionview/lib/action_view/template.rb34
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb11
-rw-r--r--actionview/lib/action_view/template/resolver.rb36
-rw-r--r--actionview/lib/action_view/test_case.rb16
-rw-r--r--actionview/lib/action_view/view_paths.rb6
-rw-r--r--actionview/test/abstract_unit.rb14
-rw-r--r--actionview/test/actionpack/abstract/layouts_test.rb4
-rw-r--r--actionview/test/actionpack/abstract/render_test.rb3
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb40
-rw-r--r--actionview/test/actionpack/controller/render_test.rb122
-rw-r--r--actionview/test/actionpack/controller/view_paths_test.rb4
-rw-r--r--actionview/test/activerecord/render_partial_with_record_identification_test.rb18
-rw-r--r--actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb1
-rw-r--r--actionview/test/fixtures/actionpack/layouts/standard.text.erb1
-rw-r--r--actionview/test/fixtures/actionpack/test/hyphen-ated.erb2
-rw-r--r--actionview/test/fixtures/digestor/events/_completed.html.erb0
-rw-r--r--actionview/test/fixtures/digestor/events/index.html.erb1
-rw-r--r--actionview/test/template/asset_tag_helper_test.rb5
-rw-r--r--actionview/test/template/digestor_test.rb73
-rw-r--r--actionview/test/template/form_helper_test.rb18
-rw-r--r--actionview/test/template/form_options_helper_test.rb7
-rw-r--r--actionview/test/template/form_tag_helper_test.rb47
-rw-r--r--actionview/test/template/record_tag_helper_test.rb3
-rw-r--r--actionview/test/template/render_test.rb17
-rw-r--r--actionview/test/template/template_test.rb32
-rw-r--r--actionview/test/template/test_case_test.rb60
-rw-r--r--actionview/test/template/text_helper_test.rb16
-rw-r--r--actionview/test/template/url_helper_test.rb7
56 files changed, 724 insertions, 402 deletions
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 201e118971..90c9c2171c 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,58 @@
+* Make `disable_with` the default behavior for submit tags. Disables the
+ button on submit to prevent double submits.
+
+ *Justin Schiff*
+
+* Add a break_sequence option to word_wrap so you can specify a custom break.
+
+ * Mauricio Gomez *
+
+* Add wildcard matching to explicit dependencies.
+
+ Turns:
+
+ ```erb
+ <% # Template Dependency: recordings/threads/events/subscribers_changed %>
+ <% # Template Dependency: recordings/threads/events/completed %>
+ <% # Template Dependency: recordings/threads/events/uncompleted %>
+ ```
+
+ Into:
+
+ ```erb
+ <% # Template Dependency: recordings/threads/events/* %>
+ ```
+
+ *Kasper Timm Hansen*
+
+* Allow defining explicit collection caching using a `# Template Collection: ...`
+ directive inside templates.
+
+ *Dov Murik*
+
+* Asset helpers raise `ArgumentError` when `nil` is passed as a source.
+
+ *Anton Kolomiychuk*
+
+* Always attach the template digest to the cache key for collection caching
+ even when `virtual_path` is not available from the view context.
+ Which could happen if the rendering was done directly in the controller
+ and not in a template.
+
+ Fixes #20535
+
+ *Roque Pinel*
+
+* Improve detection of partial templates eligible for collection caching,
+ now allowing multi-line comments at the beginning of the template file.
+
+ *Dov Murik*
+
+* Raise an ArgumentError when a false value for `include_blank` is passed to a
+ required select field (to comply with the HTML5 spec).
+
+ *Grey Baker*
+
* Do not put partial name to `local_assigns` when rendering without
an object or a collection.
diff --git a/actionview/bin/test b/actionview/bin/test
new file mode 100755
index 0000000000..404cabba51
--- /dev/null
+++ b/actionview/bin/test
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+require File.expand_path("../tools/test", COMPONENT_ROOT)
+exit Minitest.run(ARGV)
diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb
index 43124bb904..72ca6f7ec6 100644
--- a/actionview/lib/action_view/base.rb
+++ b/actionview/lib/action_view/base.rb
@@ -161,6 +161,10 @@ module ActionView #:nodoc:
cattr_accessor :raise_on_missing_translations
@@raise_on_missing_translations = false
+ # Specify whether submit_tag should automatically disable on click
+ cattr_accessor :automatically_disable_submit_tag
+ @@automatically_disable_submit_tag = true
+
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 7a7e116dbb..6e8c7f8203 100644
--- a/actionview/lib/action_view/dependency_tracker.rb
+++ b/actionview/lib/action_view/dependency_tracker.rb
@@ -1,16 +1,18 @@
require 'thread_safe'
+require 'action_view/path_set'
module ActionView
class DependencyTracker # :nodoc:
@trackers = ThreadSafe::Cache.new
- def self.find_dependencies(name, template)
+ def self.find_dependencies(name, template, view_paths = nil)
tracker = @trackers[template.handler]
+ return [] unless tracker.present?
- if tracker.present?
- tracker.call(name, template)
+ if tracker.respond_to?(:supports_view_paths?) && tracker.supports_view_paths?
+ tracker.call(name, template, view_paths)
else
- []
+ tracker.call(name, template)
end
end
@@ -82,12 +84,16 @@ module ActionView
(?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
/xm
- def self.call(name, template)
- new(name, template).dependencies
+ def self.supports_view_paths? # :nodoc:
+ true
+ end
+
+ def self.call(name, template, view_paths = nil)
+ new(name, template, view_paths).dependencies
end
- def initialize(name, template)
- @name, @template = name, template
+ def initialize(name, template, view_paths = nil)
+ @name, @template, @view_paths = name, template, view_paths
end
def dependencies
@@ -142,8 +148,22 @@ module ActionView
end
end
+ def resolve_directories(wildcard_dependencies)
+ return [] unless @view_paths
+
+ wildcard_dependencies.each_with_object([]) do |query, templates|
+ @view_paths.find_all_with_query(query).each do |template|
+ templates << "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
+ end
+ end
+ end
+
def explicit_dependencies
- source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
+ dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
+
+ wildcards, explicits = dependencies.partition { |dependency| dependency[-1] == '*' }
+
+ (explicits + resolve_directories(wildcards)).uniq
end
end
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 1f103786cb..9a8a4feb2e 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -8,11 +8,18 @@ module ActionView
@@cache = ThreadSafe::Cache.new
@@digest_monitor = Monitor.new
+ class PerRequestDigestCacheExpiry < Struct.new(:app) # :nodoc:
+ def call(env)
+ ActionView::Digestor.cache.clear
+ app.call(env)
+ end
+ end
+
class << self
# Supported options:
#
# * <tt>name</tt> - Template name
- # * <tt>finder</tt> - An instance of ActionView::LookupContext
+ # * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
# * <tt>dependencies</tt> - An array of dependent views
# * <tt>partial</tt> - Specifies whether the template is a partial
def digest(options)
@@ -41,10 +48,7 @@ module ActionView
Digestor
end
- 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
+ @@cache[cache_key] = stored_digest = klass.new(options).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
@@ -68,9 +72,10 @@ module ActionView
end
def dependencies
- DependencyTracker.find_dependencies(name, template)
+ DependencyTracker.find_dependencies(name, template, finder.view_paths)
rescue ActionView::MissingTemplate
- [] # File doesn't exist, so no dependencies
+ logger.try :error, " '#{name}' file doesn't exist, so no dependencies"
+ []
end
def nested_dependencies
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index e32f8e219e..e506c782d6 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -60,7 +60,7 @@ module ActionView
tag_options = {
"src" => path_to_javascript(source, path_options)
}.merge!(options)
- content_tag(:script, "", tag_options)
+ content_tag("script".freeze, "", tag_options)
}.join("\n").html_safe
end
@@ -237,7 +237,7 @@ module ActionView
# image_alt('underscored_file_name.png')
# # => Underscored file name
def image_alt(src)
- File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').tr('-_', ' ').capitalize
+ File.basename(src, '.*'.freeze).sub(/-[[:xdigit:]]{32}\z/, ''.freeze).tr('-_'.freeze, ' '.freeze).capitalize
end
# Returns an HTML video tag for the +sources+. If +sources+ is a string,
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index ef4a6c98c0..717b326740 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -31,26 +31,33 @@ module ActionView
# stylesheet_link_tag("application")
# # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
#
- # Browsers typically open at most two simultaneous connections to a single
- # host, which means your assets often have to wait for other assets to finish
- # downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
- # +asset_host+. For example, "assets%d.example.com". If that wildcard is
- # present Rails distributes asset requests among the corresponding four hosts
- # "assets0.example.com", ..., "assets3.example.com". With this trick browsers
- # will open eight simultaneous connections rather than two.
+ # Browsers open a limited number of simultaneous connections to a single
+ # host. The exact number varies by browser and version. This limit may cause
+ # some asset downloads to wait for previous assets to finish before they can
+ # begin. You can use the <tt>%d</tt> wildcard in the +asset_host+ to
+ # distribute the requests over four hosts. For example,
+ # <tt>assets%d.example.com<tt> will spread the asset requests over
+ # "assets0.example.com", ..., "assets3.example.com".
#
# image_tag("rails.png")
# # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
# stylesheet_link_tag("application")
# # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
#
- # To do this, you can either setup four actual hosts, or you can use wildcard
- # DNS to CNAME the wildcard to a single asset host. You can read more about
- # setting up your DNS CNAME records from your ISP.
+ # This may improve the asset loading performance of your application.
+ # It is also possible the combination of additional connection overhead
+ # (DNS, SSL) and the overall browser connection limits may result in this
+ # solution being slower. You should be sure to measure your actual
+ # performance across targeted browsers both before and after this change.
+ #
+ # To implement the corresponding hosts you can either setup four actual
+ # hosts or use wildcard DNS to CNAME the wildcard to a single asset host.
+ # You can read more about setting up your DNS CNAME records from your ISP.
#
# Note: This is purely a browser performance optimization and is not meant
# for server load balancing. See http://www.die.net/musings/page_load_time/
- # for background.
+ # for background and http://www.browserscope.org/?category=network for
+ # connection limit data.
#
# Alternatively, you can exert more control over the asset host by setting
# +asset_host+ to a proc like this:
@@ -121,11 +128,13 @@ module ActionView
# asset_path "application", type: :stylesheet # => /assets/application.css
# asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
def asset_path(source, options = {})
+ raise ArgumentError, "nil is not a valid asset source" if source.nil?
+
source = source.to_s
return "" unless source.present?
return source if source =~ URI_REGEXP
- tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
+ tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, ''.freeze)
if extname = compute_asset_extname(source, options)
source = "#{source}#{extname}"
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 251764a8de..636d7d4cc3 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -39,7 +39,7 @@ module ActionView
# This will include both records as part of the cache key and updating either of them will
# expire the cache.
#
- # ==== Template digest
+ # ==== \Template digest
#
# The template digest that's added to the cache key is computed by taking an md5 of the
# contents of the entire template file. This ensures that your caches will automatically
@@ -75,7 +75,7 @@ module ActionView
# render(topics) => render("topics/topic")
# render(message.topics) => render("topics/topic")
#
- # It's not possible to derive all render calls like that, though.
+ # It's not possible to derive all render calls like that, though.
# Here are a few examples of things that can't be derived:
#
# render group_of_attachments
@@ -98,14 +98,26 @@ module ActionView
# <%# Template Dependency: todolists/todolist %>
# <%= render_sortable_todolists @project.todolists %>
#
- # The pattern used to match these is /# Template Dependency: ([^ ]+)/,
+ # In some cases, like a single table inheritance setup, you might have
+ # a bunch of explicit dependencies. Instead of writing every template out,
+ # you can use a wildcard to match any template in a directory:
+ #
+ # <%# Template Dependency: events/* %>
+ # <%= render_categorizable_events @person.events %>
+ #
+ # This marks every template in the directory as a dependency. To find those
+ # templates, the wildcard path must be absolutely defined from app/views or paths
+ # otherwise added with +prepend_view_path+ or +append_view_path+.
+ # This way the wildcard for `app/views/recordings/events` would be `recordings/events/*` etc.
+ #
+ # The pattern used to match explicit dependencies is <tt>/# Template Dependency: (\S+)/</tt>,
# so it's important that you type it out just so.
# You can only declare one template dependency per line.
#
# === External dependencies
#
- # If you use a helper method, for example, inside a cached block and
- # you then update that helper, you'll have to bump the cache as well.
+ # If you use a helper method, for example, inside a cached block and
+ # you then update that helper, you'll have to bump the cache as well.
# It doesn't really matter how you do it, but the md5 of the template file
# must change. One recommendation is to simply be explicit in a comment, like:
#
@@ -130,14 +142,29 @@ module ActionView
# The collection can then automatically use any cached renders for that
# template by reading them at once instead of one by one.
#
- # See ActionView::Template::Handlers::ERB.resource_cache_call_pattern for
- # more information on what cache calls make a template eligible for this
+ # See ActionView::Template::Handlers::ERB.resource_cache_call_pattern for
+ # more information on what cache calls make a template eligible for this
# collection caching.
#
# The automatic cache multi read can be turned off like so:
#
# <%= render @notifications, cache: false %>
- def cache(name = {}, options = nil, &block)
+ #
+ # === Explicit Collection Caching
+ #
+ # If the partial template doesn't start with a clean cache call as
+ # mentioned above, you can still benefit from collection caching by
+ # adding a special comment format anywhere in the template, like:
+ #
+ # <%# Template Collection: notification %>
+ # <% my_helper_that_calls_cache(some_arg, notification) do %>
+ # <%= notification.name %>
+ # <% end %>
+ #
+ # The pattern used to match these is <tt>/# Template Collection: (\S+)/</tt>,
+ # so it's important that you type it out just so.
+ # You can only declare one collection in a partial template file.
+ def cache(name = {}, options = {}, &block)
if controller.respond_to?(:perform_caching) && controller.perform_caching
safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
else
@@ -153,7 +180,7 @@ module ActionView
# <b>All the topics on this project</b>
# <%= render project.topics %>
# <% end %>
- def cache_if(condition, name = {}, options = nil, &block)
+ def cache_if(condition, name = {}, options = {}, &block)
if condition
cache(name, options, &block)
else
@@ -169,22 +196,23 @@ module ActionView
# <b>All the topics on this project</b>
# <%= render project.topics %>
# <% end %>
- def cache_unless(condition, name = {}, options = nil, &block)
+ def cache_unless(condition, name = {}, options = {}, &block)
cache_if !condition, name, options, &block
end
# This helper returns the name of a cache key for a given fragment cache
- # call. By supplying skip_digest: true to cache, the digestion of cache
+ # call. By supplying +skip_digest:+ true to cache, the digestion of cache
# fragments can be manually bypassed. This is useful when cache fragments
# cannot be manually expired unless you know the exact key which is the
# case when using memcached.
- def cache_fragment_name(name = {}, options = nil)
- skip_digest = options && options[:skip_digest]
-
+ #
+ # The digest will be generated using +virtual_path:+ if it is provided.
+ #
+ def cache_fragment_name(name = {}, skip_digest: nil, virtual_path: nil)
if skip_digest
name
else
- fragment_name_with_digest(name)
+ fragment_name_with_digest(name, virtual_path)
end
end
@@ -198,10 +226,11 @@ module ActionView
private
- def fragment_name_with_digest(name) #:nodoc:
- if @virtual_path
+ def fragment_name_with_digest(name, virtual_path) #:nodoc:
+ virtual_path ||= @virtual_path
+ if virtual_path
names = Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name)
- digest = Digestor.digest name: @virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
+ digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
[ *names, digest ]
else
diff --git a/actionview/lib/action_view/helpers/capture_helper.rb b/actionview/lib/action_view/helpers/capture_helper.rb
index a67ba580f1..93c7cba395 100644
--- a/actionview/lib/action_view/helpers/capture_helper.rb
+++ b/actionview/lib/action_view/helpers/capture_helper.rb
@@ -115,7 +115,7 @@ module ActionView
# <li><%= link_to 'Home', action: 'index' %></li>
# <% end %>
#
- # And in other place:
+ # And in another place:
#
# <% content_for :navigation do %>
# <li><%= link_to 'Login', action: 'login' %></li>
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 46ce7cf0be..312e41ee48 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -68,6 +68,27 @@ module ActionView
# 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
+ #
+ # With the <tt>scope</tt> option, you can define a custom scope for Rails
+ # to look up the translation.
+ #
+ # For example you can define the following in your locale (e.g. en.yml).
+ #
+ # datetime:
+ # distance_in_words:
+ # short:
+ # about_x_hours:
+ # one: 'an hour'
+ # other: '%{count} hours'
+ #
+ # See https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/en.yml
+ # for more examples.
+ #
+ # Which will then result in the following:
+ #
+ # from_time = Time.now
+ # distance_of_time_in_words(from_time, from_time + 50.minutes, scope: 'datetime.distance_in_words.short') # => "an hour"
+ # distance_of_time_in_words(from_time, from_time + 3.hours, scope: 'datetime.distance_in_words.short') # => "3 hours"
def distance_of_time_in_words(from_time, to_time = 0, options = {})
options = {
scope: :'datetime.distance_in_words'
@@ -207,6 +228,7 @@ module ActionView
# or the given prompt string.
# * <tt>:with_css_classes</tt> - Set to true if you want assign different styles for 'select' tags. This option
# automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second' for your 'select' tags.
+ # * <tt>:use_hidden</tt> - Set to true if you only want to generate hidden input tags.
#
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
#
@@ -464,7 +486,7 @@ module ActionView
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'second' by default.
#
- # my_time = Time.now + 16.minutes
+ # my_time = Time.now + 16.seconds
#
# # Generates a select field for seconds that defaults to the seconds for the time in my_time.
# select_second(my_time)
@@ -488,7 +510,7 @@ module ActionView
# selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
#
- # my_time = Time.now + 6.hours
+ # my_time = Time.now + 10.minutes
#
# # Generates a select field for minutes that defaults to the minutes for the time in my_time.
# select_minute(my_time)
@@ -660,7 +682,7 @@ module ActionView
content = args.first || I18n.l(date_or_time, :format => format)
datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.iso8601
- content_tag(:time, content, options.reverse_merge(:datetime => datetime), &block)
+ content_tag("time".freeze, content, options.reverse_merge(:datetime => datetime), &block)
end
end
@@ -788,7 +810,7 @@ module ActionView
1.upto(12) do |month_number|
options = { :value => month_number }
options[:selected] = "selected" if month == month_number
- month_options << content_tag(:option, month_name(month_number), options) + "\n"
+ month_options << content_tag("option".freeze, month_name(month_number), options) + "\n"
end
build_select(:month, month_options.join)
end
@@ -950,7 +972,7 @@ module ActionView
tag_options[:selected] = "selected" if selected == i
text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
text = options[:ampm] ? AMPM_TRANSLATION[i] : text
- select_options << content_tag(:option, text, tag_options)
+ select_options << content_tag("option".freeze, text, tag_options)
end
(select_options.join("\n") + "\n").html_safe
@@ -970,11 +992,11 @@ module ActionView
select_options[:class] = [select_options[:class], type].compact.join(' ') if @options[:with_css_classes]
select_html = "\n"
- select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
+ select_html << content_tag("option".freeze, '', :value => '') + "\n" if @options[:include_blank]
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
select_html << select_options_as_html
- (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
+ (content_tag("select".freeze, select_html.html_safe, select_options) + "\n").html_safe
end
# Builds a prompt option tag with supplied options or from default options.
@@ -991,7 +1013,7 @@ module ActionView
I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
end
- prompt ? content_tag(:option, prompt, :value => '') : ''
+ prompt ? content_tag("option".freeze, prompt, :value => '') : ''
end
# Builds hidden input tag for date part and value.
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index d3deee0df3..728e633bbf 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -35,8 +35,8 @@ module ActionView
# <select name="post[person_id]" id="post_person_id">
# <option value="">None</option>
# <option value="1">David</option>
- # <option value="2" selected="selected">Sam</option>
- # <option value="3">Tobias</option>
+ # <option value="2" selected="selected">Eileen</option>
+ # <option value="3">Rafael</option>
# </select>
#
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
@@ -48,8 +48,8 @@ module ActionView
# <select name="post[person_id]" id="post_person_id">
# <option value="">Select Person</option>
# <option value="1">David</option>
- # <option value="2">Sam</option>
- # <option value="3">Tobias</option>
+ # <option value="2">Eileen</option>
+ # <option value="3">Rafael</option>
# </select>
#
# * <tt>:index</tt> - like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
@@ -112,8 +112,8 @@ module ActionView
# <select name="post[person_id]" id="post_person_id">
# <option value=""></option>
# <option value="1" selected="selected">David</option>
- # <option value="2">Sam</option>
- # <option value="3">Tobias</option>
+ # <option value="2">Eileen</option>
+ # <option value="3">Rafael</option>
# </select>
#
# assuming the associated person has ID 1.
@@ -456,7 +456,7 @@ module ActionView
option_tags = options_from_collection_for_select(
group.send(group_method), option_key_method, option_value_method, selected_key)
- content_tag(:optgroup, option_tags, label: group.send(group_label_method))
+ content_tag("optgroup".freeze, option_tags, label: group.send(group_label_method))
end.join.html_safe
end
@@ -528,7 +528,7 @@ module ActionView
body = "".html_safe
if prompt
- body.safe_concat content_tag(:option, prompt_text(prompt), value: "")
+ body.safe_concat content_tag("option".freeze, prompt_text(prompt), value: "")
end
grouped_options.each do |container|
@@ -541,7 +541,7 @@ module ActionView
end
html_attributes = { label: label }.merge!(html_attributes)
- body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), html_attributes)
+ body.safe_concat content_tag("optgroup".freeze, options_for_select(container, selected_key), html_attributes)
end
body
@@ -577,7 +577,7 @@ module ActionView
end
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
- zone_options.safe_concat content_tag(:option, '-------------', value: '', disabled: true)
+ zone_options.safe_concat content_tag("option".freeze, '-------------', value: '', disabled: true)
zone_options.safe_concat "\n"
zones = zones - priority_zones
@@ -707,6 +707,27 @@ module ActionView
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
# b.label(:"data-value" => b.value) { b.check_box + b.text }
# end
+ #
+ # ==== Gotcha
+ #
+ # When no selection is made for a collection of checkboxes most
+ # web browsers will not send any value.
+ #
+ # For example, if we have a +User+ model with +category_ids+ field and we
+ # have the following code in our update action:
+ #
+ # @user.update(params[:user])
+ #
+ # If no +category_ids+ are selected then we can safely assume this field
+ # will not be updated.
+ #
+ # This is possible thanks to a hidden field generated by the helper method
+ # for every collection of checkboxes.
+ # This hidden field is given the same field name as the checkboxes with a
+ # blank value.
+ #
+ # In the rare case you don't want this hidden field, you can pass the
+ # <tt>include_hidden: false</tt> option to the helper method.
def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
end
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 1f76f40138..af684e05c6 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -140,15 +140,15 @@ module ActionView
end
if include_blank
- option_tags = content_tag(:option, include_blank, value: '').safe_concat(option_tags)
+ option_tags = content_tag("option".freeze, include_blank, value: '').safe_concat(option_tags)
end
end
if prompt = options.delete(:prompt)
- option_tags = content_tag(:option, prompt, value: '').safe_concat(option_tags)
+ option_tags = content_tag("option".freeze, prompt, value: '').safe_concat(option_tags)
end
- content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
+ content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -414,42 +414,57 @@ module ActionView
# the form is processed normally, otherwise no action is taken.
# * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
# disabled version of the submit button when the form is submitted. This feature is
- # provided by the unobtrusive JavaScript driver.
+ # provided by the unobtrusive JavaScript driver. To disable this feature for a single submit tag
+ # pass <tt>:data => { disable_with: false }</tt> Defaults to value attribute.
#
# ==== Examples
# submit_tag
- # # => <input name="commit" type="submit" value="Save changes" />
+ # # => <input name="commit" data-disable-with="Save changes" type="submit" value="Save changes" />
#
# submit_tag "Edit this article"
- # # => <input name="commit" type="submit" value="Edit this article" />
+ # # => <input name="commit" data-disable-with="Edit this article" type="submit" value="Edit this article" />
#
# submit_tag "Save edits", disabled: true
- # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
+ # # => <input disabled="disabled" name="commit" data-disable-with="Save edits" type="submit" value="Save edits" />
#
- # submit_tag "Complete sale", data: { disable_with: "Please wait..." }
- # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
+ # submit_tag "Complete sale", data: { disable_with: "Submitting..." }
+ # # => <input name="commit" data-disable-with="Submitting..." type="submit" value="Complete sale" />
#
# submit_tag nil, class: "form_submit"
# # => <input class="form_submit" name="commit" type="submit" />
#
# submit_tag "Edit", class: "edit_button"
- # # => <input class="edit_button" name="commit" type="submit" value="Edit" />
+ # # => <input class="edit_button" data-disable-with="Edit" name="commit" type="submit" value="Edit" />
#
# submit_tag "Save", data: { confirm: "Are you sure?" }
- # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
+ # # => <input name='commit' type='submit' value='Save' data-disable-with="Save" data-confirm="Are you sure?" />
#
def submit_tag(value = "Save changes", options = {})
options = options.stringify_keys
+ tag_options = { "type" => "submit", "name" => "commit", "value" => value }.update(options)
+
+ if ActionView::Base.automatically_disable_submit_tag
+ unless tag_options["data-disable-with"] == false || (tag_options["data"] && tag_options["data"][:disable_with] == false)
+ disable_with_text = tag_options["data-disable-with"]
+ disable_with_text ||= tag_options["data"][:disable_with] if tag_options["data"]
+ disable_with_text ||= value.clone
+ tag_options.deep_merge!("data" => { "disable_with" => disable_with_text })
+ else
+ tag_options.delete("data-disable-with")
+ tag_options["data"].delete(:disable_with) if tag_options["data"]
+ end
+ end
- tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
+ tag :input, tag_options
end
# Creates a button element that defines a <tt>submit</tt> button,
# <tt>reset</tt>button or a generic button which can be used in
# JavaScript, for example. You can use the button tag as a regular
# submit tag but it isn't supported in legacy browsers. However,
- # the button tag allows richer labels such as images and emphasis,
- # so this helper will also accept a block.
+ # the button tag does allow for richer labels such as images and emphasis,
+ # so this helper will also accept a block. By default, it will create
+ # a button tag with type `submit`, if type is not given.
#
# ==== Options
# * <tt>:data</tt> - This option can be used to add custom data attributes.
@@ -472,6 +487,15 @@ module ActionView
# button_tag
# # => <button name="button" type="submit">Button</button>
#
+ # button_tag 'Reset', type: 'reset'
+ # # => <button name="button" type="reset">Reset</button>
+ #
+ # button_tag 'Button', type: 'button'
+ # # => <button name="button" type="button">Button</button>
+ #
+ # button_tag 'Reset', type: 'reset', disabled: true
+ # # => <button name="button" type="reset" disabled="disabled">Reset</button>
+ #
# button_tag(type: 'button') do
# content_tag(:strong, 'Ask me!')
# end
@@ -479,6 +503,9 @@ module ActionView
# # <strong>Ask me!</strong>
# # </button>
#
+ # button_tag "Save", data: { confirm: "Are you sure?" }
+ # # => <button name="button" type="submit" data-confirm="Are you sure?">Save</button>
+ #
# button_tag "Checkout", data: { disable_with: "Please wait..." }
# # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
#
@@ -555,7 +582,7 @@ module ActionView
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
def field_set_tag(legend = nil, options = nil, &block)
output = tag(:fieldset, options, true)
- output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
+ output.safe_concat(content_tag("legend".freeze, legend)) unless legend.blank?
output.concat(capture(&block)) if block_given?
output.safe_concat("</fieldset>")
end
diff --git a/actionview/lib/action_view/helpers/javascript_helper.rb b/actionview/lib/action_view/helpers/javascript_helper.rb
index e237a32cb7..ed7e882c94 100644
--- a/actionview/lib/action_view/helpers/javascript_helper.rb
+++ b/actionview/lib/action_view/helpers/javascript_helper.rb
@@ -47,8 +47,8 @@ module ActionView
# tag.
#
# javascript_tag "alert('All is good')", defer: 'defer'
- #
- # Returns:
+ #
+ # Returns:
# <script defer="defer">
# //<![CDATA[
# alert('All is good')
@@ -70,7 +70,7 @@ module ActionView
content_or_options_with_block
end
- content_tag(:script, javascript_cdata_section(content), html_options)
+ content_tag("script".freeze, javascript_cdata_section(content), html_options)
end
def javascript_cdata_section(content) #:nodoc:
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index ca8d30e4ef..13effa592d 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -279,7 +279,7 @@ module ActionView
# See <tt>number_to_human_size</tt> if you want to print a file
# size.
#
- # You can also define you own unit-quantifier names if you want
+ # You can also define your own unit-quantifier names if you want
# to use other decimal units (eg.: 1500 becomes "1.5
# kilometers", 0.150 becomes "150 milliliters", etc). You may
# define a wide range of unit quantifiers, even fractional ones
diff --git a/actionview/lib/action_view/helpers/rendering_helper.rb b/actionview/lib/action_view/helpers/rendering_helper.rb
index 827932d8e2..c98f2d74a8 100644
--- a/actionview/lib/action_view/helpers/rendering_helper.rb
+++ b/actionview/lib/action_view/helpers/rendering_helper.rb
@@ -18,7 +18,7 @@ module ActionView
# 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>
+ # type of <tt>text/plain</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
diff --git a/actionview/lib/action_view/helpers/sanitize_helper.rb b/actionview/lib/action_view/helpers/sanitize_helper.rb
index a2e9f37453..191a881de0 100644
--- a/actionview/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionview/lib/action_view/helpers/sanitize_helper.rb
@@ -120,7 +120,7 @@ module ActionView
attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
# Vendors the full, link and white list sanitizers.
- # Provided strictly for compabitility and can be removed in Rails 5.
+ # Provided strictly for compatibility and can be removed in Rails 5.
def sanitizer_vendor
Rails::Html::Sanitizer
end
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb
index a87c223a71..2562504896 100644
--- a/actionview/lib/action_view/helpers/tag_helper.rb
+++ b/actionview/lib/action_view/helpers/tag_helper.rb
@@ -22,9 +22,10 @@ module ActionView
TAG_PREFIXES = ['aria', 'data', :aria, :data].to_set
- PRE_CONTENT_STRINGS = {
- :textarea => "\n"
- }
+ PRE_CONTENT_STRINGS = Hash.new { "".freeze }
+ PRE_CONTENT_STRINGS[:textarea] = "\n"
+ PRE_CONTENT_STRINGS["textarea"] = "\n"
+
# Returns an empty HTML tag of type +name+ which by default is XHTML
# compliant. Set +open+ to true to create an open tag compatible
@@ -143,24 +144,30 @@ module ActionView
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
content = ERB::Util.unwrapped_html_escape(content) if escape
- "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
+ "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
end
def tag_options(options, escape = true)
return if options.blank?
- attrs = []
+ output = ""
+ sep = " ".freeze
options.each_pair do |key, value|
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
value.each_pair do |k, v|
- attrs << prefix_tag_option(key, k, v, escape)
+ output << sep
+ output << prefix_tag_option(key, k, v, escape)
end
elsif BOOLEAN_ATTRIBUTES.include?(key)
- attrs << boolean_tag_option(key) if value
+ if value
+ output << sep
+ output << boolean_tag_option(key)
+ end
elsif !value.nil?
- attrs << tag_option(key, value, escape)
+ output << sep
+ output << tag_option(key, value, escape)
end
end
- " #{attrs * ' '}" unless attrs.empty?
+ output unless output.empty?
end
def prefix_tag_option(prefix, key, value, escape)
@@ -177,7 +184,7 @@ module ActionView
def tag_option(key, value, escape)
if value.is_a?(Array)
- value = escape ? safe_join(value, " ") : value.join(" ")
+ value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
else
value = escape ? ERB::Util.unwrapped_html_escape(value) : value
end
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index acc6443a96..d57f26ba4f 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -120,7 +120,12 @@ module ActionView
def select_content_tag(option_tags, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
- options[:include_blank] ||= true unless options[:prompt] || select_not_required?(html_options)
+
+ if placeholder_required?(html_options)
+ raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
+ options[:include_blank] ||= true unless options[:prompt]
+ end
+
value = options.fetch(:selected) { value(object) }
select = content_tag("select", add_options(option_tags, options, value), html_options)
@@ -131,8 +136,9 @@ module ActionView
end
end
- def select_not_required?(html_options)
- !html_options["required"] || html_options["multiple"] || html_options["size"].to_i > 1
+ def placeholder_required?(html_options)
+ # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
+ html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
end
def add_options(option_tags, options, value = nil)
diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb
index c216d4401f..432693bc23 100644
--- a/actionview/lib/action_view/helpers/text_helper.rb
+++ b/actionview/lib/action_view/helpers/text_helper.rb
@@ -206,6 +206,11 @@ module ActionView
# +plural+ is supplied, it will use that when count is > 1, otherwise
# it will use the Inflector to determine the plural form.
#
+ # If passed an optional +locale:+ parameter, the word will be pluralized
+ # using rules defined for that language (you must define your own
+ # inflection rules for languages other than English). See
+ # ActiveSupport::Inflector.pluralize
+ #
# pluralize(1, 'person')
# # => 1 person
#
@@ -217,11 +222,14 @@ module ActionView
#
# pluralize(0, 'person')
# # => 0 people
- def pluralize(count, singular, plural = nil)
+ #
+ # pluralize(2, 'Person', locale: :de)
+ # # => 2 Personen
+ def pluralize(count, singular, plural = nil, locale: nil)
word = if (count == 1 || count =~ /^1(\.0+)?$/)
singular
else
- plural || singular.pluralize
+ plural || singular.pluralize(locale)
end
"#{count || 0} #{word}"
@@ -242,12 +250,15 @@ module ActionView
#
# word_wrap('Once upon a time', line_width: 1)
# # => Once\nupon\na\ntime
- def word_wrap(text, options = {})
- line_width = options.fetch(:line_width, 80)
-
+ #
+ # You can also specify a custom +break_sequence+ ("\n" by default)
+ #
+ # word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
+ # # => Once\r\nupon\r\na\r\ntime
+ def word_wrap(text, line_width: 80, break_sequence: "\n")
text.split("\n").collect! do |line|
- line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
- end * "\n"
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").strip : line
+ end * break_sequence
end
# Returns +text+ transformed into HTML using simple formatting rules.
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index afb1265ad9..d676a0a931 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -184,9 +184,9 @@ module ActionView
html_options = convert_options_to_data_attributes(options, html_options)
url = url_for(options)
- html_options['href'] ||= url
+ html_options["href".freeze] ||= url
- content_tag(:a, name || url, html_options, &block)
+ content_tag("a".freeze, name || url, html_options, &block)
end
# Generates a form containing a single button that submits to the URL created
@@ -468,9 +468,10 @@ module ActionView
}.compact
extras = extras.empty? ? '' : '?' + extras.join('&')
- html_options["href"] = "mailto:#{email_address}#{extras}"
+ encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
+ html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
- content_tag(:a, name || email_address, html_options, &block)
+ content_tag("a".freeze, name || email_address, html_options, &block)
end
# True if the current request URI was generated by the given +options+.
diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb
index 1fc609f2cd..9d636c8c9e 100644
--- a/actionview/lib/action_view/layouts.rb
+++ b/actionview/lib/action_view/layouts.rb
@@ -315,25 +315,16 @@ module ActionView
name_clause
end
- if self._layout_conditions.empty?
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def _layout
+ if _conditional_layout?
#{layout_definition}
+ else
+ #{name_clause}
end
- private :_layout
- RUBY
- else
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout
- if _conditional_layout?
- #{layout_definition}
- else
- #{name_clause}
- end
- end
- private :_layout
- RUBY
- end
+ end
+ private :_layout
+ RUBY
end
private
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index 4452dcfed5..f4a10aa393 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -6,10 +6,11 @@ require 'action_view/template/resolver'
module ActionView
# = Action View Lookup Context
#
- # LookupContext is the object responsible to hold all information required to lookup
- # templates, i.e. view paths and details. The LookupContext is also responsible to
- # generate a key, given to view paths, used in the resolver cache lookup. Since
- # this key is generated just once during the request, it speeds up all cache accesses.
+ # <tt>LookupContext</tt> is the object responsible for holding all information
+ # required for looking up templates, i.e. view paths and details.
+ # <tt>LookupContext</tt> is also responsible for generating a key, given to
+ # view paths, used in the resolver cache lookup. Since this key is generated
+ # only once during the request, it speeds up all cache accesses.
class LookupContext #:nodoc:
attr_accessor :prefixes, :rendered_format
@@ -172,13 +173,13 @@ module ActionView
# name instead of the prefix.
def normalize_name(name, prefixes) #:nodoc:
prefixes = prefixes.presence
- parts = name.to_s.split('/')
+ parts = name.to_s.split('/'.freeze)
parts.shift if parts.first.empty?
name = parts.pop
return name, prefixes || [""] if parts.empty?
- parts = parts.join('/')
+ parts = parts.join('/'.freeze)
prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
return name, prefixes
@@ -203,7 +204,7 @@ module ActionView
# add :html as fallback to :js.
def formats=(values)
if values
- values.concat(default_formats) if values.delete "*/*"
+ values.concat(default_formats) if values.delete "*/*".freeze
if values == [:js]
values << :html
@html_fallback_for_js = true
diff --git a/actionview/lib/action_view/path_set.rb b/actionview/lib/action_view/path_set.rb
index 91ee2ea8f5..7a88f6bc50 100644
--- a/actionview/lib/action_view/path_set.rb
+++ b/actionview/lib/action_view/path_set.rb
@@ -61,6 +61,15 @@ module ActionView #:nodoc:
find_all(path, prefixes, *args).any?
end
+ def find_all_with_query(query) # :nodoc:
+ paths.each do |resolver|
+ templates = resolver.find_all_with_query(query)
+ return templates unless templates.empty?
+ end
+
+ []
+ end
+
private
def typecast(paths)
diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb
index 9a26cba574..e829d86c99 100644
--- a/actionview/lib/action_view/railtie.rb
+++ b/actionview/lib/action_view/railtie.rb
@@ -42,14 +42,24 @@ module ActionView
end
end
+ initializer "action_view.per_request_digest_cache" do |app|
+ ActiveSupport.on_load(:action_view) do
+ if app.config.consider_all_requests_local
+ app.middleware.use ActionView::Digestor::PerRequestDigestCacheExpiry
+ end
+ end
+ end
+
initializer "action_view.setup_action_pack" do |app|
ActiveSupport.on_load(:action_controller) do
ActionView::RoutingUrlFor.include(ActionDispatch::Routing::UrlFor)
end
end
- rake_tasks do
- load "action_view/tasks/dependencies.rake"
+ rake_tasks do |app|
+ unless app.config.api_only
+ load "action_view/tasks/dependencies.rake"
+ end
end
end
end
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index b751bca31e..1f9e960488 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -348,8 +348,6 @@ module ActionView
content
end
- private
-
# Sets up instance variables needed for rendering a partial. This method
# finds the options and details and extracts them. The method also contains
# logic that handles the type of object passed in as the partial.
@@ -522,7 +520,7 @@ module ActionView
def retrieve_variable(path, as)
variable = as || begin
- base = path[-1] == "/" ? "" : File.basename(path)
+ base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
raise_invalid_identifier(path) unless base =~ /\A_?(.*)(?:\.\w+)*\z/
$1.to_sym
end
diff --git a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
index c8268e226e..1147963882 100644
--- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
@@ -51,7 +51,7 @@ module ActionView
end
def expanded_cache_key(key)
- key = @view.fragment_cache_key(@view.cache_fragment_name(key))
+ key = @view.fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
end
diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb
index 1e8e7415d1..86a80a5421 100644
--- a/actionview/lib/action_view/rendering.rb
+++ b/actionview/lib/action_view/rendering.rb
@@ -59,7 +59,7 @@ module ActionView
@_view_context_class ||= self.class.view_context_class
end
- # An instance of a view class. The default view class is ActionView::Base
+ # An instance of a view class. The default view class is ActionView::Base.
#
# The view class must have the following methods:
# View.new[lookup_context, assigns, controller]
@@ -103,7 +103,7 @@ module ActionView
view_renderer.render(context, options)
end
- # Assign the rendered format to lookup context.
+ # Assign the rendered format to look up context.
def _process_format(format, options = {}) #:nodoc:
super
lookup_context.formats = [format.to_sym]
diff --git a/actionview/lib/action_view/routing_url_for.rb b/actionview/lib/action_view/routing_url_for.rb
index 0371db07dc..20d6b9a64c 100644
--- a/actionview/lib/action_view/routing_url_for.rb
+++ b/actionview/lib/action_view/routing_url_for.rb
@@ -92,6 +92,16 @@ module ActionView
end
super(options)
+ when ActionController::Parameters
+ unless options.key?(:only_path)
+ if options[:host].nil?
+ options[:only_path] = _generate_paths_by_default
+ else
+ options[:only_path] = false
+ end
+ end
+
+ super(options)
when :back
_back_url
when Array
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index 377ceb534a..e232808dcb 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -130,7 +130,7 @@ module ActionView
@source = source
@identifier = identifier
@handler = handler
- @cache_name = extract_resource_cache_call_name
+ @cache_name = extract_resource_cache_name
@compiled = false
@original_encoding = nil
@locals = details[:locals] || []
@@ -154,7 +154,7 @@ module ActionView
# we use a bang in this instrumentation because you don't want to
# consume this in production. This is only slow if it's being listened to.
def render(view, locals, buffer=nil, &block)
- instrument("!render_template") do
+ instrument("!render_template".freeze) do
compile!(view)
view.send(method_name, locals, buffer, &block)
end
@@ -190,7 +190,7 @@ module ActionView
end
def inspect
- @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
+ @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", ''.freeze) : identifier
end
# This method is responsible for properly setting the encoding of the
@@ -337,27 +337,41 @@ module ActionView
def method_name #:nodoc:
@method_name ||= begin
m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
- m.tr!('-', '_')
+ m.tr!('-'.freeze, '_'.freeze)
m
end
end
def identifier_method_name #:nodoc:
- inspect.tr('^a-z_', '_')
+ inspect.tr('^a-z_'.freeze, '_'.freeze)
end
def instrument(action, &block)
payload = { virtual_path: @virtual_path, identifier: @identifier }
- ActiveSupport::Notifications.instrument("#{action}.action_view", payload, &block)
+ case action
+ when "!render_template".freeze
+ ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, payload, &block)
+ else
+ ActiveSupport::Notifications.instrument("#{action}.action_view".freeze, payload, &block)
+ end
end
- def extract_resource_cache_call_name
- $1 if @handler.respond_to?(:resource_cache_call_pattern) &&
- @source =~ @handler.resource_cache_call_pattern
+ EXPLICIT_COLLECTION = /# Template Collection: (?<resource_name>\w+)/
+
+ def extract_resource_cache_name
+ if match = @source.match(EXPLICIT_COLLECTION) || resource_cache_call_match
+ match[:resource_name]
+ end
+ end
+
+ def resource_cache_call_match
+ if @handler.respond_to?(:resource_cache_call_pattern)
+ @source.match(@handler.resource_cache_call_pattern)
+ end
end
def inferred_cache_name
- @inferred_cache_name ||= @virtual_path.split('/').last.sub('_', '')
+ @inferred_cache_name ||= @virtual_path.split('/'.freeze).last.sub('_'.freeze, ''.freeze)
end
end
end
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index 88a8570706..1f8459c24b 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -125,7 +125,7 @@ module ActionView
# Returns Regexp to extract a cached resource's name from a cache call at the
# first line of a template.
- # The extracted cache name is expected in $1.
+ # The extracted cache name is captured as :resource_name.
#
# <% cache notification do %> # => notification
#
@@ -138,7 +138,14 @@ module ActionView
#
# <% cache notification.event do %> # => nil
def resource_cache_call_pattern
- /\A(?:<%#.*%>\n?)?<% cache\(?\s*(\w+\.?)/
+ /\A
+ (?:<%\#.*%>)* # optional initial comment
+ \s* # followed by optional spaces or newlines
+ <%\s*cache[\(\s] # followed by an ERB call to cache
+ \s* # followed by optional spaces or newlines
+ (?<resource_name>\w+) # capture the cache call argument as :resource_name
+ [\s\)] # followed by a space or close paren
+ /xm
end
private
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index 955118a554..28967f40a6 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -52,6 +52,7 @@ module ActionView
def initialize
@data = SmallCache.new(&KEY_BLOCK)
+ @query_cache = SmallCache.new
end
# Cache the templates returned by the block
@@ -70,8 +71,17 @@ module ActionView
end
end
+ def cache_query(query) # :nodoc:
+ if Resolver.caching?
+ @query_cache[query] ||= canonical_no_templates(yield)
+ else
+ yield
+ end
+ end
+
def clear
@data.clear
+ @query_cache.clear
end
private
@@ -116,6 +126,10 @@ module ActionView
end
end
+ def find_all_with_query(query) # :nodoc:
+ @cache.cache_query(query) { find_template_paths(File.join(@path, query)) }
+ end
+
private
delegate :caching?, to: :class
@@ -181,9 +195,9 @@ module ActionView
def query(path, details, formats)
query = build_query(path, details)
- template_paths = find_template_paths query
+ template_paths = find_template_paths(query)
- template_paths.map { |template|
+ template_paths.map do |template|
handler, format, variant = extract_handler_and_format_and_variant(template, formats)
contents = File.binread(template)
@@ -193,36 +207,36 @@ module ActionView
:variant => variant,
:updated_at => mtime(template)
)
- }
+ end
end
def find_template_paths(query)
- Dir[query].reject { |filename|
+ Dir[query].reject do |filename|
File.directory?(filename) ||
# deals with case-insensitive file systems.
!File.fnmatch(query, filename, File::FNM_EXTGLOB)
- }
+ end
end
# Helper for building query glob string based on resolver's pattern.
def build_query(path, details)
query = @pattern.dup
- prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
- query.gsub!(/\:prefix(\/)?/, prefix)
+ prefix = path.prefix.empty? ? '' : "#{escape_entry(path.prefix)}\\1"
+ query.gsub!(/:prefix(\/)?/, prefix)
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
- query.gsub!(/\:action/, partial)
+ query.gsub!(/:action/, partial)
details.each do |ext, variants|
- query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
+ query.gsub!(/:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
end
File.expand_path(query, @path)
end
def escape_entry(entry)
- entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
+ entry.gsub(/[*?{}\[\]]/, '\\\\\\&'.freeze)
end
# Returns the file mtime from the filesystem.
@@ -234,7 +248,7 @@ module ActionView
# from the path, or the handler, we should return the array of formats given
# to the resolver.
def extract_handler_and_format_and_variant(path, default_formats)
- pieces = File.basename(path).split(".")
+ pieces = File.basename(path).split('.'.freeze)
pieces.shift
extension = pieces.pop
diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb
index 06810ad14d..f6b5696a13 100644
--- a/actionview/lib/action_view/test_case.rb
+++ b/actionview/lib/action_view/test_case.rb
@@ -24,8 +24,8 @@ module ActionView
def initialize
super
self.class.controller_path = ""
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+ @request = ActionController::TestRequest.create
+ @response = ActionDispatch::TestResponse.new
@request.env.delete('PATH_INFO')
@params = {}
@@ -263,9 +263,15 @@ module ActionView
end
def method_missing(selector, *args)
- if @controller.respond_to?(:_routes) &&
- ( @controller._routes.named_routes.route_defined?(selector) ||
- @controller._routes.mounted_helpers.method_defined?(selector) )
+ begin
+ routes = @controller.respond_to?(:_routes) && @controller._routes
+ rescue
+ # Dont call routes, if there is an error on _routes call
+ end
+
+ if routes &&
+ ( routes.named_routes.route_defined?(selector) ||
+ routes.mounted_helpers.method_defined?(selector) )
@controller.__send__(selector, *args)
else
super
diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb
index 492f67f45d..37722013ce 100644
--- a/actionview/lib/action_view/view_paths.rb
+++ b/actionview/lib/action_view/view_paths.rb
@@ -36,9 +36,9 @@ module ActionView
self.class._prefixes
end
- # LookupContext is the object responsible to hold all information required to lookup
- # templates, i.e. view paths and details. Check ActionView::LookupContext for more
- # information.
+ # <tt>LookupContext</tt> is the object responsible for holding all
+ # information required for looking up templates, i.e. view paths and
+ # details. Check <tt>ActionView::LookupContext</tt> for more information.
def lookup_context
@_lookup_context ||=
ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb
index 4635c645d0..35f520ed1c 100644
--- a/actionview/test/abstract_unit.rb
+++ b/actionview/test/abstract_unit.rb
@@ -147,13 +147,13 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
def self.build_app(routes = nil)
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
- middleware.use "ActionDispatch::ShowExceptions", ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
- middleware.use "ActionDispatch::DebugExceptions"
- middleware.use "ActionDispatch::Callbacks"
- middleware.use "ActionDispatch::ParamsParser"
- middleware.use "ActionDispatch::Cookies"
- middleware.use "ActionDispatch::Flash"
- middleware.use "Rack::Head"
+ middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
+ middleware.use ActionDispatch::DebugExceptions
+ middleware.use ActionDispatch::Callbacks
+ middleware.use ActionDispatch::ParamsParser
+ middleware.use ActionDispatch::Cookies
+ middleware.use ActionDispatch::Flash
+ middleware.use Rack::Head
yield(middleware) if block_given?
end
end
diff --git a/actionview/test/actionpack/abstract/layouts_test.rb b/actionview/test/actionpack/abstract/layouts_test.rb
index a6786d9b6b..80bc665b0a 100644
--- a/actionview/test/actionpack/abstract/layouts_test.rb
+++ b/actionview/test/actionpack/abstract/layouts_test.rb
@@ -52,7 +52,7 @@ module AbstractControllerTests
end
def overwrite_skip
- render :text => "Hello text!"
+ render plain: "Hello text!"
end
end
@@ -371,7 +371,7 @@ module AbstractControllerTests
test "layout for anonymous controller" do
klass = Class.new(WithString) do
def index
- render :text => 'index', :layout => true
+ render plain: 'index', layout: true
end
end
diff --git a/actionview/test/actionpack/abstract/render_test.rb b/actionview/test/actionpack/abstract/render_test.rb
index d09f91c1e2..e5721d6416 100644
--- a/actionview/test/actionpack/abstract/render_test.rb
+++ b/actionview/test/actionpack/abstract/render_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/deprecation'
module AbstractController
module Testing
@@ -33,7 +34,7 @@ module AbstractController
end
def text
- render :text => "With Text"
+ render plain: "With Text"
end
def default
diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb
index 64ab125637..64bc4c41d6 100644
--- a/actionview/test/actionpack/controller/layout_test.rb
+++ b/actionview/test/actionpack/controller/layout_test.rb
@@ -122,14 +122,6 @@ class PrependsViewPathController < LayoutTest
end
end
-class ParentController < LayoutTest
- layout 'item'
-end
-
-class ChildController < ParentController
- layout 'layout_test', only: :hello
-end
-
class OnlyLayoutController < LayoutTest
layout 'item', :only => "hello"
end
@@ -157,75 +149,75 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_set_when_using_default_layout
@controller = DefaultLayoutController.new
get :hello
- assert_template :layout => "layouts/layout_test"
+ assert_includes @response.body, 'layout_test.erb'
end
def test_layout_set_when_using_streaming_layout
@controller = StreamingLayoutController.new
get :hello
- assert_template :hello
+ assert_includes @response.body, 'layout_test.erb'
end
def test_layout_set_when_set_in_controller
@controller = HasOwnLayoutController.new
get :hello
- assert_template :layout => "layouts/item"
+ assert_includes @response.body, 'item.erb'
end
def test_layout_symbol_set_in_controller_returning_nil_falls_back_to_default
@controller = HasNilLayoutSymbol.new
get :hello
- assert_template layout: "layouts/layout_test"
+ assert_includes @response.body, 'layout_test.erb'
end
def test_layout_proc_set_in_controller_returning_nil_falls_back_to_default
@controller = HasNilLayoutProc.new
get :hello
- assert_template layout: "layouts/layout_test"
+ assert_includes @response.body, 'layout_test.erb'
end
def test_layout_only_exception_when_included
@controller = OnlyLayoutController.new
get :hello
- assert_template :layout => "layouts/item"
+ assert_includes @response.body, 'item.erb'
end
def test_layout_only_exception_when_excepted
@controller = OnlyLayoutController.new
get :goodbye
- assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
+ assert_not_includes @response.body, 'item.erb'
end
def test_layout_except_exception_when_included
@controller = ExceptLayoutController.new
get :hello
- assert_template :layout => "layouts/item"
+ assert_includes @response.body, 'item.erb'
end
def test_layout_except_exception_when_excepted
@controller = ExceptLayoutController.new
get :goodbye
- assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
+ assert_not_includes @response.body, 'item.erb'
end
def test_layout_set_when_using_render
with_template_handler :mab, lambda { |template| template.source.inspect } do
@controller = SetsLayoutInRenderController.new
get :hello
- assert_template :layout => "layouts/third_party_template_library"
+ assert_includes @response.body, 'layouts/third_party_template_library.mab'
end
end
def test_layout_is_not_set_when_none_rendered
@controller = RendersNoLayoutController.new
get :hello
- assert_template :layout => nil
+ assert_equal 'hello.erb', @response.body
end
def test_layout_is_picked_from_the_controller_instances_view_path
@controller = PrependsViewPathController.new
get :hello
- assert_template :layout => /layouts\/alt/
+ assert_includes @response.body, 'alt.erb'
end
def test_absolute_pathed_layout
@@ -233,12 +225,6 @@ class LayoutSetInResponseTest < ActionController::TestCase
get :hello
assert_equal "layout_test.erb hello.erb", @response.body.strip
end
-
- def test_respect_to_parent_layout
- @controller = ChildController.new
- get :goodbye
- assert_template :layout => "layouts/item"
- end
end
class SetsNonExistentLayoutFile < LayoutTest
@@ -276,7 +262,7 @@ unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
@controller = LayoutSymlinkedTest.new
get :hello
assert_response 200
- assert_template :layout => "layouts/symlinked/symlinked_layout"
+ assert_includes @response.body, 'This is my layout'
end
end
end
diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb
index d69c070ede..8d048ddbcb 100644
--- a/actionview/test/actionpack/controller/render_test.rb
+++ b/actionview/test/actionpack/controller/render_test.rb
@@ -1,6 +1,5 @@
require 'abstract_unit'
require 'active_model'
-require 'fileutils'
class ApplicationController < ActionController::Base
self.view_paths = File.join(FIXTURE_LOAD_PATH, "actionpack")
@@ -122,7 +121,7 @@ class TestController < ApplicationController
# :ported:
def render_hello_world_from_variable
@person = "david"
- render :text => "hello #{@person}"
+ render plain: "hello #{@person}"
end
# :ported:
@@ -144,13 +143,13 @@ class TestController < ApplicationController
# :ported:
def render_text_hello_world
- render :text => "hello world"
+ render plain: "hello world"
end
# :ported:
def render_text_hello_world_with_layout
@variable_for_layout = ", I am here!"
- render :text => "hello world", :layout => true
+ render plain: "hello world", :layout => true
end
def hello_world_with_layout_false
@@ -213,26 +212,26 @@ class TestController < ApplicationController
# :ported:
def render_custom_code
- render :text => "hello world", :status => 404
+ render plain: "hello world", :status => 404
end
# :ported:
def render_text_with_nil
- render :text => nil
+ render plain: nil
end
# :ported:
def render_text_with_false
- render :text => false
+ render plain: false
end
def render_text_with_resource
- render :text => Customer.new("David")
+ render plain: Customer.new("David")
end
# :ported:
def render_nothing_with_appendix
- render :text => "appended"
+ render plain: "appended"
end
# This test is testing 3 things:
@@ -263,7 +262,7 @@ class TestController < ApplicationController
# :ported:
def blank_response
- render :text => ' '
+ render plain: ' '
end
# :ported:
@@ -295,7 +294,7 @@ class TestController < ApplicationController
def hello_in_a_string
@customers = [ Customer.new("david"), Customer.new("mary") ]
- render :text => "How's there? " + render_to_string(:template => "test/list")
+ render plain: "How's there? " + render_to_string(:template => "test/list")
end
def accessing_params_in_template
@@ -363,7 +362,7 @@ class TestController < ApplicationController
def render_to_string_with_assigns
@before = "i'm before the render"
- render_to_string :text => "foo"
+ render_to_string plain: "foo"
@after = "i'm after the render"
render :template => "test/hello_world"
end
@@ -410,8 +409,8 @@ class TestController < ApplicationController
# :ported:
def double_render
- render :text => "hello"
- render :text => "world"
+ render plain: "hello"
+ render plain: "world"
end
def double_redirect
@@ -420,13 +419,13 @@ class TestController < ApplicationController
end
def render_and_redirect
- render :text => "hello"
+ render plain: "hello"
redirect_to :action => "double_render"
end
def render_to_string_and_render
- @stuff = render_to_string :text => "here is some cached stuff"
- render :text => "Hi web users! #{@stuff}"
+ @stuff = render_to_string plain: "here is some cached stuff"
+ render plain: "Hi web users! #{@stuff}"
end
def render_to_string_with_inline_and_render
@@ -455,7 +454,7 @@ class TestController < ApplicationController
# :addressed:
def render_text_with_assigns
@hello = "world"
- render :text => "foo"
+ render plain: "foo"
end
def render_with_assigns_option
@@ -468,7 +467,7 @@ class TestController < ApplicationController
def render_content_type_from_body
response.content_type = Mime::RSS
- render :text => "hello world!"
+ render body: "hello world!"
end
def render_using_layout_around_block
@@ -679,33 +678,24 @@ class RenderTest < ActionController::TestCase
ActionView::Base.logger = nil
end
- def case_sensitive_file_system?
- fname = '.case_sensitive_file_system_test'
- FileUtils.touch(fname)
- !File.exist?(fname.upcase)
- ensure
- FileUtils.rm_f(fname)
- end
-
# :ported:
def test_simple_show
get :hello_world
assert_response 200
assert_response :success
- assert_template "test/hello_world"
assert_equal "<html>Hello world!</html>", @response.body
end
# :ported:
def test_renders_default_template_for_missing_action
get :'hyphen-ated'
- assert_template 'test/hyphen-ated'
+ assert_equal "hyphen-ated.erb", @response.body
end
# :ported:
def test_render
get :render_hello_world
- assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
end
def test_line_offset
@@ -721,20 +711,18 @@ class RenderTest < ActionController::TestCase
# :ported: compatibility
def test_render_with_forward_slash
get :render_hello_world_with_forward_slash
- assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
end
# :ported:
def test_render_in_top_directory
get :render_template_in_top_directory
- assert_template "shared"
assert_equal "Elastica", @response.body
end
# :ported:
def test_render_in_top_directory_with_slash
get :render_template_in_top_directory_with_slash
- assert_template "shared"
assert_equal "Elastica", @response.body
end
@@ -752,19 +740,12 @@ class RenderTest < ActionController::TestCase
# :ported:
def test_render_action
get :render_action_hello_world
- assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
end
def test_render_action_upcased
- action = :render_action_upcased_hello_world
-
- if case_sensitive_file_system?
- assert_raise ActionView::MissingTemplate do
- get action
- end
- else
- get action
- assert_template 'test/Hello_world'
+ assert_raise ActionView::MissingTemplate do
+ get :render_action_upcased_hello_world
end
end
@@ -772,13 +753,12 @@ class RenderTest < ActionController::TestCase
def test_render_action_hello_world_as_string
get :render_action_hello_world_as_string
assert_equal "Hello world!", @response.body
- assert_template "test/hello_world"
end
# :ported:
def test_render_action_with_symbol
get :render_action_hello_world_with_symbol
- assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
end
# :ported:
@@ -790,7 +770,7 @@ class RenderTest < ActionController::TestCase
# :ported:
def test_do_with_render_text_and_layout
get :render_text_hello_world_with_layout
- assert_equal "<html>hello world, I am here!</html>", @response.body
+ assert_equal "{{hello world, I am here!}}\n", @response.body
end
# :ported:
@@ -966,7 +946,7 @@ class RenderTest < ActionController::TestCase
def test_render_to_string_inline
get :render_to_string_with_inline_and_render
- assert_template "test/hello_world"
+ assert_equal 'Hello world!', @response.body
end
# :ported:
@@ -1051,8 +1031,8 @@ class RenderTest < ActionController::TestCase
def test_render_to_string_doesnt_break_assigns
get :render_to_string_with_assigns
- assert_equal "i'm before the render", assigns(:before)
- assert_equal "i'm after the render", assigns(:after)
+ assert_equal "i'm before the render", @controller.instance_variable_get(:@before)
+ assert_equal "i'm after the render", @controller.instance_variable_get(:@after)
end
def test_bad_render_to_string_still_throws_exception
@@ -1061,8 +1041,8 @@ class RenderTest < ActionController::TestCase
def test_render_to_string_that_throws_caught_exception_doesnt_break_assigns
assert_nothing_raised { get :render_to_string_with_caught_exception }
- assert_equal "i'm before the render", assigns(:before)
- assert_equal "i'm after the render", assigns(:after)
+ assert_equal "i'm before the render", @controller.instance_variable_get(:@before)
+ assert_equal "i'm after the render", @controller.instance_variable_get(:@after)
end
def test_accessing_params_in_template_with_layout
@@ -1123,7 +1103,7 @@ class RenderTest < ActionController::TestCase
# :addressed:
def test_render_text_with_assigns
get :render_text_with_assigns
- assert_equal "world", assigns["hello"]
+ assert_equal "world", @controller.instance_variable_get(:@hello)
end
def test_render_text_with_assigns_option
@@ -1189,22 +1169,22 @@ class RenderTest < ActionController::TestCase
def test_render_to_string_partial
get :render_to_string_with_partial
- assert_equal "only partial", assigns(:partial_only)
- assert_equal "Hello: david", assigns(:partial_with_locals)
+ assert_equal "only partial", @controller.instance_variable_get(:@partial_only)
+ assert_equal "Hello: david", @controller.instance_variable_get(:@partial_with_locals)
assert_equal "text/html", @response.content_type
end
def test_render_to_string_with_template_and_html_partial
get :render_to_string_with_template_and_html_partial
- assert_equal "**only partial**\n", assigns(:text)
- assert_equal "<strong>only partial</strong>\n", assigns(:html)
+ assert_equal "**only partial**\n", @controller.instance_variable_get(:@text)
+ assert_equal "<strong>only partial</strong>\n", @controller.instance_variable_get(:@html)
assert_equal "<strong>only html partial</strong>\n", @response.body
assert_equal "text/html", @response.content_type
end
def test_render_to_string_and_render_with_different_formats
get :render_to_string_and_render_with_different_formats
- assert_equal "<strong>only partial</strong>\n", assigns(:html)
+ assert_equal "<strong>only partial</strong>\n", @controller.instance_variable_get(:@html)
assert_equal "**only partial**\n", @response.body
assert_equal "text/plain", @response.content_type
end
@@ -1228,21 +1208,18 @@ class RenderTest < ActionController::TestCase
def test_partial_with_form_builder
get :partial_with_form_builder
- assert_match(/<label/, @response.body)
- assert_template('test/_form')
+ assert_equal "<label for=\"post_title\">Title</label>\n", @response.body
end
def test_partial_with_form_builder_subclass
get :partial_with_form_builder_subclass
- assert_match(/<label/, @response.body)
- assert_template('test/_labelling_form')
+ assert_equal "<label for=\"post_title\">Title</label>\n", @response.body
end
def test_nested_partial_with_form_builder
@controller = Fun::GamesController.new
get :nested_partial_with_form_builder
- assert_match(/<label/, @response.body)
- assert_template('fun/games/_form')
+ assert_equal "<label for=\"post_title\">Title</label>\n", @response.body
end
def test_namespaced_object_partial
@@ -1286,48 +1263,29 @@ class RenderTest < ActionController::TestCase
assert_equal "Bonjour: davidBonjour: mary", @response.body
end
- def test_locals_option_to_assert_template_is_not_supported
- get :partial_collection_with_locals
-
- warning_buffer = StringIO.new
- $stderr = warning_buffer
-
- assert_template partial: 'customer_greeting', locals: { greeting: 'Bonjour' }
- assert_equal "the :locals option to #assert_template is only supported in a ActionView::TestCase\n", warning_buffer.string
- ensure
- $stderr = STDERR
- end
-
def test_partial_collection_with_spacer
get :partial_collection_with_spacer
assert_equal "Hello: davidonly partialHello: mary", @response.body
- assert_template :partial => '_customer'
end
def test_partial_collection_with_spacer_which_uses_render
get :partial_collection_with_spacer_which_uses_render
assert_equal "Hello: davidpartial html\npartial with partial\nHello: mary", @response.body
- assert_template :partial => '_customer'
end
def test_partial_collection_shorthand_with_locals
get :partial_collection_shorthand_with_locals
assert_equal "Bonjour: davidBonjour: mary", @response.body
- assert_template :partial => 'customers/_customer', :count => 2
- assert_template :partial => '_completely_fake_and_made_up_template_that_cannot_possibly_be_rendered', :count => 0
end
def test_partial_collection_shorthand_with_different_types_of_records
get :partial_collection_shorthand_with_different_types_of_records
assert_equal "Bonjour bad customer: mark0Bonjour good customer: craig1Bonjour bad customer: john2Bonjour good customer: zach3Bonjour good customer: brandon4Bonjour bad customer: dan5", @response.body
- assert_template :partial => 'good_customers/_good_customer', :count => 3
- assert_template :partial => 'bad_customers/_bad_customer', :count => 3
end
def test_empty_partial_collection
get :empty_partial_collection
assert_equal " ", @response.body
- assert_template :partial => false
end
def test_partial_with_hash_object
diff --git a/actionview/test/actionpack/controller/view_paths_test.rb b/actionview/test/actionpack/controller/view_paths_test.rb
index 7fba9ff8ff..e99659c802 100644
--- a/actionview/test/actionpack/controller/view_paths_test.rb
+++ b/actionview/test/actionpack/controller/view_paths_test.rb
@@ -23,8 +23,8 @@ class ViewLoadPathsTest < ActionController::TestCase
end
def setup
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+ @request = ActionController::TestRequest.create
+ @response = ActionDispatch::TestResponse.new
@controller = TestController.new
@paths = TestController.view_paths
end
diff --git a/actionview/test/activerecord/render_partial_with_record_identification_test.rb b/actionview/test/activerecord/render_partial_with_record_identification_test.rb
index 409370104d..9772ebb39e 100644
--- a/actionview/test/activerecord/render_partial_with_record_identification_test.rb
+++ b/actionview/test/activerecord/render_partial_with_record_identification_test.rb
@@ -52,43 +52,37 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
def test_rendering_partial_with_has_many_and_belongs_to_association
get :render_with_has_many_and_belongs_to_association
- assert_template 'projects/_project'
- assert_equal assigns(:developer).projects.map(&:name).join, @response.body
+ assert_equal Developer.find(1).projects.map(&:name).join, @response.body
end
def test_rendering_partial_with_has_many_association
get :render_with_has_many_association
- assert_template 'replies/_reply'
assert_equal 'Birdman is better!', @response.body
end
def test_rendering_partial_with_scope
get :render_with_scope
- assert_template 'replies/_reply'
assert_equal 'Birdman is better!Nuh uh!', @response.body
end
def test_render_with_record
get :render_with_record
- assert_template 'developers/_developer'
assert_equal 'David', @response.body
end
def test_render_with_record_collection
get :render_with_record_collection
- assert_template 'developers/_developer'
assert_equal 'DavidJamisfixture_3fixture_4fixture_5fixture_6fixture_7fixture_8fixture_9fixture_10Jamis', @response.body
end
def test_render_with_record_collection_and_spacer_template
get :render_with_record_collection_and_spacer_template
- assert_equal assigns(:developer).projects.map(&:name).join('only partial'), @response.body
+ assert_equal Developer.find(1).projects.map(&:name).join('only partial'), @response.body
end
def test_rendering_partial_with_has_one_association
mascot = Company.find(1).mascot
get :render_with_has_one_association
- assert_template 'mascots/_mascot'
assert_equal mascot.name, @response.body
end
end
@@ -130,13 +124,11 @@ class RenderPartialWithRecordIdentificationAndNestedControllersTest < ActiveReco
def test_render_with_record_in_nested_controller
get :render_with_record_in_nested_controller
- assert_template %r{\Afun/games/_game\Z}
assert_equal "Fun Pong\n", @response.body
end
def test_render_with_record_collection_in_nested_controller
get :render_with_record_collection_in_nested_controller
- assert_template %r{\Afun/games/_game\Z}
assert_equal "Fun Pong\nFun Tank\n", @response.body
end
end
@@ -149,7 +141,6 @@ class RenderPartialWithRecordIdentificationAndNestedControllersWithoutPrefixTest
ActionView::Base.prefix_partial_path_with_controller_namespace = false
get :render_with_record_in_nested_controller
- assert_template %r{\Agames/_game\Z}
assert_equal "Just Pong\n", @response.body
ensure
ActionView::Base.prefix_partial_path_with_controller_namespace = old_config
@@ -160,7 +151,6 @@ class RenderPartialWithRecordIdentificationAndNestedControllersWithoutPrefixTest
ActionView::Base.prefix_partial_path_with_controller_namespace = false
get :render_with_record_collection_in_nested_controller
- assert_template %r{\Agames/_game\Z}
assert_equal "Just Pong\nJust Tank\n", @response.body
ensure
ActionView::Base.prefix_partial_path_with_controller_namespace = old_config
@@ -172,13 +162,11 @@ class RenderPartialWithRecordIdentificationAndNestedDeeperControllersTest < Acti
def test_render_with_record_in_deeper_nested_controller
get :render_with_record_in_deeper_nested_controller
- assert_template %r{\Afun/serious/games/_game\Z}
assert_equal "Serious Chess\n", @response.body
end
def test_render_with_record_collection_in_deeper_nested_controller
get :render_with_record_collection_in_deeper_nested_controller
- assert_template %r{\Afun/serious/games/_game\Z}
assert_equal "Serious Chess\nSerious Sudoku\nSerious Solitaire\n", @response.body
end
end
@@ -191,7 +179,6 @@ class RenderPartialWithRecordIdentificationAndNestedDeeperControllersWithoutPref
ActionView::Base.prefix_partial_path_with_controller_namespace = false
get :render_with_record_in_deeper_nested_controller
- assert_template %r{\Agames/_game\Z}
assert_equal "Just Chess\n", @response.body
ensure
ActionView::Base.prefix_partial_path_with_controller_namespace = old_config
@@ -202,7 +189,6 @@ class RenderPartialWithRecordIdentificationAndNestedDeeperControllersWithoutPref
ActionView::Base.prefix_partial_path_with_controller_namespace = false
get :render_with_record_collection_in_deeper_nested_controller
- assert_template %r{\Agames/_game\Z}
assert_equal "Just Chess\nJust Sudoku\nJust Solitaire\n", @response.body
ensure
ActionView::Base.prefix_partial_path_with_controller_namespace = old_config
diff --git a/actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb b/actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb
index e69de29bb2..60b81525b5 100644
--- a/actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb
+++ b/actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb
@@ -0,0 +1 @@
+alt.erb
diff --git a/actionview/test/fixtures/actionpack/layouts/standard.text.erb b/actionview/test/fixtures/actionpack/layouts/standard.text.erb
new file mode 100644
index 0000000000..a58afb1aa2
--- /dev/null
+++ b/actionview/test/fixtures/actionpack/layouts/standard.text.erb
@@ -0,0 +1 @@
+{{<%= yield %><%= @variable_for_layout %>}}
diff --git a/actionview/test/fixtures/actionpack/test/hyphen-ated.erb b/actionview/test/fixtures/actionpack/test/hyphen-ated.erb
index cd0875583a..28dbe94ee1 100644
--- a/actionview/test/fixtures/actionpack/test/hyphen-ated.erb
+++ b/actionview/test/fixtures/actionpack/test/hyphen-ated.erb
@@ -1 +1 @@
-Hello world!
+hyphen-ated.erb \ No newline at end of file
diff --git a/actionview/test/fixtures/digestor/events/_completed.html.erb b/actionview/test/fixtures/digestor/events/_completed.html.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionview/test/fixtures/digestor/events/_completed.html.erb
diff --git a/actionview/test/fixtures/digestor/events/index.html.erb b/actionview/test/fixtures/digestor/events/index.html.erb
new file mode 100644
index 0000000000..bc45e41bcb
--- /dev/null
+++ b/actionview/test/fixtures/digestor/events/index.html.erb
@@ -0,0 +1 @@
+<% # Template Dependency: events/* %> \ No newline at end of file
diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb
index 6e6ce20924..01fc66bed6 100644
--- a/actionview/test/template/asset_tag_helper_test.rb
+++ b/actionview/test/template/asset_tag_helper_test.rb
@@ -310,6 +310,11 @@ class AssetTagHelperTest < ActionView::TestCase
AssetPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_asset_path_tag_raises_an_error_for_nil_source
+ e = assert_raise(ArgumentError) { asset_path(nil) }
+ assert_equal("nil is not a valid asset source", e.message)
+ end
+
def test_asset_path_tag_to_not_create_duplicate_slashes
@controller.config.asset_host = "host/"
assert_dom_equal('http://host/foo', asset_path("foo"))
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index c2b8439df3..dde757b5a2 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'fileutils'
+require 'action_view/dependency_tracker'
class FixtureTemplate
attr_reader :source, :handler
@@ -15,12 +16,13 @@ end
class FixtureFinder
FIXTURES_DIR = "#{File.dirname(__FILE__)}/../fixtures/digestor"
- attr_reader :details
+ attr_reader :details, :view_paths
attr_accessor :formats
attr_accessor :variants
def initialize
@details = {}
+ @view_paths = ActionView::PathSet.new(['digestor'])
@formats = []
@variants = []
end
@@ -75,6 +77,34 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
+ def test_explicit_dependency_wildcard
+ assert_digest_difference("events/index") do
+ change_template("events/_completed")
+ end
+ end
+
+ def test_explicit_dependency_wildcard_picks_up_added_file
+ old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false
+
+ assert_digest_difference("events/index") do
+ add_template("events/_uncompleted")
+ end
+ ensure
+ remove_template("events/_uncompleted")
+ ActionView::Resolver.caching = old_caching
+ end
+
+ def test_explicit_dependency_wildcard_picks_up_removed_file
+ old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false
+ add_template("events/_subscribers_changed")
+
+ assert_digest_difference("events/index") do
+ remove_template("events/_subscribers_changed")
+ end
+ ensure
+ ActionView::Resolver.caching = old_caching
+ end
+
def test_second_level_dependency
assert_digest_difference("messages/show") do
change_template("comments/_comments")
@@ -111,6 +141,18 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
+ def test_logging_of_missing_template_for_dependencies
+ assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
+ dependencies("messages/something_missing")
+ end
+ end
+
+ def test_logging_of_missing_template_for_nested_dependencies
+ assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
+ nested_dependencies("messages/something_missing")
+ end
+ end
+
def test_nested_template_directory
assert_digest_difference("messages/show") do
change_template("messages/actions/_move")
@@ -207,7 +249,7 @@ class TemplateDigestorTest < ActionView::TestCase
end
def test_variants
- assert_digest_difference("messages/new", false, variants: [:iphone]) do
+ assert_digest_difference("messages/new", variants: [:iphone]) do
change_template("messages/new", :iphone)
change_template("messages/_header", :iphone)
end
@@ -227,16 +269,6 @@ class TemplateDigestorTest < ActionView::TestCase
assert_not_equal digest_phone, digest_fridge_phone
end
- def test_cache_template_loading
- resolver_before = ActionView::Resolver.caching
- ActionView::Resolver.caching = false
- assert_digest_difference("messages/edit", true) do
- change_template("comments/_comment")
- end
- ensure
- ActionView::Resolver.caching = resolver_before
- end
-
def test_digest_cache_cleanup_with_recursion
first_digest = digest("level/_recursion")
second_digest = digest("level/_recursion")
@@ -279,9 +311,9 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
- def assert_digest_difference(template_name, persistent = false, options = {})
+ def assert_digest_difference(template_name, options = {})
previous_digest = digest(template_name, options)
- ActionView::Digestor.cache.clear unless persistent
+ ActionView::Digestor.cache.clear
yield
@@ -298,6 +330,14 @@ class TemplateDigestorTest < ActionView::TestCase
ActionView::Digestor.digest({ name: template_name, finder: finder }.merge(options))
end
+ def dependencies(template_name)
+ ActionView::Digestor.new({ name: template_name, finder: finder }).dependencies
+ end
+
+ def nested_dependencies(template_name)
+ ActionView::Digestor.new({ name: template_name, finder: finder }).nested_dependencies
+ end
+
def finder
@finder ||= FixtureFinder.new
end
@@ -309,4 +349,9 @@ class TemplateDigestorTest < ActionView::TestCase
f.write "\nTHIS WAS CHANGED!"
end
end
+ alias_method :add_template, :change_template
+
+ def remove_template(template_name)
+ File.delete("digestor/#{template_name}.html.erb")
+ end
end
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index b8cb5bd746..37f2d7cad6 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -1577,7 +1577,7 @@ class FormHelperTest < ActionView::TestCase
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
- "<input name='commit' type='submit' value='Create post' />" +
+ "<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />" +
"<button name='button' type='submit'>Create post</button>" +
"<button name='button' type='submit'><span>Create post</span></button>"
end
@@ -1854,7 +1854,7 @@ class FormHelperTest < ActionView::TestCase
expected = whole_form("/posts/44", "edit_post_44", "edit_post", method: "patch") do
"<input name='post[title]' type='text' id='post_title' value='And his name will be forty and four.' />" +
- "<input name='commit' type='submit' value='Edit post' />"
+ "<input name='commit' data-disable-with='Edit post' type='submit' value='Edit post' />"
end
assert_dom_equal expected, output_buffer
@@ -1875,7 +1875,7 @@ class FormHelperTest < ActionView::TestCase
"<textarea name='other_name[body]' id='other_name_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='other_name[secret]' value='0' type='hidden' />" +
"<input name='other_name[secret]' checked='checked' id='other_name_secret' value='1' type='checkbox' />" +
- "<input name='commit' value='Create post' type='submit' />"
+ "<input name='commit' value='Create post' data-disable-with='Create post' type='submit' />"
end
assert_dom_equal expected, output_buffer
@@ -2083,7 +2083,7 @@ class FormHelperTest < ActionView::TestCase
expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
"<div class='field_with_errors'><input name='post[author_name]' type='text' id='post_author_name' value='' /></div>" +
- "<input name='commit' type='submit' value='Create post' />"
+ "<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />"
end
assert_dom_equal expected, output_buffer
@@ -2101,7 +2101,7 @@ class FormHelperTest < ActionView::TestCase
expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
"<div class='field_with_errors'><input name='post[author_name]' type='text' id='post_author_name' value='' /></div>" +
- "<input name='commit' type='submit' value='Create post' />"
+ "<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />"
end
assert_dom_equal expected, output_buffer
@@ -2226,7 +2226,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts', 'new_post', 'new_post') do
- "<input name='commit' type='submit' value='Create Post' />"
+ "<input name='commit' data-disable-with='Create Post' type='submit' value='Create Post' />"
end
assert_dom_equal expected, output_buffer
@@ -2240,7 +2240,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
- "<input name='commit' type='submit' value='Confirm Post changes' />"
+ "<input name='commit' data-disable-with='Confirm Post changes' type='submit' value='Confirm Post changes' />"
end
assert_dom_equal expected, output_buffer
@@ -2254,7 +2254,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form do
- "<input name='commit' class='extra' type='submit' value='Save changes' />"
+ "<input name='commit' class='extra' data-disable-with='Save changes' type='submit' value='Save changes' />"
end
assert_dom_equal expected, output_buffer
@@ -2268,7 +2268,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do
- "<input name='commit' type='submit' value='Update your Post' />"
+ "<input name='commit' data-disable-with='Update your Post' type='submit' value='Update your Post' />"
end
assert_dom_equal expected, output_buffer
diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb
index d25fa3706f..d7daba8bf3 100644
--- a/actionview/test/template/form_options_helper_test.rb
+++ b/actionview/test/template/form_options_helper_test.rb
@@ -645,6 +645,13 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_select_with_include_blank_false_and_required
+ @post = Post.new
+ @post.category = "<mus>"
+ e = assert_raises(ArgumentError) { select("post", "category", %w( abe <mus> hest), { include_blank: false }, required: 'required') }
+ assert_match(/include_blank cannot be false for a required field./, e.message)
+ end
+
def test_select_with_blank_as_string
@post = Post.new
@post.category = "<mus>"
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index cad1c82309..a9d9562580 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -433,6 +433,44 @@ class FormTagHelperTest < ActionView::TestCase
)
end
+ def test_empty_submit_tag
+ assert_dom_equal(
+ %(<input data-disable-with="Save" name='commit' type="submit" value="Save" />),
+ submit_tag("Save")
+ )
+ end
+
+ def test_empty_submit_tag_with_opt_out
+ ActionView::Base.automatically_disable_submit_tag = false
+ assert_dom_equal(
+ %(<input name='commit' type="submit" value="Save" />),
+ submit_tag("Save")
+ )
+ ensure
+ ActionView::Base.automatically_disable_submit_tag = true
+ end
+
+ def test_data_disable_with_string
+ assert_dom_equal(
+ %(<input data-disable-with="Processing..." data-confirm="Are you sure?" name='commit' type="submit" value="Save" />),
+ submit_tag("Save", { "data-disable-with" => "Processing...", "data-confirm" => "Are you sure?" })
+ )
+ end
+
+ def test_data_disable_with_boolean
+ assert_dom_equal(
+ %(<input data-confirm="Are you sure?" name='commit' type="submit" value="Save" />),
+ submit_tag("Save", { "data-disable-with" => false, "data-confirm" => "Are you sure?" })
+ )
+ end
+
+ def test_data_hash_disable_with_boolean
+ assert_dom_equal(
+ %(<input data-confirm="Are you sure?" name='commit' type="submit" value="Save" />),
+ submit_tag("Save", { :data => { :confirm => "Are you sure?", :disable_with => false } })
+ )
+ end
+
def test_submit_tag_with_no_onclick_options
assert_dom_equal(
%(<input name='commit' data-disable-with="Saving..." type="submit" value="Save" />),
@@ -442,7 +480,7 @@ class FormTagHelperTest < ActionView::TestCase
def test_submit_tag_with_confirmation
assert_dom_equal(
- %(<input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />),
+ %(<input name='commit' type='submit' value='Save' data-confirm="Are you sure?" data-disable-with="Save" />),
submit_tag("Save", :data => { :confirm => "Are you sure?" })
)
end
@@ -510,6 +548,13 @@ class FormTagHelperTest < ActionView::TestCase
)
end
+ def test_button_tag_with_data_disable_with_option
+ assert_dom_equal(
+ %(<button name="button" type="submit" data-disable-with="Please wait...">Checkout</button>),
+ button_tag("Checkout", data: { disable_with: "Please wait..." })
+ )
+ end
+
def test_image_submit_tag_with_confirmation
assert_dom_equal(
%(<input alt="Save" type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
diff --git a/actionview/test/template/record_tag_helper_test.rb b/actionview/test/template/record_tag_helper_test.rb
index 4b7b653916..bfc5d04bed 100644
--- a/actionview/test/template/record_tag_helper_test.rb
+++ b/actionview/test/template/record_tag_helper_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
class RecordTagPost
extend ActiveModel::Naming
- include ActiveModel::Conversion
+
attr_accessor :id, :body
def initialize
@@ -14,7 +14,6 @@ class RecordTagPost
end
class RecordTagHelperTest < ActionView::TestCase
- include RenderERBUtils
tests ActionView::Helpers::RecordTagHelper
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 27bbb9b6c1..9c2c9507b7 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -7,7 +7,10 @@ end
module RenderTestCases
def setup_view(paths)
@assigns = { :secret => 'in the sauce' }
- @view = ActionView::Base.new(paths, @assigns)
+ @view = Class.new(ActionView::Base) do
+ def view_cache_dependencies; end
+ end.new(paths, @assigns)
+
@controller_view = TestController.new.view_context
# Reload and register danish language for testing
@@ -616,7 +619,7 @@ class CachedCollectionViewRenderTest < CachedViewRenderTest
test "with custom key" do
customer = Customer.new("david")
- key = ActionController::Base.new.fragment_cache_key([customer, 'key'])
+ key = cache_key([customer, 'key'], "test/_customer")
ActionView::PartialRenderer.collection_cache.write(key, 'Hello')
@@ -626,7 +629,7 @@ class CachedCollectionViewRenderTest < CachedViewRenderTest
test "automatic caching with inferred cache name" do
customer = CachedCustomer.new("david")
- key = ActionController::Base.new.fragment_cache_key(customer)
+ key = cache_key(customer, "test/_cached_customer")
ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
@@ -636,11 +639,17 @@ class CachedCollectionViewRenderTest < CachedViewRenderTest
test "automatic caching with as name" do
customer = CachedCustomer.new("david")
- key = ActionController::Base.new.fragment_cache_key(customer)
+ key = cache_key(customer, "test/_cached_customer_as")
ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
assert_equal "Cached",
@view.render(partial: "test/cached_customer_as", collection: [customer], as: :buyer)
end
+
+ private
+ def cache_key(names, virtual_path)
+ digest = ActionView::Digestor.digest name: virtual_path, finder: @view.lookup_context, dependencies: []
+ @view.fragment_cache_key([ *Array(names), digest ])
+ end
end
diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb
index aae6a9aa09..d3b51cd629 100644
--- a/actionview/test/template/template_test.rb
+++ b/actionview/test/template/template_test.rb
@@ -190,6 +190,38 @@ class TestERBTemplate < ActiveSupport::TestCase
assert_match(/\xFC/, e.message)
end
+ def test_not_eligible_for_collection_caching_without_cache_call
+ [
+ "<%= 'Hello' %>",
+ "<% cache_customer = 42 %>",
+ "<% cache customer.name do %><% end %>",
+ "<% my_cache customer do %><% end %>"
+ ].each do |body|
+ template = new_template(body, virtual_path: "test/foo/_customer")
+ assert_not template.eligible_for_collection_caching?, "Template #{body.inspect} should not be eligible for collection caching"
+ end
+ end
+
+ def test_eligible_for_collection_caching_with_cache_call_or_explicit
+ [
+ "<% cache customer do %><% end %>",
+ "<% cache(customer) do %><% end %>",
+ "<% cache( customer) do %><% end %>",
+ "<% cache( customer ) do %><% end %>",
+ "<%cache customer do %><% end %>",
+ "<% cache customer do %><% end %>",
+ " <% cache customer do %>\n<% end %>\n",
+ "<%# comment %><% cache customer do %><% end %>",
+ "<%# comment %>\n<% cache customer do %><% end %>",
+ "<%# comment\n line 2\n line 3 %>\n<% cache customer do %><% end %>",
+ "<%# comment 1 %>\n<%# comment 2 %>\n<% cache customer do %><% end %>",
+ "<%# comment 1 %>\n<%# Template Collection: customer %>\n<% my_cache customer do %><% end %>"
+ ].each do |body|
+ template = new_template(body, virtual_path: "test/foo/_customer")
+ assert template.eligible_for_collection_caching?, "Template #{body.inspect} should be eligible for collection caching"
+ end
+ end
+
def with_external_encoding(encoding)
old = Encoding.default_external
Encoding::Converter.new old, encoding if old != encoding
diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb
index c6cc47fb4f..80fbaa8392 100644
--- a/actionview/test/template/test_case_test.rb
+++ b/actionview/test/template/test_case_test.rb
@@ -209,7 +209,7 @@ module ActionView
end
test "is able to use routes" do
- controller.request.assign_parameters(@routes, 'foo', 'index')
+ controller.request.assign_parameters(@routes, 'foo', 'index', {}, '/foo', [])
assert_equal '/foo', url_for
assert_equal '/bar', url_for(:controller => 'bar')
end
@@ -306,64 +306,6 @@ module ActionView
end
end
- class RenderTemplateTest < ActionView::TestCase
- test "supports specifying templates with a Regexp" do
- controller.controller_path = "fun"
- render(:template => "fun/games/hello_world")
- assert_template %r{\Afun/games/hello_world\Z}
- end
-
- test "supports specifying partials" do
- controller.controller_path = "test"
- render(:template => "test/calling_partial_with_layout")
- assert_template :partial => "_partial_for_use_in_layout"
- end
-
- test "supports specifying locals (passing)" do
- controller.controller_path = "test"
- render(:template => "test/calling_partial_with_layout")
- assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "David" }
- end
-
- test "supports specifying locals (failing)" do
- controller.controller_path = "test"
- render(:template => "test/calling_partial_with_layout")
- e = assert_raise ActiveSupport::TestCase::Assertion do
- assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "Somebody Else" }
- end
- assert_match(/Somebody Else.*David/m, e.message)
- end
-
- test 'supports different locals on the same partial' do
- controller.controller_path = "test"
- render(:template => "test/render_two_partials")
- assert_template partial: '_partial', locals: { 'first' => '1' }
- assert_template partial: '_partial', locals: { 'second' => '2' }
- end
-
- test 'raises descriptive error message when template was not rendered' do
- controller.controller_path = "test"
- render(template: "test/hello_world_with_partial")
- e = assert_raise ActiveSupport::TestCase::Assertion do
- assert_template partial: 'i_was_never_rendered', locals: { 'did_not' => 'happen' }
- end
- assert_match "i_was_never_rendered to be rendered but it was not.", e.message
- assert_match 'Expected ["/test/partial"] to include "i_was_never_rendered"', e.message
- end
-
- test 'specifying locals works when the partial is inside a directory with underline prefix' do
- controller.controller_path = "test"
- render(template: 'test/render_partial_inside_directory')
- assert_template partial: 'test/_directory/_partial_with_locales', locals: { 'name' => 'Jane' }
- end
-
- test 'specifying locals works when the partial is inside a directory without underline prefix' do
- controller.controller_path = "test"
- render(template: 'test/render_partial_inside_directory')
- assert_template partial: 'test/_directory/partial_with_locales', locals: { 'name' => 'Jane' }
- end
- end
-
module AHelperWithInitialize
def initialize(*)
super
diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb
index f1b84c4786..fae1965ffa 100644
--- a/actionview/test/template/text_helper_test.rb
+++ b/actionview/test/template/text_helper_test.rb
@@ -366,6 +366,10 @@ class TextHelperTest < ActionView::TestCase
assert_equal options, passed_options
end
+ def test_word_wrap_with_custom_break_sequence
+ assert_equal("1234567890\r\n1234567890\r\n1234567890", word_wrap("1234567890 " * 3, line_width: 2, break_sequence: "\r\n"))
+ end
+
def test_pluralization
assert_equal("1 count", pluralize(1, "count"))
assert_equal("2 counts", pluralize(2, "count"))
@@ -383,6 +387,18 @@ class TextHelperTest < ActionView::TestCase
assert_equal("12 berries", pluralize(12, "berry"))
end
+ def test_pluralization_with_locale
+ ActiveSupport::Inflector.inflections(:de) do |inflect|
+ inflect.plural(/(person)$/i, '\1en')
+ inflect.singular(/(person)en$/i, '\1')
+ end
+
+ assert_equal("2 People", pluralize(2, "Person", locale: :en))
+ assert_equal("2 Personen", pluralize(2, "Person", locale: :de))
+
+ ActiveSupport::Inflector.inflections(:de).clear
+ end
+
def test_cycle_class
value = Cycle.new("one", 2, "3")
assert_equal("one", value.to_s)
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 0e35c67516..416d30938a 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -500,6 +500,13 @@ class UrlHelperTest < ActiveSupport::TestCase
mail_to("david@loudthinking.com", "David Heinemeier Hansson", class: "admin")
end
+ def test_mail_to_with_special_characters
+ assert_dom_equal(
+ %{<a href="mailto:%23%21%24%25%26%27%2A%2B-%2F%3D%3F%5E_%60%7B%7D%7C%7E@example.org">#!$%&amp;&#39;*+-/=?^_`{}|~@example.org</a>},
+ mail_to("#!$%&'*+-/=?^_`{}|~@example.org")
+ )
+ end
+
def test_mail_with_options
assert_dom_equal(
%{<a href="mailto:me@example.com?cc=ccaddress%40example.com&amp;bcc=bccaddress%40example.com&amp;body=This%20is%20the%20body%20of%20the%20message.&amp;subject=This%20is%20an%20example%20email&amp;reply-to=foo%40bar.com">My email</a>},