aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/test_case.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb2
-rw-r--r--actionpack/test/dispatch/response_test.rb5
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb35
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb14
-rw-r--r--actionview/lib/action_view/helpers/tags/label.rb4
-rw-r--r--actionview/test/template/form_helper/form_with_test.rb410
-rw-r--r--activemodel/test/cases/errors_test.rb1
-rw-r--r--activerecord/CHANGELOG.md52
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb16
-rw-r--r--activerecord/lib/active_record/core.rb4
-rw-r--r--activerecord/lib/active_record/errors.rb4
-rw-r--r--activerecord/lib/active_record/persistence.rb14
-rw-r--r--activerecord/lib/active_record/railtie.rb6
-rw-r--r--activerecord/lib/active_record/railties/databases.rake16
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb8
-rw-r--r--activerecord/lib/active_record/transactions.rb5
-rw-r--r--activerecord/test/assets/schema_dump_5_1.yml345
-rw-r--r--activerecord/test/cases/adapter_test.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql2/reserved_word_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb4
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb14
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb43
-rw-r--r--activerecord/test/cases/finder_test.rb1
-rw-r--r--activerecord/test/cases/query_cache_test.rb14
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb45
-rw-r--r--activesupport/CHANGELOG.md6
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb91
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini.rb12
-rw-r--r--activesupport/test/autoloading_fixtures/prepend.rb8
-rw-r--r--activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb2
-rw-r--r--activesupport/test/caching_test.rb2
-rw-r--r--activesupport/test/constantize_test_cases.rb5
-rw-r--r--activesupport/test/core_ext/object/duplicable_test.rb10
-rw-r--r--guides/source/development_dependencies_install.md4
-rw-r--r--railties/CHANGELOG.md12
-rw-r--r--railties/lib/rails/commands/plugin/plugin_command.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb18
-rw-r--r--railties/lib/rails/generators/named_base.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/yarn9
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb6
-rw-r--r--railties/test/application/rake_test.rb4
51 files changed, 1014 insertions, 305 deletions
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 85f2501d42..c639178776 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -3,6 +3,7 @@ require "active_support/core_ext/hash/conversions"
require "active_support/core_ext/object/to_query"
require "active_support/core_ext/module/anonymous"
require "active_support/core_ext/hash/keys"
+require "active_support/testing/constant_lookup"
require "action_controller/template_assertions"
require "rails-dom-testing"
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 357ca56036..f71c6afd6c 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -408,7 +408,7 @@ module ActionDispatch # :nodoc:
def parse_content_type(content_type)
if content_type
type, charset = content_type.split(/;\s*charset=/)
- type = nil if type.empty?
+ type = nil if type && type.empty?
ContentTypeHeader.new(type, charset)
else
NullContentTypeHeader
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 400af42bac..2df70704a1 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -110,6 +110,11 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal "application/aaron", @response.content_type
end
+ def test_empty_content_type_returns_nil
+ @response.headers['Content-Type'] = ""
+ assert_equal nil, @response.content_type
+ end
+
test "simple output" do
@response.body = "Hello, World!"
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index e7ea267211..6c632b8e75 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -595,6 +595,16 @@ module ActionView
#
# Where <tt>@document = Document.find(params[:id])</tt>.
#
+ # When using labels +form_with+ requires setting the id on the field being
+ # labelled:
+ #
+ # <%= form_with(model: @post) do |form| %>
+ # <%= form.label :title %>
+ # <%= form.text_field :title, id: :post_title %>
+ # <% end %>
+ #
+ # See +label+ for more on how the +for+ attribute is derived.
+ #
# === Mixing with other form helpers
#
# While +form_with+ uses a FormBuilder object it's possible to mix and
@@ -690,6 +700,8 @@ module ActionView
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
# end
def form_with(model: nil, scope: nil, url: nil, format: nil, **options)
+ options[:skip_default_ids] = true
+
if model
url ||= polymorphic_path(model, format: format)
@@ -986,6 +998,16 @@ module ActionView
# or model is yielded, so any generated field names are prefixed with
# either the passed scope or the scope inferred from the <tt>:model</tt>.
#
+ # When using labels +fields+ requires setting the id on the field being
+ # labelled:
+ #
+ # <%= fields :comment do |fields| %>
+ # <%= fields.label :body %>
+ # <%= fields.text_field :body, id: :comment_body %>
+ # <% end %>
+ #
+ # See +label+ for more on how the +for+ attribute is derived.
+ #
# === Mixing with other form helpers
#
# While +form_with+ uses a FormBuilder object it's possible to mix and
@@ -1003,7 +1025,8 @@ module ActionView
# to work with an object as a base, like
# FormOptionHelper#collection_select and DateHelper#datetime_select.
def fields(scope = nil, model: nil, **options, &block)
- # TODO: Remove when ids and classes are no longer output by default.
+ options[:skip_default_ids] = true
+
if model
scope ||= model_name_from_record_or_class(model).param_key
end
@@ -1469,7 +1492,7 @@ module ActionView
private
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: false,
skip_enforcing_utf8: false, **options)
- html_options = options.except(:index, :include_id, :builder).merge(html)
+ html_options = options.except(:index, :include_id, :skip_default_ids, :builder).merge(html)
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
html_options[:enforce_utf8] = !skip_enforcing_utf8
@@ -1603,7 +1626,7 @@ module ActionView
def initialize(object_name, object, template, options)
@nested_child_index = {}
@object_name, @object, @template, @options = object_name, object, template, options
- @default_options = @options ? @options.slice(:index, :namespace) : {}
+ @default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids) : {}
convert_to_legacy_options(@options)
@@ -1910,6 +1933,8 @@ module ActionView
# See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
def fields(scope = nil, model: nil, **options, &block)
+ options[:skip_default_ids] = true
+
convert_to_legacy_options(options)
fields_for(scope || model, model, **options, &block)
@@ -2268,10 +2293,6 @@ module ActionView
if options.key?(:skip_id)
options[:include_id] = !options.delete(:skip_id)
end
-
- if options.key?(:local)
- options[:remote] = !options.delete(:local)
- end
end
end
end
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 7bd473507b..ffc52569f2 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -461,7 +461,7 @@ module ActionView
end
# Creates a button element that defines a <tt>submit</tt> button,
- # <tt>reset</tt>button or a generic button which can be used in
+ # <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 does allow for richer labels such as images and emphasis,
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index cf8a6d6028..b8c446cbed 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -13,6 +13,7 @@ module ActionView
@object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
@object = retrieve_object(options.delete(:object))
+ @skip_default_ids = options.delete(:skip_default_ids)
@options = options
@auto_index = Regexp.last_match ? retrieve_autoindex(Regexp.last_match.pre_match) : nil
end
@@ -81,9 +82,12 @@ module ActionView
def add_default_name_and_id(options)
index = name_and_id_index(options)
options["name"] = options.fetch("name") { tag_name(options["multiple"], index) }
- options["id"] = options.fetch("id") { tag_id(index) }
- if namespace = options.delete("namespace")
- options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
+
+ unless skip_default_ids?
+ options["id"] = options.fetch("id") { tag_id(index) }
+ if namespace = options.delete("namespace")
+ options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
+ end
end
end
@@ -154,6 +158,10 @@ module ActionView
def name_and_id_index(options)
options.key?("index") ? options.delete("index") || "" : @auto_index
end
+
+ def skip_default_ids?
+ @skip_default_ids
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb
index b31d5fda66..cab15ae201 100644
--- a/actionview/lib/action_view/helpers/tags/label.rb
+++ b/actionview/lib/action_view/helpers/tags/label.rb
@@ -73,6 +73,10 @@ module ActionView
def render_component(builder)
builder.translation
end
+
+ def skip_default_ids?
+ false # The id is used as the `for` attribute.
+ end
end
end
end
diff --git a/actionview/test/template/form_helper/form_with_test.rb b/actionview/test/template/form_helper/form_with_test.rb
index c80a2f61b9..c078b47e14 100644
--- a/actionview/test/template/form_helper/form_with_test.rb
+++ b/actionview/test/template/form_helper/form_with_test.rb
@@ -116,6 +116,16 @@ class FormWithActsLikeFormTagTest < FormWithTest
assert_dom_equal expected, output_buffer
end
+
+ def test_form_with_with_block_in_erb_and_local_true
+ output_buffer = render_erb("<%= form_with(url: 'http://www.example.com', local: true) do %>Hello world!<% end %>")
+
+ expected = whole_form("http://www.example.com", local: true) do
+ "Hello world!"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
end
class FormWithActsLikeFormForTest < FormWithTest
@@ -301,10 +311,10 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts/123", "create-post", method: "patch") do
"<label for='post_title'>The Title</label>" +
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />" +
"<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />" +
"<button name='button' type='submit'>Create post</button>" +
"<button name='button' type='submit'><span>Create post</span></button>"
@@ -322,9 +332,9 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts") do
"<input type='hidden' name='post[active]' value='' />" +
- "<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
+ "<input 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' />" +
+ "<input checked='checked' name='post[active]' type='radio' value='false' />" +
"<label for='post_active_false'>false</label>"
end
@@ -345,10 +355,10 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts") do
"<input type='hidden' name='post[active]' value='' />" +
"<label for='post_active_true'>" +
- "<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
+ "<input name='post[active]' type='radio' value='true' />" +
"true</label>" +
"<label for='post_active_false'>" +
- "<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
+ "<input checked='checked' name='post[active]' type='radio' value='false' />" +
"false</label>"
end
@@ -371,12 +381,12 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts") do
"<input type='hidden' name='post[active]' value='' />" +
"<label for='post_active_true'>" +
- "<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
+ "<input name='post[active]' type='radio' value='true' />" +
"true</label>" +
"<label for='post_active_false'>" +
- "<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
+ "<input checked='checked' name='post[active]' type='radio' value='false' />" +
"false</label>" +
- "<input id='post_id' name='post[id]' type='hidden' value='1' />"
+ "<input name='post[id]' type='hidden' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -392,9 +402,9 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts") do
"<input type='hidden' name='post[1][active]' value='' />" +
- "<input id='post_1_active_true' name='post[1][active]' type='radio' value='true' />" +
+ "<input 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' />" +
+ "<input checked='checked' name='post[1][active]' type='radio' value='false' />" +
"<label for='post_1_active_false'>false</label>"
end
@@ -411,11 +421,11 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts") do
"<input name='post[tag_ids][]' type='hidden' value='' />" +
- "<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" +
"<label for='post_tag_ids_1'>Tag 1</label>" +
- "<input id='post_tag_ids_2' name='post[tag_ids][]' type='checkbox' value='2' />" +
+ "<input name='post[tag_ids][]' type='checkbox' value='2' />" +
"<label for='post_tag_ids_2'>Tag 2</label>" +
- "<input checked='checked' id='post_tag_ids_3' name='post[tag_ids][]' type='checkbox' value='3' />" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" +
"<label for='post_tag_ids_3'>Tag 3</label>"
end
@@ -436,13 +446,13 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts") do
"<input name='post[tag_ids][]' type='hidden' value='' />" +
"<label for='post_tag_ids_1'>" +
- "<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" +
"Tag 1</label>" +
"<label for='post_tag_ids_2'>" +
- "<input id='post_tag_ids_2' name='post[tag_ids][]' type='checkbox' value='2' />" +
+ "<input name='post[tag_ids][]' type='checkbox' value='2' />" +
"Tag 2</label>" +
"<label for='post_tag_ids_3'>" +
- "<input checked='checked' id='post_tag_ids_3' name='post[tag_ids][]' type='checkbox' value='3' />" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" +
"Tag 3</label>"
end
@@ -466,15 +476,15 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts") do
"<input name='post[tag_ids][]' type='hidden' value='' />" +
"<label for='post_tag_ids_1'>" +
- "<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" +
"Tag 1</label>" +
"<label for='post_tag_ids_2'>" +
- "<input id='post_tag_ids_2' name='post[tag_ids][]' type='checkbox' value='2' />" +
+ "<input name='post[tag_ids][]' type='checkbox' value='2' />" +
"Tag 2</label>" +
"<label for='post_tag_ids_3'>" +
- "<input checked='checked' id='post_tag_ids_3' name='post[tag_ids][]' type='checkbox' value='3' />" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" +
"Tag 3</label>" +
- "<input id='post_id' name='post[id]' type='hidden' value='1' />"
+ "<input name='post[id]' type='hidden' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -491,7 +501,7 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts") do
"<input name='post[1][tag_ids][]' type='hidden' value='' />" +
- "<input checked='checked' id='post_1_tag_ids_1' name='post[1][tag_ids][]' type='checkbox' value='1' />" +
+ "<input checked='checked' name='post[1][tag_ids][]' type='checkbox' value='1' />" +
"<label for='post_1_tag_ids_1'>Tag 1</label>"
end
@@ -506,7 +516,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", "create-post", method: "patch", multipart: true) do
- "<input name='post[file]' type='file' id='post_file' />"
+ "<input name='post[file]' type='file' />"
end
assert_dom_equal expected, output_buffer
@@ -522,7 +532,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch", multipart: true) do
- "<input name='post[comment][file]' type='file' id='post_comment_file' />"
+ "<input name='post[comment][file]' type='file' />"
end
assert_dom_equal expected, output_buffer
@@ -561,7 +571,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/44", method: "patch") do
- "<input name='post[title]' type='text' id='post_title' value='And his name will be forty and four.' />" +
+ "<input name='post[title]' type='text' value='And his name will be forty and four.' />" +
"<input name='commit' data-disable-with='Edit post' type='submit' value='Edit post' />"
end
@@ -579,10 +589,10 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts/123", "create-post", method: "patch") do
"<label for='other_name_title' class='post_title'>Title</label>" +
- "<input name='other_name[title]' id='other_name_title' value='Hello World' type='text' />" +
- "<textarea name='other_name[body]' id='other_name_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='other_name[title]' value='Hello World' type='text' />" +
+ "<textarea name='other_name[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='other_name[secret]' value='0' type='hidden' />" +
- "<input name='other_name[secret]' checked='checked' id='other_name_secret' value='1' type='checkbox' />" +
+ "<input name='other_name[secret]' checked='checked' value='1' type='checkbox' />" +
"<input name='commit' value='Create post' data-disable-with='Create post' type='submit' />"
end
@@ -609,10 +619,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/", "create-post", method: "delete") do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -626,10 +636,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/", "create-post", method: "delete") do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -643,7 +653,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/search", "search-post", method: "get") do
- "<input name='post[title]' type='search' id='post_title' />"
+ "<input name='post[title]' type='search' />"
end
assert_dom_equal expected, output_buffer
@@ -657,10 +667,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/", "create-post", method: "patch") do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -672,7 +682,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/", skip_enforcing_utf8: true) do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />"
+ "<input name='post[title]' type='text' value='Hello World' />"
end
assert_dom_equal expected, output_buffer
@@ -684,7 +694,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/", skip_enforcing_utf8: false) do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />"
+ "<input name='post[title]' type='text' value='Hello World' />"
end
assert_dom_equal expected, output_buffer
@@ -698,10 +708,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/", "create-post") do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -717,10 +727,10 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts/123", method: "patch") do
"<label for='post_123_title'>Title</label>" +
- "<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" +
- "<textarea name='post[123][body]' id='post_123_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[123][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[123][body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[123][secret]' type='hidden' value='0' />" +
- "<input name='post[123][secret]' checked='checked' type='checkbox' id='post_123_secret' value='1' />"
+ "<input name='post[123][secret]' checked='checked' type='checkbox' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -734,10 +744,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[][title]' type='text' id='post__title' value='Hello World' />" +
- "<textarea name='post[][body]' id='post__body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[][body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[][secret]' type='hidden' value='0' />" +
- "<input name='post[][secret]' checked='checked' type='checkbox' id='post__secret' value='1' />"
+ "<input name='post[][secret]' checked='checked' type='checkbox' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -752,7 +762,7 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts/123", method: "patch") do
"<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
- "<div class='field_with_errors'><input name='post[author_name]' type='text' id='post_author_name' value='' /></div>" +
+ "<div class='field_with_errors'><input name='post[author_name]' type='text' value='' /></div>" +
"<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />"
end
@@ -770,7 +780,7 @@ class FormWithActsLikeFormForTest < FormWithTest
expected = whole_form("/posts/123", method: "patch") do
"<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
- "<div class='field_with_errors'><input name='post[author_name]' type='text' id='post_author_name' value='' /></div>" +
+ "<div class='field_with_errors'><input name='post[author_name]' type='text' value='' /></div>" +
"<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />"
end
@@ -800,10 +810,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", "namespace_edit_post_123", "edit_post", method: "patch") do
- "<input name='post[title]' type='text' id='namespace_post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='namespace_post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='namespace_post_secret' value='1' />"
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -877,7 +887,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[comment][body]' type='text' id='post_comment_body' value='Hello World' />"
+ "<input name='post[comment][body]' type='text' value='Hello World' />"
end
assert_dom_equal expected, output_buffer
@@ -897,7 +907,7 @@ class FormWithActsLikeFormForTest < FormWithTest
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' />"
+ "<input name='posts[post][0][comment][1][name]' type='text' value='comment #1' />"
end
assert_dom_equal expected, output_buffer
@@ -912,8 +922,8 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" +
- "<input name='post[123][comment][][name]' type='text' id='post_123_comment__name' value='new comment' />"
+ "<input name='post[123][title]' type='text' value='Hello World' />" +
+ "<input name='post[123][comment][][name]' type='text' value='new comment' />"
end
assert_dom_equal expected, output_buffer
@@ -928,8 +938,8 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[1][title]' type='text' id='post_1_title' value='Hello World' />" +
- "<input name='post[1][comment][1][name]' type='text' id='post_1_comment_1_name' value='new comment' />"
+ "<input name='post[1][title]' type='text' value='Hello World' />" +
+ "<input name='post[1][comment][1][name]' type='text' value='new comment' />"
end
assert_dom_equal expected, output_buffer
@@ -943,7 +953,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[1][comment][title]' type='text' id='post_1_comment_title' value='Hello World' />"
+ "<input name='post[1][comment][title]' type='text' value='Hello World' />"
end
assert_dom_equal expected, output_buffer
@@ -957,7 +967,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[1][comment][5][title]' type='text' id='post_1_comment_5_title' value='Hello World' />"
+ "<input name='post[1][comment][5][title]' type='text' value='Hello World' />"
end
assert_dom_equal expected, output_buffer
@@ -971,7 +981,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[123][comment][title]' type='text' id='post_123_comment_title' value='Hello World' />"
+ "<input name='post[123][comment][title]' type='text' value='Hello World' />"
end
assert_dom_equal expected, output_buffer
@@ -985,7 +995,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[comment][5][title]' type='radio' id='post_comment_5_title_hello' value='hello' />"
+ "<input name='post[comment][5][title]' type='radio' value='hello' />"
end
assert_dom_equal expected, output_buffer
@@ -999,7 +1009,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[123][comment][123][title]' type='text' id='post_123_comment_123_title' value='Hello World' />"
+ "<input name='post[123][comment][123][title]' type='text' value='Hello World' />"
end
assert_dom_equal expected, output_buffer
@@ -1019,9 +1029,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[123][comment][5][title]' type='text' id='post_123_comment_5_title' value='Hello World' />"
+ "<input name='post[123][comment][5][title]' type='text' value='Hello World' />"
end + whole_form("/posts/123", method: "patch") do
- "<input name='post[1][comment][123][title]' type='text' id='post_1_comment_123_title' value='Hello World' />"
+ "<input name='post[1][comment][123][title]' type='text' value='Hello World' />"
end
assert_dom_equal expected, output_buffer
@@ -1038,8 +1048,8 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="new author" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="new author" />'
end
assert_dom_equal expected, output_buffer
@@ -1065,9 +1075,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
- '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />'
end
assert_dom_equal expected, output_buffer
@@ -1084,9 +1094,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
- '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />'
end
assert_dom_equal expected, output_buffer
@@ -1103,8 +1113,8 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />'
end
assert_dom_equal expected, output_buffer
@@ -1121,8 +1131,8 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />'
end
assert_dom_equal expected, output_buffer
@@ -1139,9 +1149,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
- '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />'
end
assert_dom_equal expected, output_buffer
@@ -1159,9 +1169,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />'
end
assert_dom_equal expected, output_buffer
@@ -1180,11 +1190,11 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
- '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
end
assert_dom_equal expected, output_buffer
@@ -1207,11 +1217,11 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
- '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
end
assert_dom_equal expected, output_buffer
@@ -1234,10 +1244,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
end
assert_dom_equal expected, output_buffer
@@ -1260,11 +1270,11 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
- '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
end
assert_dom_equal expected, output_buffer
@@ -1283,11 +1293,11 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
- '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
end
assert_dom_equal expected, output_buffer
@@ -1307,11 +1317,11 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
end
assert_dom_equal expected, output_buffer
@@ -1330,9 +1340,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="new comment" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="new comment" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="new comment" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="new comment" />'
end
assert_dom_equal expected, output_buffer
@@ -1351,10 +1361,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="new comment" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="new comment" />'
end
assert_dom_equal expected, output_buffer
@@ -1369,7 +1379,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />'
+ '<input name="post[title]" type="text" value="Hello World" />'
end
assert_dom_equal expected, output_buffer
@@ -1386,11 +1396,11 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
- '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
end
assert_dom_equal expected, output_buffer
@@ -1407,11 +1417,11 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
- '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
end
assert_dom_equal expected, output_buffer
@@ -1442,11 +1452,11 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
- '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
end
assert_dom_equal expected, output_buffer
@@ -1465,10 +1475,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
- '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="new comment" />'
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="new comment" />'
end
assert_dom_equal expected, output_buffer
@@ -1485,8 +1495,8 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
- '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
+ '<input name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
end
assert_dom_equal expected, output_buffer
@@ -1502,8 +1512,8 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
- '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
+ '<input name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
end
assert_dom_equal expected, output_buffer
@@ -1525,8 +1535,8 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
- '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
+ '<input name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
end
assert_dom_equal expected, output_buffer
@@ -1611,18 +1621,18 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
- '<input id="post_comments_attributes_0_relevances_attributes_0_value" name="post[comments_attributes][0][relevances_attributes][0][value]" type="text" value="commentrelevance #314" />' +
- '<input id="post_comments_attributes_0_relevances_attributes_0_id" name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
- '<input id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" type="text" value="tag #123" />' +
- '<input id="post_tags_attributes_0_relevances_attributes_0_value" name="post[tags_attributes][0][relevances_attributes][0][value]" type="text" value="tagrelevance #3141" />' +
- '<input id="post_tags_attributes_0_relevances_attributes_0_id" name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' +
- '<input id="post_tags_attributes_0_id" name="post[tags_attributes][0][id]" type="hidden" value="123" />' +
- '<input id="post_tags_attributes_1_value" name="post[tags_attributes][1][value]" type="text" value="tag #456" />' +
- '<input id="post_tags_attributes_1_relevances_attributes_0_value" name="post[tags_attributes][1][relevances_attributes][0][value]" type="text" value="tagrelevance #31415" />' +
- '<input id="post_tags_attributes_1_relevances_attributes_0_id" name="post[tags_attributes][1][relevances_attributes][0][id]" type="hidden" value="31415" />' +
- '<input id="post_tags_attributes_1_id" name="post[tags_attributes][1][id]" type="hidden" value="456" />'
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][0][relevances_attributes][0][value]" type="text" value="commentrelevance #314" />' +
+ '<input name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
+ '<input name="post[tags_attributes][0][value]" type="text" value="tag #123" />' +
+ '<input name="post[tags_attributes][0][relevances_attributes][0][value]" type="text" value="tagrelevance #3141" />' +
+ '<input name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' +
+ '<input name="post[tags_attributes][0][id]" type="hidden" value="123" />' +
+ '<input name="post[tags_attributes][1][value]" type="text" value="tag #456" />' +
+ '<input name="post[tags_attributes][1][relevances_attributes][0][value]" type="text" value="tagrelevance #31415" />' +
+ '<input name="post[tags_attributes][1][relevances_attributes][0][id]" type="hidden" value="31415" />' +
+ '<input name="post[tags_attributes][1][id]" type="hidden" value="456" />'
end
assert_dom_equal expected, output_buffer
@@ -1638,7 +1648,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="hash backed author" />'
+ '<input name="post[author_attributes][name]" type="text" value="hash backed author" />'
end
assert_dom_equal expected, output_buffer
@@ -1652,10 +1662,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected =
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
assert_dom_equal expected, output_buffer
end
@@ -1668,10 +1678,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected =
- "<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" +
- "<textarea name='post[123][body]' id='post_123_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[123][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[123][body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[123][secret]' type='hidden' value='0' />" +
- "<input name='post[123][secret]' checked='checked' type='checkbox' id='post_123_secret' value='1' />"
+ "<input name='post[123][secret]' checked='checked' type='checkbox' value='1' />"
assert_dom_equal expected, output_buffer
end
@@ -1684,10 +1694,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected =
- "<input name='post[][title]' type='text' id='post__title' value='Hello World' />" +
- "<textarea name='post[][body]' id='post__body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[][body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[][secret]' type='hidden' value='0' />" +
- "<input name='post[][secret]' checked='checked' type='checkbox' id='post__secret' value='1' />"
+ "<input name='post[][secret]' checked='checked' type='checkbox' value='1' />"
assert_dom_equal expected, output_buffer
end
@@ -1700,10 +1710,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected =
- "<input name='post[abc][title]' type='text' id='post_abc_title' value='Hello World' />" +
- "<textarea name='post[abc][body]' id='post_abc_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[abc][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[abc][body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[abc][secret]' type='hidden' value='0' />" +
- "<input name='post[abc][secret]' checked='checked' type='checkbox' id='post_abc_secret' value='1' />"
+ "<input name='post[abc][secret]' checked='checked' type='checkbox' value='1' />"
assert_dom_equal expected, output_buffer
end
@@ -1716,10 +1726,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected =
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
assert_dom_equal expected, output_buffer
end
@@ -1732,10 +1742,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected =
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
assert_dom_equal expected, output_buffer
end
@@ -1747,7 +1757,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
assert_dom_equal "<label for=\"author_post_title\">Title</label>" +
- "<input name='author[post][title]' type='text' id='author_post_title' value='Hello World' />",
+ "<input name='author[post][title]' type='text' value='Hello World' />",
output_buffer
end
@@ -1758,7 +1768,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
assert_dom_equal "<label for=\"author_post_1_title\">Title</label>" +
- "<input name='author[post][1][title]' type='text' id='author_post_1_title' value='Hello World' />",
+ "<input name='author[post][1][title]' type='text' value='Hello World' />",
output_buffer
end
@@ -1777,10 +1787,10 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", "create-post", method: "patch") do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
"<input name='parent_post[secret]' type='hidden' value='0' />" +
- "<input name='parent_post[secret]' checked='checked' type='checkbox' id='parent_post_secret' value='1' />"
+ "<input name='parent_post[secret]' checked='checked' type='checkbox' value='1' />"
end
assert_dom_equal expected, output_buffer
@@ -1797,9 +1807,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", "create-post", method: "patch") do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
- "<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' />"
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[comment][name]' type='text' value='new comment' />"
end
assert_dom_equal expected, output_buffer
@@ -1813,7 +1823,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<input name='post[category][name]' type='text' id='post_category_name' />"
+ "<input name='post[category][name]' type='text' />"
end
assert_dom_equal expected, output_buffer
@@ -1837,9 +1847,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" +
- "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea><br/>" +
- "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>"
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>" +
+ "<label for='body'>Body:</label> <textarea name='post[body]'>\nBack to the hill and over it again!</textarea><br/>" +
+ "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' value='1' /><br/>"
end
assert_dom_equal expected, output_buffer
@@ -1856,9 +1866,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" +
- "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea><br/>" +
- "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>"
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>" +
+ "<label for='body'>Body:</label> <textarea name='post[body]'>\nBack to the hill and over it again!</textarea><br/>" +
+ "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' value='1' /><br/>"
end
assert_dom_equal expected, output_buffer
@@ -1875,7 +1885,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected = whole_form("/posts/123", method: "patch") do
- "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>"
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>"
end
assert_dom_equal expected, output_buffer
@@ -1890,7 +1900,7 @@ class FormWithActsLikeFormForTest < FormWithTest
concat f.text_field(:title)
end
- expected = "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>"
+ expected = "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>"
assert_dom_equal expected, output_buffer
end
@@ -1902,7 +1912,7 @@ class FormWithActsLikeFormForTest < FormWithTest
concat f.text_field(:title)
end
- expected = "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>"
+ expected = "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>"
assert_dom_equal expected, output_buffer
end
@@ -1915,9 +1925,9 @@ class FormWithActsLikeFormForTest < FormWithTest
end
expected =
- "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" +
- "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea><br/>" +
- "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>"
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>" +
+ "<label for='body'>Body:</label> <textarea name='post[body]'>\nBack to the hill and over it again!</textarea><br/>" +
+ "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' value='1' /><br/>"
assert_dom_equal expected, output_buffer
end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index e868d20fc8..e6ba06301d 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "yaml"
class ErrorsTest < ActiveModel::TestCase
class Person
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8e2c039575..eac54e7566 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,4 +1,42 @@
-* Moved DecimalWithoutScale, Text, and UnsignedInteger from Active Model to Active Record
+* Notifications see frozen SQL string.
+
+ Fixes #23774
+
+ *Richard Monette*
+
+* RuntimeErrors are no longer translated to ActiveRecord::StatementInvalid.
+
+ *Richard Monette*
+
+* Change the schema cache format to use YAML instead of Marshal.
+
+ *Kir Shatrov*
+
+* Support index length and order options using both string and symbol
+ column names.
+
+ Fixes #27243.
+
+ *Ryuta Kamizono*
+
+* Raise `ActiveRecord::RangeError` when values that executed are out of range.
+
+ *Ryuta Kamizono*
+
+* Raise `ActiveRecord::NotNullViolation` when a record cannot be inserted
+ or updated because it would violate a not null constraint.
+
+ *Ryuta Kamizono*
+
+* Emulate db trigger behaviour for after_commit :destroy, :update.
+
+ Race conditions can occur when an ActiveRecord is destroyed
+ twice or destroyed and updated. The callbacks should only be
+ triggered once, similar to a SQL database trigger.
+
+ *Stefan Budeanu*
+
+* Moved `DecimalWithoutScale`, `Text`, and `UnsignedInteger` from Active Model to Active Record.
*Iain Beeston*
@@ -14,7 +52,7 @@
*Prathamesh Sonpatki*
-* PostgreSQL & MySQL: Use big integer as primary key type for new tables
+* PostgreSQL & MySQL: Use big integer as primary key type for new tables.
*Jon McCartie*, *Pavel Pravosud*
@@ -34,7 +72,7 @@
*Sergey Alekseev*
-* Raise ActiveRecord::RecordNotFound from collection `*_ids` setters
+* Raise `ActiveRecord::RecordNotFound` from collection `*_ids` setters
for unknown IDs with a better error message.
Changes the collection `*_ids` setters to cast provided IDs the data
@@ -121,7 +159,7 @@
* Fixed: Optimistic locking does not work well with `null` in the database.
- Fixes #26024
+ Fixes #26024.
*bogdanvlviv*
@@ -130,7 +168,7 @@
*Edho Arief*
-* Serialize JSON attribute value `nil` as SQL `NULL`, not JSON `null`
+* Serialize JSON attribute value `nil` as SQL `NULL`, not JSON `null`.
*Trung Duc Tran*
@@ -234,7 +272,7 @@
successfully rolled back when the column was given and invalid column
type.
- Fixes #26087
+ Fixes #26087.
*Travis O'Neill*
@@ -264,7 +302,7 @@
*Takeshi Akima*
* Virtual attributes will no longer raise when read on models loaded from the
- database
+ database.
*Sean Griffin*
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index 1953cc6a72..ee7b7c8bea 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -58,6 +58,8 @@ module ActiveRecord
sc.execute(binds, klass, conn) do |record|
set_inverse_instance record
end.first
+ rescue ::RangeError
+ nil
end
def replace(record)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index faccd1d641..947796eea0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -10,9 +10,9 @@ module ActiveRecord
def to_sql(arel, binds = [])
if arel.respond_to?(:ast)
collected = visitor.accept(arel.ast, collector)
- collected.compile(binds, self)
+ collected.compile(binds, self).freeze
else
- arel
+ arel.dup.freeze
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 237367c8b3..284529b46e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -598,7 +598,12 @@ module ActiveRecord
def translate_exception(exception, message)
# override in derived class
- ActiveRecord::StatementInvalid.new(message)
+ case exception
+ when RuntimeError
+ exception
+ else
+ ActiveRecord::StatementInvalid.new(message)
+ end
end
def without_prepared_statement?(binds)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index e4b3e296f5..6985d2c1b2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -738,6 +738,7 @@ module ActiveRecord
ER_DO_NOT_HAVE_DEFAULT = 1364
ER_NO_REFERENCED_ROW_2 = 1452
ER_DATA_TOO_LONG = 1406
+ ER_OUT_OF_RANGE = 1264
ER_LOCK_DEADLOCK = 1213
ER_CANNOT_ADD_FOREIGN = 1215
ER_CANNOT_CREATE_TABLE = 1005
@@ -758,6 +759,8 @@ module ActiveRecord
end
when ER_DATA_TOO_LONG
ValueTooLong.new(message)
+ when ER_OUT_OF_RANGE
+ RangeError.new(message)
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
NotNullViolation.new(message)
when ER_LOCK_DEADLOCK
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 02d546209d..61cd7ae4cc 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -40,6 +40,28 @@ module ActiveRecord
Base.human_attribute_name(@name)
end
+ def init_with(coder)
+ @name = coder["name"]
+ @table_name = coder["table_name"]
+ @sql_type_metadata = coder["sql_type_metadata"]
+ @null = coder["null"]
+ @default = coder["default"]
+ @default_function = coder["default_function"]
+ @collation = coder["collation"]
+ @comment = coder["comment"]
+ end
+
+ def encode_with(coder)
+ coder["name"] = @name
+ coder["table_name"] = @table_name
+ coder["sql_type_metadata"] = @sql_type_metadata
+ coder["null"] = @null
+ coder["default"] = @default
+ coder["default_function"] = @default_function
+ coder["collation"] = @collation
+ coder["comment"] = @comment
+ end
+
def ==(other)
other.is_a?(Column) &&
attributes_for_hash == other.attributes_for_hash
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 5262141995..33cdcf9a76 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -408,6 +408,7 @@ module ActiveRecord
# See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
VALUE_LIMIT_VIOLATION = "22001"
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
NOT_NULL_VIOLATION = "23502"
FOREIGN_KEY_VIOLATION = "23503"
UNIQUE_VIOLATION = "23505"
@@ -424,6 +425,8 @@ module ActiveRecord
InvalidForeignKey.new(message)
when VALUE_LIMIT_VIOLATION
ValueTooLong.new(message)
+ when NUMERIC_VALUE_OUT_OF_RANGE
+ RangeError.new(message)
when NOT_NULL_VIOLATION
NotNullViolation.new(message)
when SERIALIZATION_FAILURE
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index 8219f132c3..3a319c4029 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -21,6 +21,22 @@ module ActiveRecord
@data_sources = @data_sources.dup
end
+ def encode_with(coder)
+ coder["columns"] = @columns
+ coder["columns_hash"] = @columns_hash
+ coder["primary_keys"] = @primary_keys
+ coder["data_sources"] = @data_sources
+ coder["version"] = ActiveRecord::Migrator.current_version
+ end
+
+ def init_with(coder)
+ @columns = coder["columns"]
+ @columns_hash = coder["columns_hash"]
+ @primary_keys = coder["primary_keys"]
+ @data_sources = coder["data_sources"]
+ @version = coder["version"]
+ end
+
def primary_keys(table_name)
@primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 1fbe374ade..878d87638d 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -194,7 +194,7 @@ module ActiveRecord
name, primary_key, id)
end
record
- rescue RangeError
+ rescue ::RangeError
raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
name, primary_key)
end
@@ -223,7 +223,7 @@ module ActiveRecord
statement.execute(hash.values, self, connection).first
rescue TypeError
raise ActiveRecord::StatementInvalid
- rescue RangeError
+ rescue ::RangeError
nil
end
end
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 507615a222..c812a05101 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -159,6 +159,10 @@ module ActiveRecord
class ValueTooLong < StatementInvalid
end
+ # Raised when values that executed are out of range.
+ class RangeError < StatementInvalid
+ end
+
# Raised when number of bind variables in statement given to +:condition+ key
# (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
# does not match number of expected values supplied.
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 8e13ee3564..60d8e95b21 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -181,7 +181,11 @@ module ActiveRecord
_raise_readonly_record_error if readonly?
destroy_associations
self.class.connection.add_transaction_record(self)
- destroy_row if persisted?
+ @_trigger_destroy_callback = if persisted?
+ destroy_row > 0
+ else
+ true
+ end
@destroyed = true
freeze
end
@@ -519,6 +523,7 @@ module ActiveRecord
raise ActiveRecord::StaleObjectError.new(self, "touch")
end
+ @_trigger_update_callback = result
result
else
true
@@ -550,10 +555,13 @@ module ActiveRecord
def _update_record(attribute_names = self.attribute_names)
attributes_values = arel_attributes_with_values_for_update(attribute_names)
if attributes_values.empty?
- 0
+ rows_affected = 0
+ @_trigger_update_callback = true
else
- self.class.unscoped._update_record attributes_values, id, id_in_database
+ rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database
+ @_trigger_update_callback = rows_affected > 0
end
+ rows_affected
end
# Creates a record with values matching those of the instance attributes
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 7ce10df6d4..2701c5bca9 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -82,15 +82,15 @@ module ActiveRecord
if config.active_record.delete(:use_schema_cache_dump)
config.after_initialize do |app|
ActiveSupport.on_load(:active_record) do
- filename = File.join(app.config.paths["db"].first, "schema_cache.dump")
+ filename = File.join(app.config.paths["db"].first, "schema_cache.yml")
if File.file?(filename)
- cache = Marshal.load File.binread filename
+ cache = YAML.load(File.read(filename))
if cache.version == ActiveRecord::Migrator.current_version
self.connection.schema_cache = cache
self.connection_pool.schema_cache = cache.dup
else
- warn "Ignoring db/schema_cache.dump because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
+ warn "Ignoring db/schema_cache.yml because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 46235ab922..25d79a6c7d 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -265,19 +265,19 @@ db_namespace = namespace :db do
end
namespace :cache do
- desc "Creates a db/schema_cache.dump file."
+ desc "Creates a db/schema_cache.yml file."
task dump: [:environment, :load_config] do
- con = ActiveRecord::Base.connection
- filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
+ conn = ActiveRecord::Base.connection
+ filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml")
- con.schema_cache.clear!
- con.data_sources.each { |table| con.schema_cache.add(table) }
- open(filename, "wb") { |f| f.write(Marshal.dump(con.schema_cache)) }
+ conn.schema_cache.clear!
+ conn.data_sources.each { |table| conn.schema_cache.add(table) }
+ open(filename, "wb") { |f| f.write(YAML.dump(conn.schema_cache)) }
end
- desc "Clears a db/schema_cache.dump file."
+ desc "Clears a db/schema_cache.yml file."
task clear: [:environment, :load_config] do
- filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
+ filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml")
rm_f filename, verbose: false
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 93c8722aa3..5e456452e9 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -76,7 +76,7 @@ module ActiveRecord
# Post.find_by "published_at < ?", 2.weeks.ago
def find_by(arg, *args)
where(arg, *args).take
- rescue RangeError
+ rescue ::RangeError
nil
end
@@ -84,7 +84,7 @@ module ActiveRecord
# an ActiveRecord::RecordNotFound error.
def find_by!(arg, *args)
where(arg, *args).take!
- rescue RangeError
+ rescue ::RangeError
raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
@klass.name)
end
@@ -333,7 +333,7 @@ module ActiveRecord
end
connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
- rescue RangeError
+ rescue ::RangeError
false
end
@@ -458,7 +458,7 @@ module ActiveRecord
else
find_some(ids)
end
- rescue RangeError
+ rescue ::RangeError
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index af3fc88282..ce939c8b97 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -461,9 +461,10 @@ module ActiveRecord
when :create
transaction_record_state(:new_record)
when :destroy
- destroyed?
+ defined?(@_trigger_destroy_callback) && @_trigger_destroy_callback
when :update
- !(transaction_record_state(:new_record) || destroyed?)
+ !(transaction_record_state(:new_record) || destroyed?) &&
+ (defined?(@_trigger_update_callback) && @_trigger_update_callback)
end
end
end
diff --git a/activerecord/test/assets/schema_dump_5_1.yml b/activerecord/test/assets/schema_dump_5_1.yml
new file mode 100644
index 0000000000..f37977daf2
--- /dev/null
+++ b/activerecord/test/assets/schema_dump_5_1.yml
@@ -0,0 +1,345 @@
+--- !ruby/object:ActiveRecord::ConnectionAdapters::SchemaCache
+columns:
+ posts:
+ - &1 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: id
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: INTEGER
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &2 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: author_id
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default:
+ default_function:
+ collation:
+ comment:
+ - &3 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: title
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: varchar
+ type: :string
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &4 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: body
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: text
+ type: :text
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &5 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: type
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: varchar
+ type: :string
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default:
+ default_function:
+ collation:
+ comment:
+ - &6 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: comments_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &7 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: taggings_with_delete_all_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &8 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: taggings_with_destroy_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &9 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &10 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_with_destroy_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &11 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_with_nullify_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+columns_hash:
+ posts:
+ id: *1
+ author_id: *2
+ title: *3
+ body: *4
+ type: *5
+ comments_count: *6
+ taggings_with_delete_all_count: *7
+ taggings_with_destroy_count: *8
+ tags_count: *9
+ tags_with_destroy_count: *10
+ tags_with_nullify_count: *11
+primary_keys:
+ posts: id
+data_sources:
+ ar_internal_metadata: true
+ table_with_autoincrement: true
+ accounts: true
+ admin_accounts: true
+ admin_users: true
+ aircraft: true
+ articles: true
+ articles_magazines: true
+ articles_tags: true
+ audit_logs: true
+ authors: true
+ author_addresses: true
+ author_favorites: true
+ auto_id_tests: true
+ binaries: true
+ birds: true
+ books: true
+ booleans: true
+ bulbs: true
+ CamelCase: true
+ cars: true
+ carriers: true
+ categories: true
+ categories_posts: true
+ categorizations: true
+ citations: true
+ clubs: true
+ collections: true
+ colnametests: true
+ columns: true
+ comments: true
+ companies: true
+ content: true
+ content_positions: true
+ vegetables: true
+ computers: true
+ computers_developers: true
+ contracts: true
+ customers: true
+ customer_carriers: true
+ dashboards: true
+ developers: true
+ developers_projects: true
+ dog_lovers: true
+ dogs: true
+ doubloons: true
+ edges: true
+ engines: true
+ entrants: true
+ essays: true
+ events: true
+ eyes: true
+ funny_jokes: true
+ cold_jokes: true
+ friendships: true
+ goofy_string_id: true
+ having: true
+ guids: true
+ guitars: true
+ inept_wizards: true
+ integer_limits: true
+ invoices: true
+ iris: true
+ items: true
+ jobs: true
+ jobs_pool: true
+ keyboards: true
+ legacy_things: true
+ lessons: true
+ lessons_students: true
+ students: true
+ lint_models: true
+ line_items: true
+ lions: true
+ lock_without_defaults: true
+ lock_without_defaults_cust: true
+ magazines: true
+ mateys: true
+ members: true
+ member_details: true
+ member_friends: true
+ memberships: true
+ member_types: true
+ mentors: true
+ minivans: true
+ minimalistics: true
+ mixed_case_monkeys: true
+ mixins: true
+ movies: true
+ notifications: true
+ numeric_data: true
+ orders: true
+ organizations: true
+ owners: true
+ paint_colors: true
+ paint_textures: true
+ parrots: true
+ parrots_pirates: true
+ parrots_treasures: true
+ people: true
+ peoples_treasures: true
+ personal_legacy_things: true
+ pets: true
+ pets_treasures: true
+ pirates: true
+ posts: true
+ serialized_posts: true
+ images: true
+ price_estimates: true
+ products: true
+ product_types: true
+ projects: true
+ randomly_named_table1: true
+ randomly_named_table2: true
+ randomly_named_table3: true
+ ratings: true
+ readers: true
+ references: true
+ shape_expressions: true
+ ships: true
+ ship_parts: true
+ prisoners: true
+ shop_accounts: true
+ speedometers: true
+ sponsors: true
+ string_key_objects: true
+ subscribers: true
+ subscriptions: true
+ tags: true
+ taggings: true
+ tasks: true
+ topics: true
+ toys: true
+ traffic_lights: true
+ treasures: true
+ tuning_pegs: true
+ tyres: true
+ variants: true
+ vertices: true
+ warehouse-things: true
+ circles: true
+ squares: true
+ triangles: true
+ non_poly_ones: true
+ non_poly_twos: true
+ men: true
+ faces: true
+ interests: true
+ zines: true
+ wheels: true
+ countries: true
+ treaties: true
+ countries_treaties: true
+ liquid: true
+ molecules: true
+ electrons: true
+ weirds: true
+ nodes: true
+ trees: true
+ hotels: true
+ departments: true
+ cake_designers: true
+ drink_designers: true
+ chefs: true
+ recipes: true
+ records: true
+ overloaded_types: true
+ users: true
+ test_with_keyword_column_name: true
+ fk_test_has_pk: true
+ fk_test_has_fk: true
+version: 0
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 2f0e8d866d..3fce0a1df1 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -226,6 +226,14 @@ module ActiveRecord
assert_not_nil error.cause
end
+
+ def test_numeric_value_out_of_ranges_are_translated_to_specific_exception
+ error = assert_raises(ActiveRecord::RangeError) do
+ Book.connection.create("INSERT INTO books(author_id) VALUES (2147483648)")
+ end
+
+ assert_not_nil error.cause
+ end
end
def test_disable_referential_integrity
@@ -277,13 +285,13 @@ module ActiveRecord
unless current_adapter?(:PostgreSQLAdapter)
def test_log_invalid_encoding
- error = assert_raise ActiveRecord::StatementInvalid do
+ error = assert_raises RuntimeError do
@connection.send :log, "SELECT 'ы' FROM DUAL" do
raise "ы".force_encoding(Encoding::ASCII_8BIT)
end
end
- assert_not_nil error.cause
+ assert_not_nil error.message
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
index b400f4d2f9..2c778b1150 100644
--- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
@@ -143,7 +143,7 @@ class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
end
# custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
- def create_tables_directly (tables, connection = @connection)
+ def create_tables_directly(tables, connection = @connection)
tables.each do |table_name, column_properties|
connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
end
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index 59475ad177..268800d538 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -34,10 +34,10 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
assert_raise(ActiveModel::RangeError) do
UnsignedType.create(unsigned_bigint: -10)
end
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raise(ActiveRecord::RangeError) do
UnsignedType.create(unsigned_float: -10.0)
end
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raise(ActiveRecord::RangeError) do
UnsignedType.create(unsigned_decimal: -10.0)
end
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 72f1b3b125..81a2a161f2 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1072,6 +1072,20 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, parent.reload.children_count
end
+ def test_belongs_to_with_out_of_range_value_assigning
+ model = Class.new(Comment) do
+ def self.name; "Temp"; end
+ validates :post, presence: true
+ end
+
+ comment = model.new
+ comment.post_id = 9223372036854775808 # out of range in the bigint
+
+ assert_nil comment.post
+ assert_not comment.valid?
+ assert_equal [{ error: :blank }], comment.errors.details[:post]
+ end
+
def test_polymorphic_with_custom_primary_key
toy = Toy.create!
sponsor = Sponsor.create!(sponsorable: toy)
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
index d4459603af..1b4f80fc67 100644
--- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -12,6 +12,33 @@ module ActiveRecord
assert_equal "id", @cache.primary_keys("posts")
end
+ def test_yaml_dump_and_load
+ @cache.columns("posts")
+ @cache.columns_hash("posts")
+ @cache.data_sources("posts")
+ @cache.primary_keys("posts")
+
+ new_cache = YAML.load(YAML.dump(@cache))
+ assert_no_queries do
+ assert_equal 11, new_cache.columns("posts").size
+ assert_equal 11, new_cache.columns_hash("posts").size
+ assert new_cache.data_sources("posts")
+ assert_equal "id", new_cache.primary_keys("posts")
+ end
+ end
+
+ def test_yaml_loads_5_1_dump
+ body = File.open(schema_dump_path).read
+ cache = YAML.load(body)
+
+ assert_no_queries do
+ assert_equal 11, cache.columns("posts").size
+ assert_equal 11, cache.columns_hash("posts").size
+ assert cache.data_sources("posts")
+ assert_equal "id", cache.primary_keys("posts")
+ end
+ end
+
def test_primary_key_for_non_existent_table
assert_nil @cache.primary_keys("omgponies")
end
@@ -45,10 +72,12 @@ module ActiveRecord
@cache = Marshal.load(Marshal.dump(@cache))
- assert_equal 11, @cache.columns("posts").size
- assert_equal 11, @cache.columns_hash("posts").size
- assert @cache.data_sources("posts")
- assert_equal "id", @cache.primary_keys("posts")
+ assert_no_queries do
+ assert_equal 11, @cache.columns("posts").size
+ assert_equal 11, @cache.columns_hash("posts").size
+ assert @cache.data_sources("posts")
+ assert_equal "id", @cache.primary_keys("posts")
+ end
end
def test_table_methods_deprecation
@@ -56,6 +85,12 @@ module ActiveRecord
assert_deprecated { assert @cache.tables("posts") }
assert_deprecated { @cache.clear_table_cache!("posts") }
end
+
+ private
+
+ def schema_dump_path
+ "test/assets/schema_dump_5_1.yml"
+ end
end
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 7eaf31aa24..f8724b0993 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -983,7 +983,6 @@ class FinderTest < ActiveRecord::TestCase
assert_equal devs[2], Developer.offset(2).first
assert_equal devs[-3], Developer.offset(2).last
- assert_equal devs[-3], Developer.offset(2).last
assert_equal devs[-3], Developer.offset(2).order("id DESC").first
end
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 90054ce83d..4a49bfe9b1 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -202,6 +202,20 @@ class QueryCacheTest < ActiveRecord::TestCase
ActiveSupport::Notifications.unsubscribe subscriber
end
+ def test_query_cache_does_not_allow_sql_key_mutation
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |_, _, _, _, payload|
+ payload[:sql].downcase!
+ end
+
+ assert_raises RuntimeError do
+ ActiveRecord::Base.cache do
+ assert_queries(1) { Task.find(1); Task.find(1) }
+ end
+ end
+ ensure
+ ActiveSupport::Notifications.unsubscribe subscriber
+ end
+
def test_cache_is_flat
Task.cache do
assert_queries(1) { Topic.find(1); Topic.find(1); }
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index dba100f5c9..391bbe8877 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -449,6 +449,51 @@ class CallbacksOnMultipleActionsTest < ActiveRecord::TestCase
end
end
+class CallbacksOnDestroyUpdateActionRaceTest < ActiveRecord::TestCase
+ class TopicWithHistory < ActiveRecord::Base
+ self.table_name = :topics
+
+ def self.clear_history
+ @@history = []
+ end
+
+ def self.history
+ @@history ||= []
+ end
+ end
+
+ class TopicWithCallbacksOnDestroy < TopicWithHistory
+ after_commit(on: :destroy) { |record| record.class.history << :destroy }
+ end
+
+ class TopicWithCallbacksOnUpdate < TopicWithHistory
+ after_commit(on: :update) { |record| record.class.history << :update }
+ end
+
+ def test_trigger_once_on_multiple_deletions
+ TopicWithCallbacksOnDestroy.clear_history
+ topic = TopicWithCallbacksOnDestroy.new
+ topic.save
+ topic_clone = TopicWithCallbacksOnDestroy.find(topic.id)
+ topic.destroy
+ topic_clone.destroy
+
+ assert_equal [:destroy], TopicWithCallbacksOnDestroy.history
+ end
+
+ def test_trigger_on_update_where_row_was_deleted
+ TopicWithCallbacksOnUpdate.clear_history
+ topic = TopicWithCallbacksOnUpdate.new
+ topic.save
+ topic_clone = TopicWithCallbacksOnUpdate.find(topic.id)
+ topic.destroy
+ topic_clone.author_name = "Test Author"
+ topic_clone.save
+
+ assert_equal [], TopicWithCallbacksOnUpdate.history
+ end
+end
+
class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
class TopicWithoutTransactionalEnrollmentCallbacks < ActiveRecord::Base
self.table_name = :topics
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 10095ee1bd..2a5c8deb11 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Change return value of `NilClass#duplicable?`, `FalseClass#duplicable?`,
+ `TrueClass#duplicable?`, `Symbol#duplicable?` and `Numeric#duplicable?`
+ to true with Ruby 2.4+. These classes can dup with Ruby 2.4+.
+
+ *Yuji Yaginuma*
+
* Remove deprecated class `ActiveSupport::Concurrency::Latch`
*Andrew White*
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index aa2282cb7e..ebe31b38ca 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -1,7 +1,7 @@
#--
-# Most objects are cloneable, but not all. For example you can't dup +nil+:
+# Most objects are cloneable, but not all. For example you can't dup methods:
#
-# nil.dup # => TypeError: can't dup NilClass
+# method(:puts).dup # => TypeError: allocator undefined for Method
#
# Classes may signal their instances are not duplicable removing +dup+/+clone+
# or raising exceptions from them. So, to dup an arbitrary object you normally
@@ -19,7 +19,7 @@
class Object
# Can you safely dup this object?
#
- # False for +nil+, +false+, +true+, symbol, number, method objects;
+ # False for method objects;
# true otherwise.
def duplicable?
true
@@ -27,52 +27,77 @@ class Object
end
class NilClass
- # +nil+ is not duplicable:
- #
- # nil.duplicable? # => false
- # nil.dup # => TypeError: can't dup NilClass
- def duplicable?
- false
+ begin
+ nil.dup
+ rescue TypeError
+
+ # +nil+ is not duplicable:
+ #
+ # nil.duplicable? # => false
+ # nil.dup # => TypeError: can't dup NilClass
+ def duplicable?
+ false
+ end
end
end
class FalseClass
- # +false+ is not duplicable:
- #
- # false.duplicable? # => false
- # false.dup # => TypeError: can't dup FalseClass
- def duplicable?
- false
+ begin
+ false.dup
+ rescue TypeError
+
+ # +false+ is not duplicable:
+ #
+ # false.duplicable? # => false
+ # false.dup # => TypeError: can't dup FalseClass
+ def duplicable?
+ false
+ end
end
end
class TrueClass
- # +true+ is not duplicable:
- #
- # true.duplicable? # => false
- # true.dup # => TypeError: can't dup TrueClass
- def duplicable?
- false
+ begin
+ true.dup
+ rescue TypeError
+
+ # +true+ is not duplicable:
+ #
+ # true.duplicable? # => false
+ # true.dup # => TypeError: can't dup TrueClass
+ def duplicable?
+ false
+ end
end
end
class Symbol
- # Symbols are not duplicable:
- #
- # :my_symbol.duplicable? # => false
- # :my_symbol.dup # => TypeError: can't dup Symbol
- def duplicable?
- false
+ begin
+ :symbol.dup
+ rescue TypeError
+
+ # Symbols are not duplicable:
+ #
+ # :my_symbol.duplicable? # => false
+ # :my_symbol.dup # => TypeError: can't dup Symbol
+ def duplicable?
+ false
+ end
end
end
class Numeric
- # Numbers are not duplicable:
- #
- # 3.duplicable? # => false
- # 3.dup # => TypeError: can't dup Integer
- def duplicable?
- false
+ begin
+ 1.dup
+ rescue TypeError
+
+ # Numbers are not duplicable:
+ #
+ # 3.duplicable? # => false
+ # 3.dup # => TypeError: can't dup Integer
+ def duplicable?
+ false
+ end
end
end
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index ef3df1240d..fa063af3f4 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -274,7 +274,7 @@ module ActiveSupport
# Go down the ancestors to check if it is owned directly. The check
# stops when we reach Object or the end of ancestors tree.
- constant = constant.ancestors.inject do |const, ancestor|
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
break const if ancestor == Object
break ancestor if ancestor.const_defined?(name, false)
const
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index e16581d697..e3203ef076 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -68,7 +68,17 @@ module ActiveSupport
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
"integer" => Proc.new { |integer| integer.to_i },
"float" => Proc.new { |float| float.to_f },
- "decimal" => Proc.new { |number| BigDecimal(number) },
+ "decimal" => Proc.new do |number|
+ if String === number
+ begin
+ BigDecimal(number)
+ rescue ArgumentError
+ BigDecimal('0')
+ end
+ else
+ BigDecimal(number)
+ end
+ end,
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.to_s.strip) },
"string" => Proc.new { |string| string.to_s },
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
diff --git a/activesupport/test/autoloading_fixtures/prepend.rb b/activesupport/test/autoloading_fixtures/prepend.rb
new file mode 100644
index 0000000000..3134d1df2b
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/prepend.rb
@@ -0,0 +1,8 @@
+class SubClassConflict
+end
+
+class Prepend
+ module PrependedModule
+ end
+ prepend PrependedModule
+end
diff --git a/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb b/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb
new file mode 100644
index 0000000000..090dda3043
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb
@@ -0,0 +1,2 @@
+class Prepend::SubClassConflict
+end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 551235e9e8..73b5a7d2a8 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -983,7 +983,7 @@ class MemoryStoreTest < ActiveSupport::TestCase
end
def test_pruning_is_capped_at_a_max_time
- def @cache.delete_entry (*args)
+ def @cache.delete_entry(*args)
sleep(0.01)
super
end
diff --git a/activesupport/test/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb
index af2db8c991..32b720bcbb 100644
--- a/activesupport/test/constantize_test_cases.rb
+++ b/activesupport/test/constantize_test_cases.rb
@@ -73,6 +73,11 @@ module ConstantizeTestCases
yield("RaisesNoMethodError")
end
end
+
+ with_autoloading_fixtures do
+ yield("Prepend::SubClassConflict")
+ assert_equal "constant", defined?(Prepend::SubClassConflict)
+ end
end
def run_safe_constantize_tests_on
diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb
index 677e32db1d..c2a1e68f57 100644
--- a/activesupport/test/core_ext/object/duplicable_test.rb
+++ b/activesupport/test/core_ext/object/duplicable_test.rb
@@ -4,9 +4,13 @@ require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/numeric/time"
class DuplicableTest < ActiveSupport::TestCase
- RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts)]
- ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new]
- ALLOW_DUP << BigDecimal.new("4.56")
+ if RUBY_VERSION >= "2.4.0"
+ RAISE_DUP = [method(:puts)]
+ ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56"), nil, false, true, :symbol, 1, 2.3]
+ else
+ RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts)]
+ ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56")]
+ end
def test_duplicable
rubinius_skip "* Method#dup is allowed at the moment on Rubinius\n" \
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index 20cd34c182..16c7e782bc 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -162,6 +162,10 @@ $ cd actionpack
$ bundle exec ruby -Itest path/to/test.rb -n test_name
```
+### Railties Setup
+
+Some Railties tests depend on a JavaScript runtime environment, such as having [Node.js](https://nodejs.org/) installed.
+
### Active Record Setup
Active Record's test suite runs three times: once for SQLite3, once for MySQL, and once for PostgreSQL. We are going to see now how to set up the environment for them.
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 0ba220104a..8a1a440fca 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,15 @@
+* Add Webpack support in new apps via the --webpack option, which will delegate to the rails/webpacker gem.
+
+ To generate a new app that has Webpack dependencies configured and binstubs for webpack and webpack-watcher:
+
+ rails new myapp --webpack
+
+ To generate a new app that has Webpack + React configured and an example intalled:
+
+ rails new myapp --webpack=react
+
+ *DHH*
+
* Add Yarn support in new apps with a yarn binstub and vendor/package.json. Skippable via --skip-yarn option.
*Liceth Ovalles*, *Guillermo Iguaran*, *DHH*
diff --git a/railties/lib/rails/commands/plugin/plugin_command.rb b/railties/lib/rails/commands/plugin/plugin_command.rb
index 16587ce067..b40ab006af 100644
--- a/railties/lib/rails/commands/plugin/plugin_command.rb
+++ b/railties/lib/rails/commands/plugin/plugin_command.rb
@@ -11,7 +11,7 @@ module Rails
"#{executable} new [options]"
end
- class_option :rc, type: :boolean, default: File.join("~", ".railsrc"),
+ class_option :rc, type: :string, default: File.join("~", ".railsrc"),
desc: "Initialize the plugin command with previous defaults. Uses .railsrc in your home directory by default."
class_option :no_rc, desc: "Skip evaluating .railsrc."
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index aba68d6db2..15cc070e35 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -33,6 +33,9 @@ module Rails
class_option :javascript, type: :string, aliases: "-j",
desc: "Preconfigure for selected JavaScript library"
+ class_option :webpack, type: :string, default: nil,
+ desc: "Preconfigure for app-like JavaScript with Webpack"
+
class_option :skip_yarn, type: :boolean, default: false,
desc: "Don't use Yarn for managing JavaScript dependencies"
@@ -128,6 +131,7 @@ module Rails
database_gemfile_entry,
webserver_gemfile_entry,
assets_gemfile_entry,
+ webpacker_gemfile_entry,
javascript_gemfile_entry,
jbuilder_gemfile_entry,
psych_gemfile_entry,
@@ -315,6 +319,13 @@ module Rails
gems
end
+ def webpacker_gemfile_entry
+ return [] unless options[:webpack]
+
+ comment = "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
+ GemfileEntry.github "webpacker", "rails/webpacker", nil, comment
+ end
+
def jbuilder_gemfile_entry
comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder"
GemfileEntry.new "jbuilder", "~> 2.5", comment, {}, options[:api]
@@ -414,6 +425,13 @@ module Rails
bundle_command("install") if bundle_install?
end
+ def run_webpack
+ if !(webpack = options[:webpack]).nil?
+ rails_command "webpacker:install"
+ rails_command "webpacker:install:#{webpack}" unless webpack == "webpack"
+ end
+ end
+
def generate_spring_binstubs
if bundle_install? && spring_install?
bundle_command("exec spring binstub --all")
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index 45f2fba5b9..70f63dc672 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -90,10 +90,6 @@ module Rails
@class_path
end
- def namespaced_file_path
- @namespaced_file_path ||= namespaced_class_path.join("/")
- end
-
def namespaced_class_path
@namespaced_class_path ||= [namespaced_path] + @class_path
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index a8586d56dd..19d3ba2f0f 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -357,7 +357,7 @@ module Rails
end
public_task :apply_rails_template, :run_bundle
- public_task :generate_spring_binstubs
+ public_task :run_webpack, :generate_spring_binstubs
def run_after_bundle_callbacks
@after_bundle_callbacks.each(&:call)
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn b/railties/lib/rails/generators/rails/app/templates/bin/yarn
index cf48e2e5b7..872438cecb 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/yarn
+++ b/railties/lib/rails/generators/rails/app/templates/bin/yarn
@@ -1,2 +1,9 @@
VENDOR_PATH = File.expand_path('../vendor', __dir__)
-Dir.chdir(VENDOR_PATH) { exec "yarnpkg #{ARGV.join(" ")}" }
+Dir.chdir(VENDOR_PATH) do
+ begin
+ exec "yarnpkg #{ARGV.join(" ")}"
+ rescue Errno::ENOENT
+ puts "Yarn executable was not detected in the system."
+ puts "Download Yarn at https://yarnpkg.com/en/docs/install"
+ end
+end
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index ced3c85c00..01214dc919 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -16,7 +16,7 @@ module Rails
unless options[:skip_routes]
actions.reverse_each do |action|
# route prepends two spaces onto the front of the string that is passed, this corrects that.
- route generate_routing_code(action)[2..-1]
+ route generate_routing_code(action)
end
end
end
@@ -40,7 +40,7 @@ module Rails
# namespace :bar do
namespace_ladder = regular_class_path.each_with_index.map do |ns, i|
indent(" namespace :#{ns} do\n", i * 2)
- end.join
+ end.join[2..-1]
# Create route
# get 'baz/index'
@@ -54,7 +54,7 @@ module Rails
end.join
# Combine the 3 parts to generate complete route entry
- namespace_ladder + route + end_ladder
+ "#{namespace_ladder}#{route}#{end_ladder}"
end
end
end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index cd09270df1..d80a45a83f 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -387,14 +387,14 @@ module ApplicationTests
bin/rails generate model product name:string;
bin/rails db:migrate db:schema:cache:dump`
end
- assert File.exist?(File.join(app_path, "db", "schema_cache.dump"))
+ assert File.exist?(File.join(app_path, "db", "schema_cache.yml"))
end
def test_rake_clear_schema_cache
Dir.chdir(app_path) do
`bin/rails db:schema:cache:dump db:schema:cache:clear`
end
- assert !File.exist?(File.join(app_path, "db", "schema_cache.dump"))
+ assert !File.exist?(File.join(app_path, "db", "schema_cache.yml"))
end
def test_copy_templates