aboutsummaryrefslogtreecommitdiffstats
path: root/actionview
diff options
context:
space:
mode:
Diffstat (limited to 'actionview')
-rw-r--r--actionview/CHANGELOG.md5
-rw-r--r--actionview/lib/action_view/dependency_tracker.rb4
-rw-r--r--actionview/lib/action_view/digestor.rb6
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb15
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb22
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb3
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_check_boxes.rb25
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_helpers.rb26
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb10
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb9
-rw-r--r--actionview/lib/action_view/lookup_context.rb8
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb6
-rw-r--r--actionview/lib/action_view/template/resolver.rb4
-rw-r--r--actionview/test/abstract_unit.rb1
-rw-r--r--actionview/test/actionpack/controller/capture_test.rb2
-rw-r--r--actionview/test/actionpack/controller/render_test.rb2
-rw-r--r--actionview/test/template/dependency_tracker_test.rb1
-rw-r--r--actionview/test/template/form_collections_helper_test.rb42
-rw-r--r--actionview/test/template/form_helper_test.rb36
-rw-r--r--actionview/test/template/form_tag_helper_test.rb6
-rw-r--r--actionview/test/template/lookup_context_test.rb6
-rw-r--r--actionview/test/template/number_helper_test.rb1
-rw-r--r--actionview/test/template/render_test.rb10
-rw-r--r--actionview/test/template/translation_helper_test.rb6
24 files changed, 185 insertions, 71 deletions
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 9ea250643a..82a88952c3 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Add a `hidden_field` on the `collection_radio_buttons` to avoid raising a error
+ when the only input on the form is the `collection_radio_buttons`.
+
+ *Mauro George*
+
* `url_for` does not modify its arguments when generating polymorphic URLs.
*Bernerd Schaefer*
diff --git a/actionview/lib/action_view/dependency_tracker.rb b/actionview/lib/action_view/dependency_tracker.rb
index 6e8c7f8203..7716955fd9 100644
--- a/actionview/lib/action_view/dependency_tracker.rb
+++ b/actionview/lib/action_view/dependency_tracker.rb
@@ -1,9 +1,9 @@
-require 'thread_safe'
+require 'concurrent'
require 'action_view/path_set'
module ActionView
class DependencyTracker # :nodoc:
- @trackers = ThreadSafe::Cache.new
+ @trackers = Concurrent::Map.new
def self.find_dependencies(name, template, view_paths = nil)
tracker = @trackers[template.handler]
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 9a8a4feb2e..12e9723a02 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -1,11 +1,11 @@
-require 'thread_safe'
+require 'concurrent'
require 'action_view/dependency_tracker'
require 'monitor'
module ActionView
class Digestor
cattr_reader(:cache)
- @@cache = ThreadSafe::Cache.new
+ @@cache = Concurrent::Map.new
@@digest_monitor = Monitor.new
class PerRequestDigestCacheExpiry < Struct.new(:app) # :nodoc:
@@ -28,7 +28,7 @@ module ActionView
cache_key = ([ options[:name], options[:finder].details_key.hash ].compact + Array.wrap(options[:dependencies])).join('.')
# this is a correctly done double-checked locking idiom
- # (ThreadSafe::Cache's lookups have volatile semantics)
+ # (Concurrent::Map's lookups have volatile semantics)
@@cache[cache_key] || @@digest_monitor.synchronize do
@@cache.fetch(cache_key) do # re-check under lock
compute_and_store_digest(cache_key, options)
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index ebf5ea5b77..2a367b85af 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -849,8 +849,8 @@ module ActionView
# file_field(:user, :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" />
+ # file_field(:post, :image, multiple: true)
+ # # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
#
# file_field(:post, :attached, accept: 'text/html')
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
@@ -1038,7 +1038,7 @@ module ActionView
# date_field("user", "born_on")
# # => <input id="user_born_on" name="user[born_on]" type="date" />
#
- # The default value is generated by trying to call "to_date"
+ # The default value is generated by trying to call +strftime+ with "%Y-%m-%d"
# on the object's value, which makes it behave as expected for instances
# of DateTime and ActiveSupport::TimeWithZone. You can still override that
# by passing the "value" option explicitly, e.g.
@@ -1617,7 +1617,14 @@ module ActionView
@auto_index
end
- record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
+ record_name = if index
+ "#{object_name}[#{index}][#{record_name}]"
+ elsif record_name.to_s.end_with?('[]')
+ record_name = record_name.to_s.sub(/(.*)\[\]$/, "[\\1][#{record_object.id}]")
+ "#{object_name}#{record_name}"
+ else
+ "#{object_name}[#{record_name}]"
+ end
fields_options[:child_index] = index
@template.fields_for(record_name, record_object, fields_options, &block)
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index 728e633bbf..430051379d 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -548,7 +548,7 @@ module ActionView
end
# Returns a string of option tags for pretty much any time zone in the
- # world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
+ # world. Supply an ActiveSupport::TimeZone name as +selected+ to have it
# marked as the selected option tag. You can also supply an array of
# ActiveSupport::TimeZone objects as +priority_zones+, so that they will
# be listed above the rest of the (long) list. (You can use
@@ -556,7 +556,7 @@ module ActionView
# of the US time zones, or a Regexp to select the zones of your choice)
#
# The +selected+ parameter must be either +nil+, or a string that names
- # a ActiveSupport::TimeZone.
+ # an ActiveSupport::TimeZone.
#
# By default, +model+ is the ActiveSupport::TimeZone constant (which can
# be obtained in Active Record as a value object). The only requirement
@@ -644,6 +644,24 @@ module ActionView
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
# b.label(:"data-value" => b.value) { b.radio_button + b.text }
# end
+ #
+ # ==== Gotcha
+ #
+ # The HTML specification says when nothing is select on a collection of radio buttons
+ # web browsers do not send any value to server.
+ # Unfortunately this introduces a gotcha:
+ # if a +User+ model has a +category_id+ field, and in the form none category is selected no +category_id+ parameter is sent. So,
+ # any strong parameters idiom like
+ #
+ # params.require(:user).permit(...)
+ #
+ # will raise an error since no +{user: ...}+ will be present.
+ #
+ # To prevent this the helper generates an auxiliary hidden field before
+ # every collection of radio buttons. The hidden field has the same name as collection radio button and blank value.
+ #
+ # In case if you don't want the helper to generate this hidden field you can specify
+ # <tt>include_hidden: false</tt> option.
def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
end
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index 13effa592d..d7182d1fac 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -1,4 +1,3 @@
-
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/string/output_safety'
require 'active_support/number_helper'
@@ -140,7 +139,7 @@ module ActionView
# number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
# number_to_percentage(1000, locale: :fr) # => 1 000,000%
# number_to_percentage("98a") # => 98a%
- # number_to_percentage(100, format: "%n %") # => 100 %
+ # number_to_percentage(100, format: "%n %") # => 100.000 %
#
# number_to_percentage("98a", raise: true) # => InvalidNumberError
def number_to_percentage(number, options = {})
diff --git a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
index 1765fa6558..3256d44e18 100644
--- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
@@ -9,29 +9,13 @@ module ActionView
class CheckBoxBuilder < Builder # :nodoc:
def check_box(extra_html_options={})
html_options = extra_html_options.merge(@input_html_options)
+ html_options[:multiple] = true
@template_object.check_box(@object_name, @method_name, html_options, @value, nil)
end
end
def render(&block)
- rendered_collection = render_collection do |item, value, text, default_html_options|
- default_html_options[:multiple] = true
- builder = instantiate_builder(CheckBoxBuilder, item, value, text, default_html_options)
-
- if block_given?
- @template_object.capture(builder, &block)
- else
- render_component(builder)
- end
- end
-
- # Append a hidden field to make sure something will be sent back to the
- # server if all check boxes are unchecked.
- if @options.fetch(:include_hidden, true)
- rendered_collection + hidden_field
- else
- rendered_collection
- end
+ render_collection_for(CheckBoxBuilder, &block)
end
private
@@ -39,11 +23,6 @@ module ActionView
def render_component(builder)
builder.check_box + builder.label
end
-
- def hidden_field
- hidden_name = @html_options[:name] || "#{tag_name(false, @options[:index])}[]"
- @template_object.hidden_field_tag(hidden_name, "", id: nil)
- end
end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
index 8050638363..fea4c8d4ec 100644
--- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
@@ -79,6 +79,32 @@ module ActionView
yield item, value, text, default_html_options.merge(additional_html_options)
end.join.html_safe
end
+
+ def render_collection_for(builder_class, &block) #:nodoc:
+ options = @options.stringify_keys
+ rendered_collection = render_collection do |item, value, text, default_html_options|
+ builder = instantiate_builder(builder_class, item, value, text, default_html_options)
+
+ if block_given?
+ @template_object.capture(builder, &block)
+ else
+ render_component(builder)
+ end
+ end
+
+ # Append a hidden field to make sure something will be sent back to the
+ # server if all radio buttons are unchecked.
+ if options.fetch('include_hidden', true)
+ rendered_collection + hidden_field
+ else
+ rendered_collection
+ end
+ end
+
+ def hidden_field #:nodoc:
+ hidden_name = @html_options[:name] || "#{tag_name(false, @options[:index])}[]"
+ @template_object.hidden_field_tag(hidden_name, "", id: nil)
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
index 20be34c1f2..21aaf122f8 100644
--- a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
@@ -14,15 +14,7 @@ module ActionView
end
def render(&block)
- render_collection do |item, value, text, default_html_options|
- builder = instantiate_builder(RadioButtonBuilder, item, value, text, default_html_options)
-
- if block_given?
- @template_object.capture(builder, &block)
- else
- render_component(builder)
- end
- end
+ render_collection_for(RadioButtonBuilder, &block)
end
private
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index 0615bd2e0d..dde1ef22ac 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -88,7 +88,14 @@ module ActionView
raise e if raise_error
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
- content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
+ title = "translation missing: #{keys.join('.')}"
+
+ interpolations = options.except(:default)
+ if interpolations.any?
+ title << ", " << interpolations.map { |k, v| "#{k}: #{ERB::Util.html_escape(v)}" }.join(', ')
+ end
+
+ content_tag('span', keys.last.to_s.titleize, class: 'translation_missing', title: title)
end
end
alias :t :translate
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index fba9a44cb1..ec6edfaaa3 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -1,4 +1,4 @@
-require 'thread_safe'
+require 'concurrent'
require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/module/attribute_accessors'
require 'action_view/template/resolver'
@@ -20,7 +20,7 @@ module ActionView
mattr_accessor :registered_details
self.registered_details = []
- def self.register_detail(name, options = {}, &block)
+ def self.register_detail(name, &block)
self.registered_details << name
initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
@@ -55,14 +55,14 @@ module ActionView
end
register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
register_detail(:variants) { [] }
- register_detail(:handlers){ Template::Handlers.extensions }
+ register_detail(:handlers) { Template::Handlers.extensions }
class DetailsKey #:nodoc:
alias :eql? :equal?
alias :object_hash :hash
attr_reader :hash
- @details_keys = ThreadSafe::Cache.new
+ @details_keys = Concurrent::Map.new
def self.get(details)
if details[:formats]
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index 1f9e960488..39c8658ffe 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -1,5 +1,5 @@
require 'action_view/renderer/partial_renderer/collection_caching'
-require 'thread_safe'
+require 'concurrent'
module ActionView
class PartialIteration
@@ -283,8 +283,8 @@ module ActionView
class PartialRenderer < AbstractRenderer
include CollectionCaching
- PREFIXED_PARTIAL_NAMES = ThreadSafe::Cache.new do |h, k|
- h[k] = ThreadSafe::Cache.new
+ PREFIXED_PARTIAL_NAMES = Concurrent::Map.new do |h, k|
+ h[k] = Concurrent::Map.new
end
def initialize(*)
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index 28967f40a6..7859c58b43 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -3,7 +3,7 @@ require "active_support/core_ext/class"
require "active_support/core_ext/module/attribute_accessors"
require "action_view/template"
require "thread"
-require "thread_safe"
+require "concurrent"
module ActionView
# = Action View Resolver
@@ -35,7 +35,7 @@ module ActionView
# Threadsafe template cache
class Cache #:nodoc:
- class SmallCache < ThreadSafe::Cache
+ class SmallCache < Concurrent::Map
def initialize(options = {})
super(options.merge(:initial_capacity => 2))
end
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb
index 12870acaa6..2354e91822 100644
--- a/actionview/test/abstract_unit.rb
+++ b/actionview/test/abstract_unit.rb
@@ -151,7 +151,6 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
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
diff --git a/actionview/test/actionpack/controller/capture_test.rb b/actionview/test/actionpack/controller/capture_test.rb
index f8387b27b0..933456ce9d 100644
--- a/actionview/test/actionpack/controller/capture_test.rb
+++ b/actionview/test/actionpack/controller/capture_test.rb
@@ -54,7 +54,7 @@ class CaptureTest < ActionController::TestCase
assert_equal expected_content_for_output, @response.body
end
- def test_should_concatentate_content_for
+ def test_should_concatenate_content_for
get :content_for_concatenated
assert_equal expected_content_for_output, @response.body
end
diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb
index 8d048ddbcb..27150c7d5f 100644
--- a/actionview/test/actionpack/controller/render_test.rb
+++ b/actionview/test/actionpack/controller/render_test.rb
@@ -1122,7 +1122,7 @@ class RenderTest < ActionController::TestCase
assert_equal "<title>Putting stuff in the title!</title>\nGreat stuff!\n", @response.body
end
- def test_overwritting_rendering_relative_file_with_extension
+ def test_overwriting_rendering_relative_file_with_extension
get :hello_world_from_rxml_using_template
assert_equal "<html>\n <p>Hello</p>\n</html>\n", @response.body
diff --git a/actionview/test/template/dependency_tracker_test.rb b/actionview/test/template/dependency_tracker_test.rb
index 672b4747ec..3ece9e50cd 100644
--- a/actionview/test/template/dependency_tracker_test.rb
+++ b/actionview/test/template/dependency_tracker_test.rb
@@ -1,4 +1,3 @@
-
require 'abstract_unit'
require 'action_view/dependency_tracker'
diff --git a/actionview/test/template/form_collections_helper_test.rb b/actionview/test/template/form_collections_helper_test.rb
index b193d387c3..41932d15ee 100644
--- a/actionview/test/template/form_collections_helper_test.rb
+++ b/actionview/test/template/form_collections_helper_test.rb
@@ -198,6 +198,41 @@ class FormCollectionsHelperTest < ActionView::TestCase
assert_select 'input[type=radio][value=false][checked=checked]'
end
+ test 'collection radio buttons generates only one hidden field for the entire collection, to ensure something will be sent back to the server when posting an empty collection' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_radio_buttons :user, :category_ids, collection, :id, :name
+
+ assert_select "input[type=hidden][name='user[category_ids][]'][value='']", count: 1
+ end
+
+ test 'collection radio buttons generates a hidden field using the given :name in :html_options' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_radio_buttons :user, :category_ids, collection, :id, :name, {}, { name: "user[other_category_ids][]" }
+
+ assert_select "input[type=hidden][name='user[other_category_ids][]'][value='']", count: 1
+ end
+
+ test 'collection radio buttons generates a hidden field with index if it was provided' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_radio_buttons :user, :category_ids, collection, :id, :name, { index: 322 }
+
+ assert_select "input[type=hidden][name='user[322][category_ids][]'][value='']", count: 1
+ end
+
+ test 'collection radio buttons does not generate a hidden field if include_hidden option is false' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_radio_buttons :user, :category_ids, collection, :id, :name, include_hidden: false
+
+ assert_select "input[type=hidden][name='user[category_ids][]'][value='']", count: 0
+ end
+
+ test 'collection radio buttons does not generate a hidden field if include_hidden option is false with key as string' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_radio_buttons :user, :category_ids, collection, :id, :name, 'include_hidden' => false
+
+ assert_select "input[type=hidden][name='user[category_ids][]'][value='']", count: 0
+ end
+
# COLLECTION CHECK BOXES
test 'collection check boxes accepts a collection and generate a series of checkboxes for value method' do
collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
@@ -235,6 +270,13 @@ class FormCollectionsHelperTest < ActionView::TestCase
assert_select "input[type=hidden][name='user[category_ids][]'][value='']", :count => 0
end
+ test 'collection check boxes does not generate a hidden field if include_hidden option is false with key as string' do
+ collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
+ with_collection_check_boxes :user, :category_ids, collection, :id, :name, 'include_hidden' => false
+
+ assert_select "input[type=hidden][name='user[category_ids][]'][value='']", count: 0
+ end
+
test 'collection check boxes accepts a collection and generate a series of checkboxes with labels for label method' do
collection = [Category.new(1, 'Category 1'), Category.new(2, 'Category 2')]
with_collection_check_boxes :user, :category_ids, collection, :id, :name
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index aef137935a..41f31f1582 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -366,7 +366,7 @@ class FormHelperTest < ActionView::TestCase
)
end
- def test_label_with_to_model_and_overriden_model_name
+ def test_label_with_to_model_and_overridden_model_name
with_locale :label do
assert_dom_equal(
%{<label for="post_delegator_title">Delegate model_name title</label>},
@@ -1596,7 +1596,8 @@ class FormHelperTest < ActionView::TestCase
"<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
"<label for='post_active_true'>true</label>" +
"<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
- "<label for='post_active_false'>false</label>"
+ "<label for='post_active_false'>false</label>" +
+ "<input type='hidden' name='post[active][]' value='' />"
end
assert_dom_equal expected, output_buffer
@@ -1619,7 +1620,8 @@ class FormHelperTest < ActionView::TestCase
"true</label>" +
"<label for='post_active_false'>"+
"<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
- "false</label>"
+ "false</label>" +
+ "<input type='hidden' name='post[active][]' value='' />"
end
assert_dom_equal expected, output_buffer
@@ -1645,6 +1647,7 @@ class FormHelperTest < ActionView::TestCase
"<label for='post_active_false'>"+
"<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
"false</label>"+
+ "<input type='hidden' name='post[active][]' value='' />" +
"<input id='post_id' name='post[id]' type='hidden' value='1' />"
end
@@ -1663,7 +1666,8 @@ class FormHelperTest < ActionView::TestCase
"<input id='foo_post_active_true' name='post[active]' type='radio' value='true' />" +
"<label for='foo_post_active_true'>true</label>" +
"<input checked='checked' id='foo_post_active_false' name='post[active]' type='radio' value='false' />" +
- "<label for='foo_post_active_false'>false</label>"
+ "<label for='foo_post_active_false'>false</label>" +
+ "<input type='hidden' name='post[active][]' value='' />"
end
assert_dom_equal expected, output_buffer
@@ -1681,7 +1685,8 @@ class FormHelperTest < ActionView::TestCase
"<input id='post_1_active_true' name='post[1][active]' type='radio' value='true' />" +
"<label for='post_1_active_true'>true</label>" +
"<input checked='checked' id='post_1_active_false' name='post[1][active]' type='radio' value='false' />" +
- "<label for='post_1_active_false'>false</label>"
+ "<label for='post_1_active_false'>false</label>" +
+ "<input type='hidden' name='post[1][active][]' value='' />"
end
assert_dom_equal expected, output_buffer
@@ -2292,6 +2297,27 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_deep_nested_fields_for
+ @comment.save
+ form_for(:posts) do |f|
+ f.fields_for('post[]', @post) do |f2|
+ f2.text_field(:id)
+ @post.comments.each do |comment|
+ concat f2.fields_for('comment[]', comment) { |c|
+ concat c.text_field(:name)
+ }
+ end
+ end
+ end
+
+ expected = whole_form do
+ "<input name='posts[post][0][comment][1][name]' type='text' id='posts_post_0_comment_1_name' value='comment #1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+
def test_nested_fields_for_with_nested_collections
form_for(@post, as: 'post[]') do |f|
concat f.text_field(:title)
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index 7e1324c011..8db35d02e1 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -450,21 +450,21 @@ class FormTagHelperTest < ActionView::TestCase
ActionView::Base.automatically_disable_submit_tag = true
end
- def test_data_disable_with_string
+ def test_submit_tag_having_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
+ def test_submit_tag_having_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
+ def test_submit_tag_having_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 } })
diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb
index 14dafd7d63..1184cf7da8 100644
--- a/actionview/test/template/lookup_context_test.rb
+++ b/actionview/test/template/lookup_context_test.rb
@@ -27,7 +27,7 @@ class LookupContextTest < ActiveSupport::TestCase
end
test "normalizes details on initialization" do
- assert_equal Mime::SET, @lookup_context.formats
+ assert_equal Mime::SET.to_a, @lookup_context.formats
assert_equal :en, @lookup_context.locale
end
@@ -48,11 +48,11 @@ class LookupContextTest < ActiveSupport::TestCase
test "handles */* formats" do
@lookup_context.formats = ["*/*"]
- assert_equal Mime::SET, @lookup_context.formats
+ assert_equal Mime::SET.to_a, @lookup_context.formats
end
test "handles explicitly defined */* formats fallback to :js" do
- @lookup_context.formats = [:js, Mime::ALL]
+ @lookup_context.formats = [:js, Mime::Type[:ALL]]
assert_equal [:js, *Mime::SET.symbols], @lookup_context.formats
end
diff --git a/actionview/test/template/number_helper_test.rb b/actionview/test/template/number_helper_test.rb
index 631d45586b..ace3e950b8 100644
--- a/actionview/test/template/number_helper_test.rb
+++ b/actionview/test/template/number_helper_test.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
require "abstract_unit"
class NumberHelperTest < ActionView::TestCase
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 9c2c9507b7..00fc28a522 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -627,6 +627,16 @@ class CachedCollectionViewRenderTest < CachedViewRenderTest
@view.render(partial: "test/customer", collection: [customer], cache: ->(item) { [item, 'key'] })
end
+ test "with caching with custom key and rendering with different key" do
+ customer = Customer.new("david")
+ key = cache_key([customer, 'key'], "test/_customer")
+
+ ActionView::PartialRenderer.collection_cache.write(key, 'Hello')
+
+ assert_equal "Hello: david",
+ @view.render(partial: "test/customer", collection: [customer], cache: ->(item) { [item, 'another_key'] })
+ end
+
test "automatic caching with inferred cache name" do
customer = CachedCustomer.new("david")
key = cache_key(customer, "test/_cached_customer")
diff --git a/actionview/test/template/translation_helper_test.rb b/actionview/test/template/translation_helper_test.rb
index 749d0dd7fd..261576bead 100644
--- a/actionview/test/template/translation_helper_test.rb
+++ b/actionview/test/template/translation_helper_test.rb
@@ -60,6 +60,12 @@ class TranslationHelperTest < ActiveSupport::TestCase
assert_equal true, translate(:"translations.missing").html_safe?
end
+ def test_returns_missing_translation_message_with_unescaped_interpolation
+ expected = '<span class="translation_missing" title="translation missing: en.translations.missing, name: Kir, year: 2015, vulnerable: &amp;quot; onclick=&amp;quot;alert()&amp;quot;">Missing</span>'
+ assert_equal expected, translate(:"translations.missing", name: "Kir", year: "2015", vulnerable: %{" onclick="alert()"})
+ assert translate(:"translations.missing").html_safe?
+ end
+
def test_raises_missing_translation_message_with_raise_config_option
ActionView::Base.raise_on_missing_translations = true