aboutsummaryrefslogtreecommitdiffstats
path: root/actionview
diff options
context:
space:
mode:
Diffstat (limited to 'actionview')
-rw-r--r--actionview/CHANGELOG.md42
-rw-r--r--actionview/README.rdoc2
-rw-r--r--actionview/Rakefile38
-rwxr-xr-xactionview/bin/test4
-rw-r--r--actionview/lib/action_view/digestor.rb5
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb7
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/atom_feed_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb48
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb25
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb63
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb25
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb17
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb12
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb71
-rw-r--r--actionview/lib/action_view/layouts.rb25
-rw-r--r--actionview/lib/action_view/lookup_context.rb2
-rw-r--r--actionview/lib/action_view/railtie.rb6
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb4
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb4
-rw-r--r--actionview/lib/action_view/rendering.rb2
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb2
-rw-r--r--actionview/lib/action_view/template/resolver.rb20
-rw-r--r--actionview/lib/action_view/view_paths.rb4
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb40
-rw-r--r--actionview/test/actionpack/controller/render_test.rb68
-rw-r--r--actionview/test/activerecord/form_helper_activerecord_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/test/hyphen-ated.erb2
-rw-r--r--actionview/test/fixtures/test/_partial_name_in_local_assigns.erb1
-rw-r--r--actionview/test/template/asset_tag_helper_test.rb13
-rw-r--r--actionview/test/template/atom_feed_helper_test.rb22
-rw-r--r--actionview/test/template/capture_helper_test.rb6
-rw-r--r--actionview/test/template/date_helper_test.rb8
-rw-r--r--actionview/test/template/digestor_test.rb20
-rw-r--r--actionview/test/template/form_options_helper_test.rb7
-rw-r--r--actionview/test/template/form_tag_helper_test.rb7
-rw-r--r--actionview/test/template/record_tag_helper_test.rb3
-rw-r--r--actionview/test/template/render_test.rb25
-rw-r--r--actionview/test/template/template_test.rb30
-rw-r--r--actionview/test/template/test_case_test.rb58
-rw-r--r--actionview/test/template/translation_helper_test.rb29
-rw-r--r--actionview/test/template/url_helper_test.rb10
45 files changed, 444 insertions, 364 deletions
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 78ce230b3a..069d181674 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,43 @@
+* 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.
+
+ *Henrik Nygren*
+
+* Remove `:rescue_format` option for `translate` helper since it's no longer
+ supported by I18n.
+
+ *Bernard Potocki*
+
+* `translate` should handle `raise` flag correctly in case of both main and default
+ translation is missing.
+
+ Fixes #19967
+
+ *Bernard Potocki*
+
* Load the `default_form_builder` from the controller on initialization, which overrides
the global config if it is present.
@@ -108,7 +148,7 @@
*Angelo Capilleri*
-* Allow entries without a link tag in AtomFeedHelper.
+* Allow entries without a link tag in `AtomFeedHelper`.
*Daniel Gomez de Souza*
diff --git a/actionview/README.rdoc b/actionview/README.rdoc
index 5bb62c7562..8b1f85f748 100644
--- a/actionview/README.rdoc
+++ b/actionview/README.rdoc
@@ -9,7 +9,7 @@ used to inline short Ruby snippets inside HTML), and XML Builder.
The latest version of Action View can be installed with RubyGems:
- % [sudo] gem install actionview
+ % gem install actionview
Source code can be downloaded as part of the Rails project on GitHub
diff --git a/actionview/Rakefile b/actionview/Rakefile
index 2b752b83df..93be50721d 100644
--- a/actionview/Rakefile
+++ b/actionview/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
desc "Default Task"
task :default => :test
@@ -45,39 +44,8 @@ namespace :test do
end
end
-spec = eval(File.read('actionview.gemspec'))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-desc "Release to rubygems"
-task :release => :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
-
task :lines do
- lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
-
- FileList["lib/**/*.rb"].each do |file_name|
- next if file_name =~ /vendor/
- File.open(file_name, 'r') do |f|
- while line = f.gets
- lines += 1
- next if line =~ /^\s*$/
- next if line =~ /^\s*#/
- codelines += 1
- end
- end
- puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
-
- total_lines += lines
- total_codelines += codelines
-
- lines, codelines = 0, 0
- end
-
- puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
+ load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics'
+ files = FileList["lib/**/*.rb"]
+ CodeTools::LineStatistics.new(files).print_loc
end
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/digestor.rb b/actionview/lib/action_view/digestor.rb
index 1f103786cb..df8059d04e 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -12,7 +12,7 @@ module ActionView
# 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)
@@ -70,7 +70,8 @@ module ActionView
def dependencies
DependencyTracker.find_dependencies(name, template)
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 60fc9ee1a2..e32f8e219e 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -207,6 +207,7 @@ module ActionView
# # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
def image_tag(source, options={})
options = options.symbolize_keys
+ check_for_image_tag_errors(options)
src = options[:src] = path_to_image(source)
@@ -325,6 +326,12 @@ module ActionView
[size, size]
end
end
+
+ def check_for_image_tag_errors(options)
+ if options[:size] && (options[:height] || options[:width])
+ raise ArgumentError, "Cannot pass a :size option with a :height or :width option"
+ end
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index ef4a6c98c0..b19dc25025 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -121,6 +121,8 @@ 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
diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb
index d8be4e5678..bb1cdd0f8d 100644
--- a/actionview/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb
@@ -16,7 +16,7 @@ module ActionView
# end
#
# app/controllers/posts_controller.rb:
- # class PostsController < ApplicationController::Base
+ # class PostsController < ApplicationController
# # GET /posts.html
# # GET /posts.atom
# def index
@@ -51,7 +51,7 @@ module ActionView
# * <tt>:language</tt>: Defaults to "en-US".
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
- # * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}"
+ # * <tt>:id</tt>: The id for this feed. Defaults to "tag:localhost,2005:/posts", in this case.
# * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
# created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
# 2005 is used (as an "I don't care" value).
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 0e2a5f90f4..8945575860 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,8 @@ 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. Here are a few examples of things that can't be derived:
+ # 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
# render @project.documents.where(published: true).order('created_at')
@@ -97,19 +98,21 @@ module ActionView
# <%# Template Dependency: todolists/todolist %>
# <%= render_sortable_todolists @project.todolists %>
#
- # The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
+ # The pattern used to match these 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 of 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
+ # 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:
#
# <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
# <%= some_helper_method(person) %>
#
- # Now all you'll have to do is change that timestamp when the helper method changes.
+ # Now all you have to do is change that timestamp when the helper method changes.
#
# === Automatic Collection Caching
#
@@ -118,7 +121,7 @@ module ActionView
# <%= render @notifications %>
# <%= render partial: 'notifications/notification', collection: @notifications %>
#
- # If the notifications/_notification partial starts with a cache call like so:
+ # If the notifications/_notification partial starts with a cache call as:
#
# <% cache notification do %>
# <%= notification.name %>
@@ -127,14 +130,15 @@ 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 collection caching.
+ # 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)
- if controller.perform_caching
+ 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
yield
@@ -149,7 +153,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
@@ -165,22 +169,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
@@ -194,10 +199,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/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 46ce7cf0be..9c8edc69a9 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 lookup 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'
@@ -464,7 +485,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 +509,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)
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 7fdeca5ea8..3a9acafaa2 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -1631,7 +1631,7 @@ module ActionView
# target labels for radio_button tags (where the value is used in the ID of the input tag).
#
# ==== Examples
- # label(:post, :title)
+ # label(:title)
# # => <label for="post_title">Title</label>
#
# You can localize your labels based on model and attribute names.
@@ -1644,7 +1644,7 @@ module ActionView
#
# Which then will result in
#
- # label(:post, :body)
+ # label(:body)
# # => <label for="post_body">Write your entire text here</label>
#
# Localization can also be based purely on the translation of the attribute-name
@@ -1655,21 +1655,22 @@ module ActionView
# post:
# cost: "Total cost"
#
- # label(:post, :cost)
+ # label(:cost)
# # => <label for="post_cost">Total cost</label>
#
- # label(:post, :title, "A short title")
+ # label(:title, "A short title")
# # => <label for="post_title">A short title</label>
#
- # label(:post, :title, "A short title", class: "title_label")
+ # label(:title, "A short title", class: "title_label")
# # => <label for="post_title" class="title_label">A short title</label>
#
- # label(:post, :privacy, "Public Post", value: "public")
+ # label(:privacy, "Public Post", value: "public")
# # => <label for="post_privacy_public">Public Post</label>
#
- # label(:post, :terms) do
+ # label(:terms) do
# 'Accept <a href="/terms">Terms</a>.'.html_safe
# end
+ # # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
def label(method, text = nil, options = {}, &block)
@template.label(@object_name, method, text, objectify_options(options), &block)
end
@@ -1718,16 +1719,17 @@ module ActionView
# hashes instead of arrays.
#
# # Let's say that @post.validated? is 1:
- # check_box("post", "validated")
+ # check_box("validated")
# # => <input name="post[validated]" type="hidden" value="0" />
# # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
#
# # Let's say that @puppy.gooddog is "no":
- # check_box("puppy", "gooddog", {}, "yes", "no")
+ # check_box("gooddog", {}, "yes", "no")
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
#
- # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
+ # # Let's say that @eula.accepted is "no":
+ # check_box("accepted", { class: 'eula_check' }, "yes", "no")
# # => <input name="eula[accepted]" type="hidden" value="no" />
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
@@ -1742,13 +1744,14 @@ module ActionView
# +options+ hash. You may pass HTML options there as well.
#
# # Let's say that @post.category returns "rails":
- # radio_button("post", "category", "rails")
- # radio_button("post", "category", "java")
+ # radio_button("category", "rails")
+ # radio_button("category", "java")
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
#
- # radio_button("user", "receive_newsletter", "yes")
- # radio_button("user", "receive_newsletter", "no")
+ # # Let's say that @user.category returns "no":
+ # radio_button("receive_newsletter", "yes")
+ # radio_button("receive_newsletter", "no")
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
def radio_button(method, tag_value, options = {})
@@ -1761,14 +1764,17 @@ module ActionView
# shown.
#
# ==== Examples
- # hidden_field(:signup, :pass_confirm)
- # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
+ # # Let's say that @signup.pass_confirm returns true:
+ # hidden_field(:pass_confirm)
+ # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="true" />
#
- # hidden_field(:post, :tag_list)
- # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
+ # # Let's say that @post.tag_list returns "blog, ruby":
+ # hidden_field(:tag_list)
+ # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="blog, ruby" />
#
- # hidden_field(:user, :token)
- # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
+ # # Let's say that @user.token returns "abcde":
+ # hidden_field(:token)
+ # # => <input type="hidden" id="user_token" name="user[token]" value="abcde" />
#
def hidden_field(method, options = {})
@emitted_hidden_id = true if method == :id
@@ -1789,19 +1795,24 @@ module ActionView
# * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
#
# ==== Examples
- # file_field(:user, :avatar)
+ # # Let's say that @user has avatar:
+ # file_field(:avatar)
# # => <input type="file" id="user_avatar" name="user[avatar]" />
#
- # file_field(:post, :image, :multiple => true)
- # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
+ # # Let's say that @post has image:
+ # file_field(:image, :multiple => true)
+ # # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
#
- # file_field(:post, :attached, accept: 'text/html')
+ # # Let's say that @post has attached:
+ # file_field(:attached, accept: 'text/html')
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
#
- # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
+ # # Let's say that @post has image:
+ # file_field(:image, accept: 'image/png,image/gif,image/jpeg')
# # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
#
- # file_field(:attachment, :file, class: 'file_input')
+ # # Let's say that @attachment has file:
+ # file_field(:file, class: 'file_input')
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
def file_field(method, options = {})
self.multipart = true
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index 8a5928477f..1b7b188d65 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -80,7 +80,7 @@ module ActionView
#
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
#
- # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
+ # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: -> (category) { category.archived? }})
#
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
# <select name="post[category_id]" id="post_category_id">
@@ -410,7 +410,7 @@ module ActionView
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
# array of child objects representing the <tt><option></tt> tags.
- # * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
+ # * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
@@ -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..896020bc15 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -448,8 +448,9 @@ module ActionView
# <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 +473,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 +489,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>
#
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/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/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index 9d7390f1fd..0615bd2e0d 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -7,34 +7,47 @@ module ActionView
module Helpers
module TranslationHelper
include TagHelper
- # Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
+ # Delegates to <tt>I18n#translate</tt> but also performs three additional
+ # functions.
#
- # First, it will ensure that any thrown +MissingTranslation+ messages will be turned
- # into inline spans that:
+ # First, it will ensure that any thrown +MissingTranslation+ messages will
+ # be rendered as inline spans that:
#
- # * have a "translation-missing" class set,
- # * contain the missing key as a title attribute and
- # * a titleized version of the last key segment as a text.
+ # * Have a <tt>translation-missing</tt> class applied
+ # * Contain the missing key as the value of the +title+ attribute
+ # * Have a titleized version of the last key segment as text
#
- # E.g. the value returned for a missing translation key :"blog.post.title" will be
- # <span class="translation_missing" title="translation missing: en.blog.post.title">Title</span>.
- # This way your views will display rather reasonable strings but it will still
- # be easy to spot missing translations.
+ # For example, the value returned for the missing translation key
+ # <tt>"blog.post.title"</tt> will be:
#
- # Second, it'll scope the key by the current partial if the key starts
- # with a period. So if you call <tt>translate(".foo")</tt> from the
- # <tt>people/index.html.erb</tt> template, you'll actually be calling
- # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
- # to translate many keys within the same partials and gives you a simple framework
- # for scoping them consistently. If you don't prepend the key with a period,
- # nothing is converted.
+ # <span
+ # class="translation_missing"
+ # title="translation missing: en.blog.post.title">Title</span>
#
- # Third, it'll mark the translation as safe HTML if the key has the suffix
- # "_html" or the last element of the key is the word "html". For example,
- # calling translate("footer_html") or translate("footer.html") will return
- # a safe HTML string that won't be escaped by other HTML helper methods. This
- # naming convention helps to identify translations that include HTML tags so that
- # you know what kind of output to expect when you call translate in a template.
+ # This allows for views to display rather reasonable strings while still
+ # giving developers a way to find missing translations.
+ #
+ # If you would prefer missing translations to raise an error, you can
+ # opt out of span-wrapping behavior globally by setting
+ # <tt>ActionView::Base.raise_on_missing_translations = true</tt> or
+ # individually by passing <tt>raise: true</tt> as an option to
+ # <tt>translate</tt>.
+ #
+ # Second, if the key starts with a period <tt>translate</tt> will scope
+ # the key by the current partial. Calling <tt>translate(".foo")</tt> from
+ # the <tt>people/index.html.erb</tt> template is equivalent to calling
+ # <tt>translate("people.index.foo")</tt>. This makes it less
+ # repetitive to translate many keys within the same partial and provides
+ # a convention to scope keys consistently.
+ #
+ # Third, the translation will be marked as <tt>html_safe</tt> if the key
+ # has the suffix "_html" or the last element of the key is "html". Calling
+ # <tt>translate("footer_html")</tt> or <tt>translate("footer.html")</tt>
+ # will return an HTML safe string that won't be escaped by other HTML
+ # helper methods. This naming convention helps to identify translations
+ # that include HTML tags so that you know what kind of output to expect
+ # when you call translate in a template and translators know which keys
+ # they can provide HTML values for.
def translate(key, options = {})
options = options.dup
has_default = options.has_key?(:default)
@@ -47,12 +60,12 @@ module ActionView
# If the user has explicitly decided to NOT raise errors, pass that option to I18n.
# Otherwise, tell I18n to raise an exception, which we rescue further in this method.
# Note: `raise_error` refers to us re-raising the error in this method. I18n is forced to raise by default.
- if options[:raise] == false || (options.key?(:rescue_format) && options[:rescue_format].nil?)
+ if options[:raise] == false
raise_error = false
- options[:raise] = false
+ i18n_raise = false
else
- raise_error = options[:raise] || options[:rescue_format] || ActionView::Base.raise_on_missing_translations
- options[:raise] = true
+ raise_error = options[:raise] || ActionView::Base.raise_on_missing_translations
+ i18n_raise = true
end
if html_safe_translation_key?(key)
@@ -62,11 +75,11 @@ module ActionView
html_safe_options[name] = ERB::Util.html_escape(value.to_s)
end
end
- translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
+ translation = I18n.translate(scope_key_by_partial(key), html_safe_options.merge(raise: i18n_raise))
translation.respond_to?(:html_safe) ? translation.html_safe : translation
else
- I18n.translate(scope_key_by_partial(key), options)
+ I18n.translate(scope_key_by_partial(key), options.merge(raise: i18n_raise))
end
rescue I18n::MissingTranslationData => e
if remaining_defaults.present?
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..817b30dd2f 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -6,7 +6,7 @@ require 'action_view/template/resolver'
module ActionView
# = Action View Lookup Context
#
- # LookupContext is the object responsible to hold all information required to lookup
+ # <tt>LookupContext</tt> 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.
diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb
index 9a26cba574..5dc7950d6b 100644
--- a/actionview/lib/action_view/railtie.rb
+++ b/actionview/lib/action_view/railtie.rb
@@ -48,8 +48,10 @@ module ActionView
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 cd151c0189..780fdabbd1 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -338,7 +338,7 @@ module ActionView
end
object ||= locals[as]
- locals[as] = object
+ locals[as] = object if @has_object
content = @template.render(view, locals) do |*name|
view._layout_for(*name, &block)
@@ -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.
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 b77c884e66..1147963882 100644
--- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
@@ -6,7 +6,7 @@ module ActionView
included do
# Fallback cache store if Action View is used without Rails.
- # Otherwise overriden in Railtie to use Rails.cache.
+ # Otherwise overridden in Railtie to use Rails.cache.
mattr_accessor(:collection_cache) { ActiveSupport::Cache::MemoryStore.new }
end
@@ -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..91b7e845d6 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]
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index 88a8570706..da96347e4d 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -138,7 +138,7 @@ module ActionView
#
# <% cache notification.event do %> # => nil
def resource_cache_call_pattern
- /\A(?:<%#.*%>\n?)?<% cache\(?\s*(\w+\.?)/
+ /\A(?:<%#.*%>)*\s*<%\s*cache\(?\s*(\w+)[\s\)]/m
end
private
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index 955118a554..2b00b0303d 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -181,9 +181,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,29 +193,29 @@ 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)
@@ -234,7 +234,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('.')
pieces.shift
extension = pieces.pop
diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb
index 492f67f45d..ebca598337 100644
--- a/actionview/lib/action_view/view_paths.rb
+++ b/actionview/lib/action_view/view_paths.rb
@@ -36,8 +36,8 @@ 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
+ # <tt>LookupContext</tt> is the object responsible to hold all information required to lookup
+ # templates, i.e. view paths and details. Check <tt>ActionView::LookupContext</tt> for more
# information.
def lookup_context
@_lookup_context ||=
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 8b47536a18..ebaf39a12d 100644
--- a/actionview/test/actionpack/controller/render_test.rb
+++ b/actionview/test/actionpack/controller/render_test.rb
@@ -1,5 +1,5 @@
require 'abstract_unit'
-require "active_model"
+require 'active_model'
class ApplicationController < ActionController::Base
self.view_paths = File.join(FIXTURE_LOAD_PATH, "actionpack")
@@ -357,7 +357,7 @@ class TestController < ApplicationController
end
def rendering_nothing_on_layout
- render :nothing => true
+ head :ok
end
def render_to_string_with_assigns
@@ -683,20 +683,19 @@ class RenderTest < ActionController::TestCase
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
@@ -712,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
@@ -743,7 +740,7 @@ 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
@@ -756,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:
@@ -950,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:
@@ -1035,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
@@ -1045,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
@@ -1107,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
@@ -1173,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
@@ -1212,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
@@ -1270,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/activerecord/form_helper_activerecord_test.rb b/actionview/test/activerecord/form_helper_activerecord_test.rb
index 0a62f49f35..2769b97445 100644
--- a/actionview/test/activerecord/form_helper_activerecord_test.rb
+++ b/actionview/test/activerecord/form_helper_activerecord_test.rb
@@ -35,10 +35,6 @@ class FormHelperActiveRecordTest < ActionView::TestCase
end
end
- def _routes
- Routes
- end
-
include Routes.url_helpers
def test_nested_fields_for_with_child_index_option_override_on_a_nested_attributes_collection_association
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/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/test/_partial_name_in_local_assigns.erb b/actionview/test/fixtures/test/_partial_name_in_local_assigns.erb
new file mode 100644
index 0000000000..28ee9f41c5
--- /dev/null
+++ b/actionview/test/fixtures/test/_partial_name_in_local_assigns.erb
@@ -0,0 +1 @@
+<%= local_assigns.has_key?(:partial_name_in_local_assigns) %> \ 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 02dce71496..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"))
@@ -464,6 +469,14 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal({:size => '16x10'}, options)
end
+ def test_image_tag_raises_an_error_for_competing_size_arguments
+ exception = assert_raise(ArgumentError) do
+ image_tag("gold.png", :height => "100", :width => "200", :size => "45x70")
+ end
+
+ assert_equal("Cannot pass a :size option with a :height or :width option", exception.message)
+ end
+
def test_favicon_link_tag
FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
diff --git a/actionview/test/template/atom_feed_helper_test.rb b/actionview/test/template/atom_feed_helper_test.rb
index 525d99750d..591cd71404 100644
--- a/actionview/test/template/atom_feed_helper_test.rb
+++ b/actionview/test/template/atom_feed_helper_test.rb
@@ -14,7 +14,7 @@ class ScrollsController < ActionController::Base
FEEDS["defaults"] = <<-EOT
atom_feed(:schema_date => '2008') do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -31,7 +31,7 @@ class ScrollsController < ActionController::Base
FEEDS["entry_options"] = <<-EOT
atom_feed do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param.to_s, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
@@ -48,7 +48,7 @@ class ScrollsController < ActionController::Base
FEEDS["entry_type_options"] = <<-EOT
atom_feed(:schema_date => '2008') do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll, :type => 'text/xml') do |entry|
@@ -65,7 +65,7 @@ class ScrollsController < ActionController::Base
FEEDS["entry_url_false_option"] = <<-EOT
atom_feed do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll, :url => false) do |entry|
@@ -82,7 +82,7 @@ class ScrollsController < ActionController::Base
FEEDS["xml_block"] = <<-EOT
atom_feed do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
feed.author do |author|
author.name("DHH")
@@ -100,7 +100,7 @@ class ScrollsController < ActionController::Base
atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -118,7 +118,7 @@ class ScrollsController < ActionController::Base
FEEDS["feed_with_overridden_ids"] = <<-EOT
atom_feed({:id => 'tag:test.rubyonrails.org,2008:test/'}) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll, :id => "tag:test.rubyonrails.org,2008:"+scroll.id.to_s) do |entry|
@@ -137,7 +137,7 @@ class ScrollsController < ActionController::Base
atom_feed(:schema_date => '2008',
:instruct => {'xml-stylesheet' => { :href=> 't.css', :type => 'text/css' }}) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -155,7 +155,7 @@ class ScrollsController < ActionController::Base
atom_feed(:schema_date => '2008',
:instruct => {'target1' => [{ :a => '1', :b => '2' }, { :c => '3', :d => '4' }]}) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -172,7 +172,7 @@ class ScrollsController < ActionController::Base
FEEDS["feed_with_xhtml_content"] = <<-'EOT'
atom_feed do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -197,7 +197,7 @@ class ScrollsController < ActionController::Base
new_xml = Builder::XmlMarkup.new(:target=>'')
atom_feed(:xml => new_xml) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb
index f213da5934..1e099d482c 100644
--- a/actionview/test/template/capture_helper_test.rb
+++ b/actionview/test/template/capture_helper_test.rb
@@ -210,10 +210,4 @@ class CaptureHelperTest < ActionView::TestCase
def alt_encoding(output_buffer)
output_buffer.encoding == Encoding::US_ASCII ? Encoding::UTF_8 : Encoding::US_ASCII
end
-
- def view_with_controller
- TestController.new.view_context.tap do |view|
- view.output_buffer = ActionView::OutputBuffer.new
- end
- end
end
diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb
index a0e1f5f3bb..9212420ec9 100644
--- a/actionview/test/template/date_helper_test.rb
+++ b/actionview/test/template/date_helper_test.rb
@@ -3217,12 +3217,4 @@ class DateHelperTest < ActionView::TestCase
expected = '<time datetime="2013-02-20T00:00:00+00:00">20 Feb 00:00</time>'
assert_equal expected, time_tag(time, :format => :short)
end
-
- protected
- def with_env_tz(new_tz = 'US/Eastern')
- old_tz, ENV['TZ'] = ENV['TZ'], new_tz
- yield
- ensure
- old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
- end
end
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index c2b8439df3..f0afcdb5ae 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -111,6 +111,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")
@@ -298,6 +310,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
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..f602c82c42 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -510,6 +510,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 22665b6844..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
@@ -277,6 +280,14 @@ module RenderTestCases
assert_nil @view.render(:partial => "test/customer", :collection => nil)
end
+ def test_render_partial_without_object_does_not_put_partial_name_to_local_assigns
+ assert_equal 'false', @view.render(partial: 'test/partial_name_in_local_assigns')
+ end
+
+ def test_render_partial_with_nil_object_puts_partial_name_to_local_assigns
+ assert_equal 'true', @view.render(partial: 'test/partial_name_in_local_assigns', object: nil)
+ end
+
def test_render_partial_with_nil_values_in_collection
assert_equal "Hello: davidHello: Anonymous", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), nil ])
end
@@ -608,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')
@@ -618,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')
@@ -628,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..d17034e88f 100644
--- a/actionview/test/template/template_test.rb
+++ b/actionview/test/template/template_test.rb
@@ -190,6 +190,36 @@ 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 %>"
+ ].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
+ [
+ "<% 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 %>"
+ ].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..05c3dc0613 100644
--- a/actionview/test/template/test_case_test.rb
+++ b/actionview/test/template/test_case_test.rb
@@ -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/translation_helper_test.rb b/actionview/test/template/translation_helper_test.rb
index df096b3c3a..5dc281adb2 100644
--- a/actionview/test/template/translation_helper_test.rb
+++ b/actionview/test/template/translation_helper_test.rb
@@ -41,8 +41,8 @@ class TranslationHelperTest < ActiveSupport::TestCase
I18n.backend.reload!
end
- def test_delegates_to_i18n_setting_the_rescue_format_option_to_html
- I18n.expects(:translate).with(:foo, :locale => 'en', :raise=>true).returns("")
+ def test_delegates_setting_to_i18n
+ I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true).returns("")
translate :foo, :locale => 'en'
end
@@ -58,12 +58,6 @@ class TranslationHelperTest < ActiveSupport::TestCase
assert_equal true, translate(:"translations.missing").html_safe?
end
- def test_returns_missing_translation_message_using_nil_as_rescue_format
- expected = 'translation missing: en.translations.missing'
- assert_equal expected, translate(:"translations.missing", :rescue_format => nil)
- assert_equal false, translate(:"translations.missing", :rescue_format => nil).html_safe?
- end
-
def test_raises_missing_translation_message_with_raise_config_option
ActionView::Base.raise_on_missing_translations = true
@@ -96,12 +90,6 @@ class TranslationHelperTest < ActiveSupport::TestCase
I18n.exception_handler = old_exception_handler
end
- def test_i18n_translate_defaults_to_nil_rescue_format
- expected = 'translation missing: en.translations.missing'
- assert_equal expected, I18n.translate(:"translations.missing")
- assert_equal false, I18n.translate(:"translations.missing").html_safe?
- end
-
def test_translation_returning_an_array
expected = %w(foo bar)
assert_equal expected, translate(:"translations.array")
@@ -157,6 +145,19 @@ class TranslationHelperTest < ActiveSupport::TestCase
assert_equal true, translation.html_safe?
end
+ def test_translate_with_missing_default
+ translation = translate(:'translations.missing', :default => :'translations.missing_html')
+ expected = '<span class="translation_missing" title="translation missing: en.translations.missing_html">Missing Html</span>'
+ assert_equal expected, translation
+ assert_equal true, translation.html_safe?
+ end
+
+ def test_translate_with_missing_default_and_raise_option
+ assert_raise(I18n::MissingTranslationData) do
+ translate(:'translations.missing', :default => :'translations.missing_html', :raise => true)
+ end
+ end
+
def test_translate_with_two_defaults_named_html
translation = translate(:'translations.missing', :default => [:'translations.missing_html', :'translations.hello_html'])
assert_equal '<a>Hello World</a>', translation
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index ef4df0407a..0e35c67516 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -379,6 +379,11 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_dom_equal %{<a href="/">Listing</a>}, link_to_if(true, "Listing", url_hash)
end
+ def test_link_to_if_with_block
+ assert_equal "Fallback", link_to_if(false, "Showing", url_hash) { "Fallback" }
+ assert_dom_equal %{<a href="/">Listing</a>}, link_to_if(true, "Listing", url_hash) { "Fallback" }
+ end
+
def request_for_url(url, opts = {})
env = Rack::MockRequest.env_for("http://www.example.com#{url}", opts)
ActionDispatch::Request.new(env)
@@ -479,6 +484,11 @@ class UrlHelperTest < ActiveSupport::TestCase
link_to_unless_current("Listing", "http://www.example.com/")
end
+ def test_link_to_unless_with_block
+ assert_dom_equal %{<a href="/">Showing</a>}, link_to_unless(false, "Showing", url_hash) { "Fallback" }
+ assert_equal "Fallback", link_to_unless(true, "Listing", url_hash) { "Fallback" }
+ end
+
def test_mail_to
assert_dom_equal %{<a href="mailto:david@loudthinking.com">david@loudthinking.com</a>}, mail_to("david@loudthinking.com")
assert_dom_equal %{<a href="mailto:david@loudthinking.com">David Heinemeier Hansson</a>}, mail_to("david@loudthinking.com", "David Heinemeier Hansson")