aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock7
-rw-r--r--actionmailer/test/base_test.rb8
-rw-r--r--actionpack/CHANGELOG.md4
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb2
-rw-r--r--actionpack/test/controller/parameters/accessors_test.rb8
-rw-r--r--activejob/lib/active_job/queue_adapters/async_adapter.rb2
-rw-r--r--activejob/lib/active_job/test_helper.rb6
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb38
-rw-r--r--activemodel/lib/active_model/attributes.rb32
-rw-r--r--activerecord/CHANGELOG.md10
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb24
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb43
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb22
-rw-r--r--activerecord/lib/active_record/collection_cache_key.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb29
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb11
-rw-r--r--activerecord/lib/active_record/core.rb26
-rw-r--r--activerecord/lib/active_record/model_schema.rb6
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb14
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb14
-rw-r--r--activesupport/CHANGELOG.md23
-rw-r--r--activesupport/lib/active_support/cache.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb6
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb62
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb50
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb4
-rw-r--r--activesupport/test/json/encoding_test.rb10
-rw-r--r--activesupport/test/multibyte_chars_test.rb93
-rw-r--r--activesupport/test/multibyte_conformance_test.rb98
-rw-r--r--activesupport/test/multibyte_normalization_conformance_test.rb98
-rw-r--r--guides/source/action_controller_overview.md2
-rw-r--r--guides/source/active_record_basics.md2
-rw-r--r--guides/source/association_basics.md22
-rw-r--r--guides/source/layouts_and_rendering.md2
-rw-r--r--railties/CHANGELOG.md6
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt2
-rw-r--r--railties/test/generators/app_generator_test.rb1
-rw-r--r--railties/test/generators/scaffold_generator_test.rb16
-rw-r--r--railties/test/isolation/abstract_unit.rb3
49 files changed, 518 insertions, 347 deletions
diff --git a/.travis.yml b/.travis.yml
index a4f5fa045d..f703ed0f90 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,7 +31,7 @@ addons:
- postgresql-10
- postgresql-client-10
-bundler_args: --without test --jobs 3 --retry 3
+bundler_args: --jobs 3 --retry 3
before_install:
- "rm ${BUNDLE_GEMFILE}.lock"
- "travis_retry gem update --system"
diff --git a/Gemfile b/Gemfile
index c630f95482..fb15bf127e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -99,6 +99,7 @@ instance_eval File.read local_gemfile if File.exist? local_gemfile
group :test do
gem "minitest-bisect"
+ gem "minitest-retry"
platforms :mri do
gem "stackprof"
diff --git a/Gemfile.lock b/Gemfile.lock
index b4f46698bc..1321436814 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -306,7 +306,7 @@ GEM
loofah (2.2.2)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
- mail (2.7.0)
+ mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (0.3.3)
mimemagic (~> 0.3.2)
@@ -323,6 +323,8 @@ GEM
minitest-bisect (1.4.0)
minitest-server (~> 1.0)
path_expander (~> 1.0)
+ minitest-retry (0.1.9)
+ minitest (>= 5.0)
minitest-server (1.0.5)
minitest (~> 5.0)
mono_logger (1.1.0)
@@ -538,6 +540,7 @@ DEPENDENCIES
libxml-ruby
listen (>= 3.0.5, < 3.2)
minitest-bisect
+ minitest-retry
mysql2 (>= 0.4.10)
nokogiri (>= 1.8.1)
pg (>= 0.18.0)
@@ -576,4 +579,4 @@ DEPENDENCIES
websocket-client-simple!
BUNDLED WITH
- 1.16.5
+ 1.16.6
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 7a1a505398..cbbee9fae8 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -152,9 +152,9 @@ class BaseTest < ActiveSupport::TestCase
assert_equal(2, email.parts.length)
assert_equal("multipart/mixed", email.mime_type)
assert_equal("text/html", email.parts[0].mime_type)
- assert_equal("Attachment with content", email.parts[0].body.encoded)
+ assert_equal("Attachment with content", email.parts[0].decoded)
assert_equal("application/pdf", email.parts[1].mime_type)
- assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded)
+ assert_equal("This is test File content", email.parts[1].decoded)
end
test "adds the given :body as part" do
@@ -162,9 +162,9 @@ class BaseTest < ActiveSupport::TestCase
assert_equal(2, email.parts.length)
assert_equal("multipart/mixed", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
- assert_equal("I'm the eggman", email.parts[0].body.encoded)
+ assert_equal("I'm the eggman", email.parts[0].decoded)
assert_equal("application/pdf", email.parts[1].mime_type)
- assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded)
+ assert_equal("This is test File content", email.parts[1].decoded)
end
test "can embed an inline attachment" do
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 8205d79dd2..3858c211ea 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add `ActionController::Parameters#each_value`.
+
+ *Lukáš Zapletal*
+
* Deprecate `ActionDispatch::Http::ParameterFilter` in favor of `ActiveSupport::ParameterFilter`.
*Yoshiyuki Kinjo*
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index c1272ce667..04922b0715 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -352,7 +352,7 @@ module ActionController
# the same way as <tt>Hash#each_value</tt>.
def each_value(&block)
@parameters.each_pair do |key, value|
- yield [convert_hashes_to_parameters(key, value)]
+ yield convert_hashes_to_parameters(key, value)
end
end
diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb
index 9f1fb3d042..7789e654d5 100644
--- a/actionpack/test/controller/parameters/accessors_test.rb
+++ b/actionpack/test/controller/parameters/accessors_test.rb
@@ -77,11 +77,15 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
test "each_value carries permitted status" do
@params.permit!
- @params["person"].each_value { |value| assert(value.permitted?) if value == 32 }
+ @params.each_value do |value|
+ assert_predicate(value, :permitted?)
+ end
end
test "each_value carries unpermitted status" do
- @params["person"].each_value { |value| assert_not(value.permitted?) if value == 32 }
+ @params.each_value do |value|
+ assert_not_predicate(value, :permitted?)
+ end
end
test "each_key converts to hash for permitted" do
diff --git a/activejob/lib/active_job/queue_adapters/async_adapter.rb b/activejob/lib/active_job/queue_adapters/async_adapter.rb
index ebf6f384e3..53a7e3d53e 100644
--- a/activejob/lib/active_job/queue_adapters/async_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/async_adapter.rb
@@ -31,7 +31,7 @@ module ActiveJob
# jobs. Since jobs share a single thread pool, long-running jobs will block
# short-lived jobs. Fine for dev/test; bad for production.
class AsyncAdapter
- # See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadPoolExecutor.html] for executor options.
+ # See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadPoolExecutor.html] for executor options.
def initialize(**executor_options)
@scheduler = Scheduler.new(**executor_options)
end
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 261b68d4f8..229855c5d9 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -197,7 +197,7 @@ module ActiveJob
# assert_performed_jobs 2
# end
#
- # If a block is passed, that block should cause the specified number of
+ # If a block is passed, asserts that the block will cause the specified number of
# jobs to be performed.
#
# def test_jobs_again
@@ -279,7 +279,7 @@ module ActiveJob
# end
# end
#
- # If a block is passed, that block should not cause any job to be performed.
+ # If a block is passed, asserts that the block will not cause any job to be performed.
#
# def test_jobs_again
# assert_no_performed_jobs do
@@ -347,7 +347,7 @@ module ActiveJob
# end
#
#
- # If a block is passed, that block should cause the job to be
+ # If a block is passed, asserts that the block will cause the job to be
# enqueued with the given arguments.
#
# def test_assert_enqueued_with
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 1ad9071cc2..d8352343e9 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -474,5 +474,43 @@ module ActiveModel
def _read_attribute(attr)
__send__(attr)
end
+
+ module AttrNames # :nodoc:
+ DEF_SAFE_NAME = /\A[a-zA-Z_]\w*\z/
+
+ # We want to generate the methods via module_eval rather than
+ # define_method, because define_method is slower on dispatch.
+ # Evaluating many similar methods may use more memory as the instruction
+ # sequences are duplicated and cached (in MRI). define_method may
+ # be slower on dispatch, but if you're careful about the closure
+ # created, then define_method will consume much less memory.
+ #
+ # But sometimes the database might return columns with
+ # characters that are not allowed in normal method names (like
+ # 'my_column(omg)'. So to work around this we first define with
+ # the __temp__ identifier, and then use alias method to rename
+ # it to what we want.
+ #
+ # We are also defining a constant to hold the frozen string of
+ # the attribute name. Using a constant means that we do not have
+ # to allocate an object on each call to the attribute method.
+ # Making it frozen means that it doesn't get duped when used to
+ # key the @attributes in read_attribute.
+ def self.define_attribute_accessor_method(mod, attr_name, writer: false)
+ method_name = "#{attr_name}#{'=' if writer}"
+ if attr_name.ascii_only? && DEF_SAFE_NAME.match?(attr_name)
+ yield method_name, "'#{attr_name}'.freeze"
+ else
+ safe_name = attr_name.unpack1("h*")
+ const_name = "ATTR_#{safe_name}"
+ const_set(const_name, attr_name) unless const_defined?(const_name)
+ temp_method_name = "__temp__#{safe_name}#{'=' if writer}"
+ attr_name_expr = "::ActiveModel::AttributeMethods::AttrNames::#{const_name}"
+ yield temp_method_name, attr_name_expr
+ mod.send(:alias_method, method_name, temp_method_name)
+ mod.send(:undef_method, temp_method_name)
+ end
+ end
+ end
end
end
diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb
index 41fe5168f3..c3a446098c 100644
--- a/activemodel/lib/active_model/attributes.rb
+++ b/activemodel/lib/active_model/attributes.rb
@@ -29,17 +29,16 @@ module ActiveModel
private
def define_method_attribute=(name)
- safe_name = name.unpack1("h*")
- ActiveModel::AttributeMethods::AttrNames.set_name_cache safe_name, name
-
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def __temp__#{safe_name}=(value)
- name = ::ActiveModel::AttributeMethods::AttrNames::ATTR_#{safe_name}
- write_attribute(name, value)
- end
- alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
- undef_method :__temp__#{safe_name}=
- STR
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
+ generated_attribute_methods, name, writer: true,
+ ) do |temp_method_name, attr_name_expr|
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{temp_method_name}(value)
+ name = #{attr_name_expr}
+ write_attribute(name, value)
+ end
+ RUBY
+ end
end
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
@@ -97,15 +96,4 @@ module ActiveModel
write_attribute(attribute_name, value)
end
end
-
- module AttributeMethods #:nodoc:
- AttrNames = Module.new {
- def self.set_name_cache(name, value)
- const_name = "ATTR_#{name}"
- unless const_defined? const_name
- const_set const_name, -value
- end
- end
- }
- end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 32fc9df5c7..fd9f5c6385 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,4 +1,12 @@
-* Add `reselect` method. This is short-hand for `unscope(:select).select(fields)`.
+* Fix collection cache key with limit and custom select to avoid ambiguous timestamp column error.
+
+ Fixes #33056.
+
+ *Federico Martinez*
+
+* Add `reselect` method. This is a short-hand for `unscope(:select).select(fields)`.
+
+ Fixes #27340.
*Willian Gustavo Veiga*
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 221ebea8ea..eeb88e4b7a 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -22,15 +22,6 @@ module ActiveRecord
delegate :column_for_attribute, to: :class
end
- AttrNames = Module.new {
- def self.set_name_cache(name, value)
- const_name = "ATTR_#{name}"
- unless const_defined? const_name
- const_set const_name, -value
- end
- end
- }
-
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
class GeneratedAttributeMethods < Module #:nodoc:
@@ -270,21 +261,14 @@ module ActiveRecord
def respond_to?(name, include_private = false)
return false unless super
- case name
- when :to_partial_path
- name = "to_partial_path"
- when :to_model
- name = "to_model"
- else
- name = name.to_s
- end
-
# If the result is true then check for the select case.
# For queries selecting a subset of columns, return false for unselected columns.
# We check defined?(@attributes) not to issue warnings if called on objects that
# have been allocated but not yet initialized.
- if defined?(@attributes) && self.class.column_names.include?(name)
- return has_attribute?(name)
+ if defined?(@attributes)
+ if name = self.class.symbol_column_to_string(name.to_sym)
+ return has_attribute?(name)
+ end
end
true
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 903fe86e04..ff0e863529 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -8,42 +8,19 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
- # We want to generate the methods via module_eval rather than
- # define_method, because define_method is slower on dispatch.
- # Evaluating many similar methods may use more memory as the instruction
- # sequences are duplicated and cached (in MRI). define_method may
- # be slower on dispatch, but if you're careful about the closure
- # created, then define_method will consume much less memory.
- #
- # But sometimes the database might return columns with
- # characters that are not allowed in normal method names (like
- # 'my_column(omg)'. So to work around this we first define with
- # the __temp__ identifier, and then use alias method to rename
- # it to what we want.
- #
- # We are also defining a constant to hold the frozen string of
- # the attribute name. Using a constant means that we do not have
- # to allocate an object on each call to the attribute method.
- # Making it frozen means that it doesn't get duped when used to
- # key the @attributes in read_attribute.
def define_method_attribute(name)
- safe_name = name.unpack1("h*")
- temp_method = "__temp__#{safe_name}"
-
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def #{temp_method}
- #{sync_with_transaction_state}
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
- _read_attribute(name) { |n| missing_attribute(n, caller) }
- end
- STR
-
- generated_attribute_methods.module_eval do
- alias_method name, temp_method
- undef_method temp_method
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
+ generated_attribute_methods, name
+ ) do |temp_method_name, attr_name_expr|
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{temp_method_name}
+ #{sync_with_transaction_state}
+ name = #{attr_name_expr}
+ _read_attribute(name) { |n| missing_attribute(n, caller) }
+ end
+ RUBY
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 62743bc9d8..7f246eed46 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -13,19 +13,19 @@ module ActiveRecord
private
def define_method_attribute=(name)
- safe_name = name.unpack1("h*")
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def __temp__#{safe_name}=(value)
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
- #{sync_with_transaction_state}
- _write_attribute(name, value)
- end
- alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
- undef_method :__temp__#{safe_name}=
- STR
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
+ generated_attribute_methods, name, writer: true,
+ ) do |temp_method_name, attr_name_expr|
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{temp_method_name}(value)
+ name = #{attr_name_expr}
+ #{sync_with_transaction_state}
+ _write_attribute(name, value)
+ end
+ RUBY
+ end
end
end
diff --git a/activerecord/lib/active_record/collection_cache_key.rb b/activerecord/lib/active_record/collection_cache_key.rb
index 61581b0451..4b6db8a96c 100644
--- a/activerecord/lib/active_record/collection_cache_key.rb
+++ b/activerecord/lib/active_record/collection_cache_key.rb
@@ -20,9 +20,9 @@ module ActiveRecord
select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
if collection.has_limit_or_offset?
- query = collection.select(column)
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
subquery_alias = "subquery_for_cache_key"
- subquery_column = "#{subquery_alias}.#{timestamp_column}"
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
subquery = query.arel.as(subquery_alias)
arel = Arel::SelectManager.new(subquery).project(select_values % subquery_column)
else
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 015204c056..70607fda5a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -348,8 +348,8 @@ module ActiveRecord
#
# create_table :taggings do |t|
# t.references :tag, index: { name: 'index_taggings_on_tag_id' }
- # t.references :tagger, polymorphic: true, index: true
- # t.references :taggable, polymorphic: { default: 'Photo' }
+ # t.references :tagger, polymorphic: true
+ # t.references :taggable, polymorphic: { default: 'Photo' }, index: false
# end
def column(name, type, options = {})
name = name.to_s
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 723d8c318d..8a7020a799 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -846,17 +846,17 @@ module ActiveRecord
# [<tt>:null</tt>]
# Whether the column allows nulls. Defaults to true.
#
- # ====== Create a user_id bigint column
+ # ====== Create a user_id bigint column without a index
#
- # add_reference(:products, :user)
+ # add_reference(:products, :user, index: false)
#
# ====== Create a user_id string column
#
# add_reference(:products, :user, type: :string)
#
- # ====== Create supplier_id, supplier_type columns and appropriate index
+ # ====== Create supplier_id, supplier_type columns
#
- # add_reference(:products, :supplier, polymorphic: true, index: true)
+ # add_reference(:products, :supplier, polymorphic: true)
#
# ====== Create a supplier_id column with a unique index
#
@@ -884,7 +884,7 @@ module ActiveRecord
#
# ====== Remove the reference
#
- # remove_reference(:products, :user, index: true)
+ # remove_reference(:products, :user, index: false)
#
# ====== Remove polymorphic reference
#
@@ -892,7 +892,7 @@ module ActiveRecord
#
# ====== Remove the reference with a foreign key
#
- # remove_reference(:products, :user, index: true, foreign_key: true)
+ # remove_reference(:products, :user, foreign_key: true)
#
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
if foreign_key
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index fa10f18cb7..0fe868478c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -125,6 +125,8 @@ module ActiveRecord
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
config.fetch(:advisory_locks, true)
)
+
+ check_version
end
def replica?
@@ -502,6 +504,9 @@ module ActiveRecord
end
private
+ def check_version
+ end
+
def type_map
@type_map ||= Type::TypeMap.new.tap do |mapping|
initialize_type_map(mapping)
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 d40f38fb77..cb5eeb64dd 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -54,10 +54,6 @@ module ActiveRecord
super(connection, logger, config)
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
-
- if version < "5.5.8"
- raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.5.8."
- end
end
def version #:nodoc:
@@ -535,6 +531,12 @@ module ActiveRecord
end
private
+ def check_version
+ if version < "5.5.8"
+ raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.5.8."
+ end
+ end
+
def combine_multi_statements(total_sql)
total_sql.each_with_object([]) do |sql, total_sql_chunks|
previous_packet = total_sql_chunks.last
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index bc6eb11572..a280ca500a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -43,9 +43,14 @@ module ActiveRecord
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
conn_params.slice!(*valid_conn_param_keys)
- # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
- # so just pass a nil connection object for the time being.
- ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
+ conn = PG.connect(conn_params)
+ ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
+ rescue ::PG::Error => error
+ if error.message.include?("does not exist")
+ raise ActiveRecord::NoDatabaseError
+ else
+ raise
+ end
end
end
@@ -220,15 +225,11 @@ module ActiveRecord
@local_tz = nil
@max_identifier_length = nil
- connect
+ configure_connection
add_pg_encoders
@statements = StatementPool.new @connection,
self.class.type_cast_config_to_integer(config[:statement_limit])
- if postgresql_version < 90100
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
- end
-
add_pg_decoders
@type_map = Type::HashLookupTypeMap.new
@@ -410,6 +411,12 @@ module ActiveRecord
end
private
+ def check_version
+ if postgresql_version < 90100
+ raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
+ end
+ end
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
VALUE_LIMIT_VIOLATION = "22001"
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
@@ -699,12 +706,6 @@ module ActiveRecord
def connect
@connection = PG.connect(@connection_parameters)
configure_connection
- rescue ::PG::Error => error
- if error.message.include?("does not exist")
- raise ActiveRecord::NoDatabaseError
- else
- raise
- end
end
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 81882f6cc1..e0355a316b 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -105,11 +105,6 @@ module ActiveRecord
@active = true
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
-
- if sqlite_version < "3.8.0"
- raise "Your version of SQLite (#{sqlite_version}) is too old. Active Record supports SQLite >= 3.8."
- end
-
configure_connection
end
@@ -401,6 +396,12 @@ module ActiveRecord
end
private
+ def check_version
+ if sqlite_version < "3.8.0"
+ raise "Your version of SQLite (#{sqlite_version}) is too old. Active Record supports SQLite >= 3.8."
+ end
+ end
+
def initialize_type_map(m = type_map)
super
register_class_with_limit m, %r(int)i, SQLite3Integer
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index b53681ee20..0718688863 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -344,34 +344,22 @@ module ActiveRecord
# post = Post.allocate
# post.init_with(coder)
# post.title # => 'hello world'
- def init_with(coder)
+ def init_with(coder, &block)
coder = LegacyYamlAdapter.convert(self.class, coder)
- @attributes = self.class.yaml_encoder.decode(coder)
-
- init_internals
-
- @new_record = coder["new_record"]
-
- self.class.define_attribute_methods
-
- yield self if block_given?
-
- _run_find_callbacks
- _run_initialize_callbacks
-
- self
+ attributes = self.class.yaml_encoder.decode(coder)
+ init_with_attributes(attributes, coder["new_record"], &block)
end
##
- # Initializer used for instantiating objects that have been read from the
- # database. +attributes+ should be an attributes object, and unlike the
+ # Initialize an empty model object from +attributes+.
+ # +attributes+ should be an attributes object, and unlike the
# `initialize` method, no assignment calls are made per attribute.
#
# :nodoc:
- def init_from_db(attributes)
+ def init_with_attributes(attributes, new_record = false)
init_internals
- @new_record = false
+ @new_record = new_record
@attributes = attributes
self.class.define_attribute_methods
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 5a4a1fb969..c50a420432 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -388,6 +388,11 @@ module ActiveRecord
@column_names ||= columns.map(&:name)
end
+ def symbol_column_to_string(name_symbol) # :nodoc:
+ @symbol_column_to_string_name_hash ||= column_names.index_by(&:to_sym)
+ @symbol_column_to_string_name_hash[name_symbol]
+ end
+
# Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
# and columns used for single table inheritance have been removed.
def content_columns
@@ -477,6 +482,7 @@ module ActiveRecord
def reload_schema_from_cache
@arel_table = nil
@column_names = nil
+ @symbol_column_to_string_name_hash = nil
@attribute_types = nil
@content_columns = nil
@default_attributes = nil
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 50767ee93f..8b9098df6c 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -426,7 +426,7 @@ module ActiveRecord
existing_record.assign_attributes(assignable_attributes)
association(association_name).initialize_attributes(existing_record)
else
- method = "build_#{association_name}"
+ method = :"build_#{association_name}"
if respond_to?(method)
send(method, assignable_attributes)
else
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 8404119631..45ceb1e3ad 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -209,7 +209,7 @@ module ActiveRecord
# new instance of the class. Accepts only keys as strings.
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
attributes = klass.attributes_builder.build_from_database(attributes, column_types)
- klass.allocate.init_from_db(attributes, &block)
+ klass.allocate.init_with_attributes(attributes, &block)
end
# Called by +instantiate+ to decide which class to use for a new
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 38b121d37b..515eb65d37 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -25,6 +25,8 @@ require "models/user"
require "models/member"
require "models/membership"
require "models/sponsor"
+require "models/lesson"
+require "models/student"
require "models/country"
require "models/treaty"
require "models/vertex"
@@ -275,7 +277,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_habtm_saving_multiple_relationships
new_project = Project.new("name" => "Grimetime")
amount_of_developers = 4
- developers = (0...amount_of_developers).collect { |i| Developer.create(name: "JME #{i}") }.reverse
+ developers = (0...amount_of_developers).reverse_each.map { |i| Developer.create(name: "JME #{i}") }
new_project.developer_ids = [developers[0].id, developers[1].id]
new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
@@ -780,6 +782,16 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
end
+ def test_singular_ids_are_reloaded_after_collection_concat
+ student = Student.create(name: "Alberto Almagro")
+ student.lesson_ids
+
+ lesson = Lesson.create(name: "DSI")
+ student.lessons << lesson
+
+ assert_includes student.lesson_ids, lesson.id
+ end
+
def test_scoped_find_on_through_association_doesnt_return_read_only_records
tag = Post.find(1).tags.find_by_name("General")
diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb
index 844b2b2162..483383257b 100644
--- a/activerecord/test/cases/collection_cache_key_test.rb
+++ b/activerecord/test/cases/collection_cache_key_test.rb
@@ -42,6 +42,20 @@ module ActiveRecord
assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
end
+ test "cache_key for relation with custom select and limit" do
+ developers = Developer.where(salary: 100000).order(updated_at: :desc).limit(5)
+ developers_with_select = developers.select("developers.*")
+ last_developer_timestamp = developers.first.updated_at
+
+ assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers_with_select.cache_key)
+
+ /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers_with_select.cache_key
+
+ assert_equal ActiveSupport::Digest.hexdigest(developers_with_select.to_sql), $1
+ assert_equal developers.count.to_s, $2
+ assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
+ end
+
test "cache_key for loaded relation" do
developers = Developer.where(salary: 100000).order(updated_at: :desc).limit(5).load
last_developer_timestamp = developers.first.updated_at
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index b796df26aa..88dfc64ed1 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,26 @@
+* Deprecate `ActiveSupport::Multibyte::Chars.consumes?` in favor of `String#is_utf8?`.
+
+ *Francesco Rodríguez*
+
+* Fix duration being rounded to a full second.
+ ```
+ time = DateTime.parse("2018-1-1")
+ time += 0.51.seconds
+ ```
+ Will now correctly add 0.51 second and not 1 full second.
+
+ *Edouard Chin*
+
+* Deprecate `ActiveSupport::Multibyte::Unicode#normalize` and `ActiveSuppport::Multibyte::Chars#normalize`
+ in favor of `String#unicode_normalize`
+
+ *Francesco Rodríguez*
+
+* Deprecate `ActiveSupport::Multibyte::Unicode#downcase/upcase/swapcase` in favor of
+ `String#downcase/upcase/swapcase`.
+
+ *Francesco Rodríguez*
+
* Add `ActiveSupport::ParameterFilter`.
*Yoshiyuki Kinjo*
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 2be79d86dd..e8518645d9 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -411,8 +411,6 @@ module ActiveSupport
# to the cache. If you do not want to write the cache when the cache is
# not found, use #read_multi.
#
- # Options are passed to the underlying cache implementation.
- #
# Returns a hash with the data for each of the names. For example:
#
# cache.write("bim", "bam")
@@ -422,6 +420,17 @@ module ActiveSupport
# # => { "bim" => "bam",
# # "unknown_key" => "Fallback value for key: unknown_key" }
#
+ # Options are passed to the underlying cache implementation. For example:
+ #
+ # cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
+ # "buzz"
+ # end
+ # # => {"fizz"=>"buzz"}
+ # cache.read("fizz")
+ # # => "buzz"
+ # sleep(6)
+ # cache.read("fizz")
+ # # => nil
def fetch_multi(*names)
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index e61b23f842..bc670c3e76 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -110,7 +110,7 @@ class DateTime
# instance time. Do not use this method in combination with x.months, use
# months_since instead!
def since(seconds)
- self + Rational(seconds.round, 86400)
+ self + Rational(seconds, 86400)
end
alias :in :since
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index dbc8b8a2fa..0d2a17970f 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -62,9 +62,9 @@ module ActiveSupport
raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
I18n.transliterate(
- ActiveSupport::Multibyte::Unicode.normalize(
- ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
- replacement: replacement)
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
+ replacement: replacement
+ )
end
# Replaces special characters in a string so that it may be used as part of
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 499a206f49..bd8cab3460 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -17,7 +17,7 @@ module ActiveSupport #:nodoc:
# through the +mb_chars+ method. Methods which would normally return a
# String object now return a Chars object so methods can be chained.
#
- # 'The Perfect String '.mb_chars.downcase.strip.normalize
+ # 'The Perfect String '.mb_chars.downcase.strip
# # => #<ActiveSupport::Multibyte::Chars:0x007fdc434ccc10 @wrapped_string="the perfect string">
#
# Chars objects are perfectly interchangeable with String objects as long as
@@ -76,6 +76,11 @@ module ActiveSupport #:nodoc:
# Returns +true+ when the proxy class can handle the string. Returns
# +false+ otherwise.
def self.consumes?(string)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars.consumes? is deprecated and will be
+ removed from Rails 6.1. Use string.is_utf8? instead.
+ MSG
+
string.encoding == Encoding::UTF_8
end
@@ -108,7 +113,7 @@ module ActiveSupport #:nodoc:
#
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
def reverse
- chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack("U*"))
+ chars(@wrapped_string.scan(/\X/).reverse.join)
end
# Limits the byte size of the string to a number of bytes without breaking
@@ -120,40 +125,12 @@ module ActiveSupport #:nodoc:
slice(0...translate_offset(limit))
end
- # Converts characters in the string to uppercase.
- #
- # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
- def upcase
- chars Unicode.upcase(@wrapped_string)
- end
-
- # Converts characters in the string to lowercase.
- #
- # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
- def downcase
- chars Unicode.downcase(@wrapped_string)
- end
-
- # Converts characters in the string to the opposite case.
- #
- # 'El Cañón'.mb_chars.swapcase.to_s # => "eL cAÑÓN"
- def swapcase
- chars Unicode.swapcase(@wrapped_string)
- end
-
- # Converts the first character to uppercase and the remainder to lowercase.
- #
- # 'über'.mb_chars.capitalize.to_s # => "Über"
- def capitalize
- (slice(0) || chars("")).upcase + (slice(1..-1) || chars("")).downcase
- end
-
# Capitalizes the first letter of every word, when possible.
#
# "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró"
# "日本語".mb_chars.titleize.to_s # => "日本語"
def titleize
- chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1) })
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { $1.upcase })
end
alias_method :titlecase, :titleize
@@ -165,7 +142,24 @@ module ActiveSupport #:nodoc:
# <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
# ActiveSupport::Multibyte::Unicode.default_normalization_form
def normalize(form = nil)
- chars(Unicode.normalize(@wrapped_string, form))
+ form ||= Unicode.default_normalization_form
+
+ # See https://www.unicode.org/reports/tr15, Table 1
+ if alias_form = Unicode::NORMALIZATION_FORM_ALIASES[form]
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
+ removed from Rails 6.1. Use #unicode_normalize(:#{alias_form}) instead.
+ MSG
+
+ send(:unicode_normalize, alias_form)
+ else
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
+ removed from Rails 6.1. Use #unicode_normalize instead.
+ MSG
+
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
+ end
end
# Performs canonical decomposition on all the characters.
@@ -189,7 +183,7 @@ module ActiveSupport #:nodoc:
# 'क्षि'.mb_chars.length # => 4
# 'क्षि'.mb_chars.grapheme_length # => 3
def grapheme_length
- Unicode.unpack_graphemes(@wrapped_string).length
+ @wrapped_string.scan(/\X/).length
end
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
@@ -205,7 +199,7 @@ module ActiveSupport #:nodoc:
to_s.as_json(options)
end
- %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
+ %w(reverse tidy_bytes).each do |method|
define_method("#{method}!") do |*args|
@wrapped_string = send(method, *args).to_s
self
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 4f0e1165ef..43d196eeeb 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -6,10 +6,17 @@ module ActiveSupport
extend self
# A list of all available normalization forms.
- # See http://www.unicode.org/reports/tr15/tr15-29.html for more
+ # See https://www.unicode.org/reports/tr15/tr15-29.html for more
# information about normalization.
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
+ NORMALIZATION_FORM_ALIASES = { # :nodoc:
+ c: :nfc,
+ d: :nfd,
+ kc: :nfkc,
+ kd: :nfkd
+ }
+
# The Unicode version that is supported by the implementation
UNICODE_VERSION = RbConfig::CONFIG["UNICODE_VERSION"]
@@ -100,31 +107,34 @@ module ActiveSupport
# Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
def normalize(string, form = nil)
form ||= @default_normalization_form
- # See http://www.unicode.org/reports/tr15, Table 1
- case form
- when :d
- string.unicode_normalize(:nfd)
- when :c
- string.unicode_normalize(:nfc)
- when :kd
- string.unicode_normalize(:nfkd)
- when :kc
- string.unicode_normalize(:nfkc)
+
+ # See https://www.unicode.org/reports/tr15, Table 1
+ if alias_form = NORMALIZATION_FORM_ALIASES[form]
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#normalize is deprecated and will be
+ removed from Rails 6.1. Use String#unicode_normalize(:#{alias_form}) instead.
+ MSG
+
+ string.unicode_normalize(alias_form)
else
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode#normalize is deprecated and will be
+ removed from Rails 6.1. Use String#unicode_normalize instead.
+ MSG
+
raise ArgumentError, "#{form} is not a valid normalization variant", caller
end
end
- def downcase(string)
- string.downcase
- end
-
- def upcase(string)
- string.upcase
- end
+ %w(downcase upcase swapcase).each do |method|
+ define_method(method) do |string|
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveSupport::Multibyte::Unicode##{method} is deprecated and
+ will be removed from Rails 6.1. Use String methods directly.
+ MSG
- def swapcase(string)
- string.swapcase
+ string.send(method)
+ end
end
private
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 894fb80cba..f9f6b21c9b 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -152,8 +152,8 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal DateTime.civil(2005, 2, 22, 11, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).since(3600)
assert_equal DateTime.civil(2005, 2, 24, 10, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).since(86400 * 2)
assert_equal DateTime.civil(2005, 2, 24, 11, 10, 35), DateTime.civil(2005, 2, 22, 10, 10, 10).since(86400 * 2 + 3600 + 25)
- assert_equal DateTime.civil(2005, 2, 22, 10, 10, 11), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.333)
- assert_equal DateTime.civil(2005, 2, 22, 10, 10, 12), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.667)
+ assert_not_equal DateTime.civil(2005, 2, 22, 10, 10, 11), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.333)
+ assert_not_equal DateTime.civil(2005, 2, 22, 10, 10, 12), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.667)
end
def test_change
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index ebc5df14dd..8062873386 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -157,6 +157,16 @@ class TestJSONEncoding < ActiveSupport::TestCase
assert_equal({ "foo" => "hello" }, JSON.parse(json))
end
+ def test_struct_to_json_with_options_nested
+ klass = Struct.new(:foo, :bar)
+ struct = klass.new "hello", "world"
+ parent_struct = klass.new struct, "world"
+ json = parent_struct.to_json only: [:foo]
+
+ assert_equal({ "foo" => { "foo" => "hello" } }, JSON.parse(json))
+ end
+
+
def test_hash_should_pass_encoding_options_to_children_in_as_json
person = {
name: "John",
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 11c4822748..6b33f5f9b2 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -73,9 +73,15 @@ class MultibyteCharsTest < ActiveSupport::TestCase
end
def test_consumes_utf8_strings
- assert @proxy_class.consumes?(UNICODE_STRING)
- assert @proxy_class.consumes?(ASCII_STRING)
- assert_not @proxy_class.consumes?(BYTE_STRING)
+ ActiveSupport::Deprecation.silence do
+ assert @proxy_class.consumes?(UNICODE_STRING)
+ assert @proxy_class.consumes?(ASCII_STRING)
+ assert_not @proxy_class.consumes?(BYTE_STRING)
+ end
+ end
+
+ def test_consumes_is_deprecated
+ assert_deprecated { @proxy_class.consumes?(UNICODE_STRING) }
end
def test_concatenation_should_return_a_proxy_class_instance
@@ -165,7 +171,9 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
assert chars("").upcase.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").downcase.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").capitalize.kind_of?(ActiveSupport::Multibyte.proxy_class)
- assert chars("").normalize.kind_of?(ActiveSupport::Multibyte.proxy_class)
+ ActiveSupport::Deprecation.silence do
+ assert chars("").normalize.kind_of?(ActiveSupport::Multibyte.proxy_class)
+ end
assert chars("").decompose.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").compose.kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").tidy_bytes.kind_of?(ActiveSupport::Multibyte.proxy_class)
@@ -383,10 +391,12 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
def test_reverse_should_work_with_normalized_strings
str = "bös"
reversed_str = "söb"
- assert_equal chars(reversed_str).normalize(:kc), chars(str).normalize(:kc).reverse
- assert_equal chars(reversed_str).normalize(:c), chars(str).normalize(:c).reverse
- assert_equal chars(reversed_str).normalize(:d), chars(str).normalize(:d).reverse
- assert_equal chars(reversed_str).normalize(:kd), chars(str).normalize(:kd).reverse
+ ActiveSupport::Deprecation.silence do
+ assert_equal chars(reversed_str).normalize(:kc), chars(str).normalize(:kc).reverse
+ assert_equal chars(reversed_str).normalize(:c), chars(str).normalize(:c).reverse
+ assert_equal chars(reversed_str).normalize(:d), chars(str).normalize(:d).reverse
+ assert_equal chars(reversed_str).normalize(:kd), chars(str).normalize(:kd).reverse
+ end
assert_equal chars(reversed_str).decompose, chars(str).decompose.reverse
assert_equal chars(reversed_str).compose, chars(str).compose.reverse
end
@@ -477,7 +487,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
def test_method_works_for_proxyed_methods
assert_equal "ll", "hello".mb_chars.method(:slice).call(2..3) # Defined on Chars
- chars = "hello".mb_chars
+ chars = +"hello".mb_chars
assert_equal "Hello", chars.method(:capitalize!).call # Defined on Chars
assert_equal "Hello", chars
assert_equal "jello", "hello".mb_chars.method(:gsub).call(/h/, "j") # Defined on String
@@ -568,7 +578,9 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
def test_composition_exclusion_is_set_up_properly
# Normalization of DEVANAGARI LETTER QA breaks when composition exclusion isn't used correctly
qa = [0x915, 0x93c].pack("U*")
- assert_equal qa, chars(qa).normalize(:c)
+ ActiveSupport::Deprecation.silence do
+ assert_equal qa, chars(qa).normalize(:c)
+ end
end
# Test for the Public Review Issue #29, bad explanation of composition might lead to a
@@ -578,17 +590,21 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
[0x0B47, 0x0300, 0x0B3E],
[0x1100, 0x0300, 0x1161]
].map { |c| c.pack("U*") }.each do |c|
- assert_equal_codepoints c, chars(c).normalize(:c)
+ ActiveSupport::Deprecation.silence do
+ assert_equal_codepoints c, chars(c).normalize(:c)
+ end
end
end
def test_normalization_shouldnt_strip_null_bytes
null_byte_str = "Test\0test"
- assert_equal null_byte_str, chars(null_byte_str).normalize(:kc)
- assert_equal null_byte_str, chars(null_byte_str).normalize(:c)
- assert_equal null_byte_str, chars(null_byte_str).normalize(:d)
- assert_equal null_byte_str, chars(null_byte_str).normalize(:kd)
+ ActiveSupport::Deprecation.silence do
+ assert_equal null_byte_str, chars(null_byte_str).normalize(:kc)
+ assert_equal null_byte_str, chars(null_byte_str).normalize(:c)
+ assert_equal null_byte_str, chars(null_byte_str).normalize(:d)
+ assert_equal null_byte_str, chars(null_byte_str).normalize(:kd)
+ end
assert_equal null_byte_str, chars(null_byte_str).decompose
assert_equal null_byte_str, chars(null_byte_str).compose
end
@@ -601,11 +617,13 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
323 # COMBINING DOT BELOW
].pack("U*")
- assert_equal_codepoints "", chars("").normalize
- assert_equal_codepoints [44, 105, 106, 328, 323].pack("U*"), chars(comp_str).normalize(:kc).to_s
- assert_equal_codepoints [44, 307, 328, 323].pack("U*"), chars(comp_str).normalize(:c).to_s
- assert_equal_codepoints [44, 307, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:d).to_s
- assert_equal_codepoints [44, 105, 106, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:kd).to_s
+ ActiveSupport::Deprecation.silence do
+ assert_equal_codepoints "", chars("").normalize
+ assert_equal_codepoints [44, 105, 106, 328, 323].pack("U*"), chars(comp_str).normalize(:kc).to_s
+ assert_equal_codepoints [44, 307, 328, 323].pack("U*"), chars(comp_str).normalize(:c).to_s
+ assert_equal_codepoints [44, 307, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:d).to_s
+ assert_equal_codepoints [44, 105, 106, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:kd).to_s
+ end
end
def test_should_compute_grapheme_length
@@ -719,6 +737,41 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
assert_equal BYTE_STRING.dup.mb_chars.class, ActiveSupport::Multibyte::Chars
end
+ def test_unicode_normalize_deprecation
+ # String#unicode_normalize default form is `:nfc`, and
+ # different than Multibyte::Unicode default, `:nkfc`.
+ # Deprecation should suggest the right form if no params
+ # are given and default is used.
+ assert_deprecated(/unicode_normalize\(:nfkc\)/) do
+ ActiveSupport::Multibyte::Unicode.normalize("")
+ end
+
+ assert_deprecated(/unicode_normalize\(:nfd\)/) do
+ ActiveSupport::Multibyte::Unicode.normalize("", :d)
+ end
+ end
+
+ def test_chars_normalize_deprecation
+ # String#unicode_normalize default form is `:nfc`, and
+ # different than Multibyte::Unicode default, `:nkfc`.
+ # Deprecation should suggest the right form if no params
+ # are given and default is used.
+ assert_deprecated(/unicode_normalize\(:nfkc\)/) do
+ "".mb_chars.normalize
+ end
+
+ assert_deprecated(/unicode_normalize\(:nfc\)/) { "".mb_chars.normalize(:c) }
+ assert_deprecated(/unicode_normalize\(:nfd\)/) { "".mb_chars.normalize(:d) }
+ assert_deprecated(/unicode_normalize\(:nfkc\)/) { "".mb_chars.normalize(:kc) }
+ assert_deprecated(/unicode_normalize\(:nfkd\)/) { "".mb_chars.normalize(:kd) }
+ end
+
+ def test_unicode_deprecations
+ assert_deprecated { ActiveSupport::Multibyte::Unicode.downcase("") }
+ assert_deprecated { ActiveSupport::Multibyte::Unicode.upcase("") }
+ assert_deprecated { ActiveSupport::Multibyte::Unicode.swapcase("") }
+ end
+
private
def string_from_classes(classes)
diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb
index a704505fc6..b19689723f 100644
--- a/activesupport/test/multibyte_conformance_test.rb
+++ b/activesupport/test/multibyte_conformance_test.rb
@@ -18,64 +18,72 @@ class MultibyteConformanceTest < ActiveSupport::TestCase
end
def test_normalizations_C
- each_line_of_norm_tests do |*cols|
- col1, col2, col3, col4, col5, comment = *cols
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do |*cols|
+ col1, col2, col3, col4, col5, comment = *cols
- # CONFORMANCE:
- # 1. The following invariants must be true for all conformant implementations
- #
- # NFC
- # c2 == NFC(c1) == NFC(c2) == NFC(c3)
- assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}"
- assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}"
- assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}"
- #
- # c4 == NFC(c4) == NFC(c5)
- assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}"
+ # CONFORMANCE:
+ # 1. The following invariants must be true for all conformant implementations
+ #
+ # NFC
+ # c2 == NFC(c1) == NFC(c2) == NFC(c3)
+ assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}"
+ assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}"
+ assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}"
+ #
+ # c4 == NFC(c4) == NFC(c5)
+ assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}"
+ end
end
end
def test_normalizations_D
- each_line_of_norm_tests do |*cols|
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFD
- # c3 == NFD(c1) == NFD(c2) == NFD(c3)
- assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}"
- assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}"
- assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}"
- # c5 == NFD(c4) == NFD(c5)
- assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do |*cols|
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFD
+ # c3 == NFD(c1) == NFD(c2) == NFD(c3)
+ assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}"
+ assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}"
+ assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}"
+ # c5 == NFD(c4) == NFD(c5)
+ assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}"
+ end
end
end
def test_normalizations_KC
- each_line_of_norm_tests do | *cols |
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFKC
- # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
- assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do | *cols |
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFKC
+ # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
+ assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}"
+ end
end
end
def test_normalizations_KD
- each_line_of_norm_tests do | *cols |
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFKD
- # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
- assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do | *cols |
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFKD
+ # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
+ assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}"
+ end
end
end
diff --git a/activesupport/test/multibyte_normalization_conformance_test.rb b/activesupport/test/multibyte_normalization_conformance_test.rb
index 3674ea44f3..82edf69294 100644
--- a/activesupport/test/multibyte_normalization_conformance_test.rb
+++ b/activesupport/test/multibyte_normalization_conformance_test.rb
@@ -18,64 +18,72 @@ class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase
end
def test_normalizations_C
- each_line_of_norm_tests do |*cols|
- col1, col2, col3, col4, col5, comment = *cols
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do |*cols|
+ col1, col2, col3, col4, col5, comment = *cols
- # CONFORMANCE:
- # 1. The following invariants must be true for all conformant implementations
- #
- # NFC
- # c2 == NFC(c1) == NFC(c2) == NFC(c3)
- assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}"
- assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}"
- assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}"
- #
- # c4 == NFC(c4) == NFC(c5)
- assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}"
+ # CONFORMANCE:
+ # 1. The following invariants must be true for all conformant implementations
+ #
+ # NFC
+ # c2 == NFC(c1) == NFC(c2) == NFC(c3)
+ assert_equal_codepoints col2, @proxy.new(col1).normalize(:c), "Form C - Col 2 has to be NFC(1) - #{comment}"
+ assert_equal_codepoints col2, @proxy.new(col2).normalize(:c), "Form C - Col 2 has to be NFC(2) - #{comment}"
+ assert_equal_codepoints col2, @proxy.new(col3).normalize(:c), "Form C - Col 2 has to be NFC(3) - #{comment}"
+ #
+ # c4 == NFC(c4) == NFC(c5)
+ assert_equal_codepoints col4, @proxy.new(col4).normalize(:c), "Form C - Col 4 has to be C(4) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col5).normalize(:c), "Form C - Col 4 has to be C(5) - #{comment}"
+ end
end
end
def test_normalizations_D
- each_line_of_norm_tests do |*cols|
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFD
- # c3 == NFD(c1) == NFD(c2) == NFD(c3)
- assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}"
- assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}"
- assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}"
- # c5 == NFD(c4) == NFD(c5)
- assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do |*cols|
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFD
+ # c3 == NFD(c1) == NFD(c2) == NFD(c3)
+ assert_equal_codepoints col3, @proxy.new(col1).normalize(:d), "Form D - Col 3 has to be NFD(1) - #{comment}"
+ assert_equal_codepoints col3, @proxy.new(col2).normalize(:d), "Form D - Col 3 has to be NFD(2) - #{comment}"
+ assert_equal_codepoints col3, @proxy.new(col3).normalize(:d), "Form D - Col 3 has to be NFD(3) - #{comment}"
+ # c5 == NFD(c4) == NFD(c5)
+ assert_equal_codepoints col5, @proxy.new(col4).normalize(:d), "Form D - Col 5 has to be NFD(4) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col5).normalize(:d), "Form D - Col 5 has to be NFD(5) - #{comment}"
+ end
end
end
def test_normalizations_KC
- each_line_of_norm_tests do | *cols |
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFKC
- # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
- assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}"
- assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do | *cols |
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFKC
+ # c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
+ assert_equal_codepoints col4, @proxy.new(col1).normalize(:kc), "Form D - Col 4 has to be NFKC(1) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col2).normalize(:kc), "Form D - Col 4 has to be NFKC(2) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col3).normalize(:kc), "Form D - Col 4 has to be NFKC(3) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col4).normalize(:kc), "Form D - Col 4 has to be NFKC(4) - #{comment}"
+ assert_equal_codepoints col4, @proxy.new(col5).normalize(:kc), "Form D - Col 4 has to be NFKC(5) - #{comment}"
+ end
end
end
def test_normalizations_KD
- each_line_of_norm_tests do | *cols |
- col1, col2, col3, col4, col5, comment = *cols
- #
- # NFKD
- # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
- assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}"
- assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}"
+ ActiveSupport::Deprecation.silence do
+ each_line_of_norm_tests do | *cols |
+ col1, col2, col3, col4, col5, comment = *cols
+ #
+ # NFKD
+ # c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
+ assert_equal_codepoints col5, @proxy.new(col1).normalize(:kd), "Form KD - Col 5 has to be NFKD(1) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col2).normalize(:kd), "Form KD - Col 5 has to be NFKD(2) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col3).normalize(:kd), "Form KD - Col 5 has to be NFKD(3) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col4).normalize(:kd), "Form KD - Col 5 has to be NFKD(4) - #{comment}"
+ assert_equal_codepoints col5, @proxy.new(col5).normalize(:kd), "Form KD - Col 5 has to be NFKD(5) - #{comment}"
+ end
end
end
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 43bc9306ce..aa746e4731 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -166,7 +166,7 @@ NOTE: Support for parsing XML parameters has been extracted into a gem named `ac
The `params` hash will always contain the `:controller` and `:action` keys, but you should use the methods `controller_name` and `action_name` instead to access these values. Any other parameters defined by the routing, such as `:id`, will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the `:status` parameter in a "pretty" URL:
```ruby
-get '/clients/:status' => 'clients#index', foo: 'bar'
+get '/clients/:status', to: 'clients#index', foo: 'bar'
```
In this case, when a user opens the URL `/clients/active`, `params[:status]` will be set to "active". When this route is used, `params[:foo]` will also be set to "bar", as if it were passed in the query string. Your controller will also receive `params[:action]` as "index" and `params[:controller]` as "clients".
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index fad4c19827..b9e24099b1 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -82,9 +82,9 @@ of two or more words, the model class name should follow the Ruby conventions,
using the CamelCase form, while the table name must contain the words separated
by underscores. Examples:
-* Database Table - Plural with underscores separating words (e.g., `book_clubs`).
* Model Class - Singular with the first letter of each word capitalized (e.g.,
`BookClub`).
+* Database Table - Plural with underscores separating words (e.g., `book_clubs`).
| Model / Class | Table / Schema |
| ---------------- | -------------- |
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index a2231c55d7..b0a905c754 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -109,7 +109,7 @@ class CreateBooks < ActiveRecord::Migration[5.0]
end
create_table :books do |t|
- t.belongs_to :author, index: true
+ t.belongs_to :author
t.datetime :published_at
t.timestamps
end
@@ -140,7 +140,7 @@ class CreateSuppliers < ActiveRecord::Migration[5.0]
end
create_table :accounts do |t|
- t.belongs_to :supplier, index: true
+ t.belongs_to :supplier
t.string :account_number
t.timestamps
end
@@ -184,7 +184,7 @@ class CreateAuthors < ActiveRecord::Migration[5.0]
end
create_table :books do |t|
- t.belongs_to :author, index: true
+ t.belongs_to :author
t.datetime :published_at
t.timestamps
end
@@ -231,8 +231,8 @@ class CreateAppointments < ActiveRecord::Migration[5.0]
end
create_table :appointments do |t|
- t.belongs_to :physician, index: true
- t.belongs_to :patient, index: true
+ t.belongs_to :physician
+ t.belongs_to :patient
t.datetime :appointment_date
t.timestamps
end
@@ -312,13 +312,13 @@ class CreateAccountHistories < ActiveRecord::Migration[5.0]
end
create_table :accounts do |t|
- t.belongs_to :supplier, index: true
+ t.belongs_to :supplier
t.string :account_number
t.timestamps
end
create_table :account_histories do |t|
- t.belongs_to :account, index: true
+ t.belongs_to :account
t.integer :credit_rating
t.timestamps
end
@@ -358,8 +358,8 @@ class CreateAssembliesAndParts < ActiveRecord::Migration[5.0]
end
create_table :assemblies_parts, id: false do |t|
- t.belongs_to :assembly, index: true
- t.belongs_to :part, index: true
+ t.belongs_to :assembly
+ t.belongs_to :part
end
end
end
@@ -487,7 +487,7 @@ class CreatePictures < ActiveRecord::Migration[5.0]
def change
create_table :pictures do |t|
t.string :name
- t.references :imageable, polymorphic: true, index: true
+ t.references :imageable, polymorphic: true
t.timestamps
end
end
@@ -517,7 +517,7 @@ In your migrations/schema, you will add a references column to the model itself.
class CreateEmployees < ActiveRecord::Migration[5.0]
def change
create_table :employees do |t|
- t.references :manager, index: true
+ t.references :manager
t.timestamps
end
end
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 00da65b784..ad08e5a5a9 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -1266,7 +1266,7 @@ You can also pass in arbitrary local variables to any partial you are rendering
In this case, the partial will have access to a local variable `title` with the value "Products Page".
-TIP: Rails also makes a counter variable available within a partial called by the collection, named after the title of the partial followed by `_counter`. For example, when rendering a collection `@products` the partial `_product.html.erb` can access the variable `product_counter` which indexes the number of times it has been rendered within the enclosing view.
+TIP: Rails also makes a counter variable available within a partial called by the collection, named after the title of the partial followed by `_counter`. For example, when rendering a collection `@products` the partial `_product.html.erb` can access the variable `product_counter` which indexes the number of times it has been rendered within the enclosing view. Note that it also applies for when the partial name was changed by using the `as:` option. For example, the counter variable for the code above would be `item_counter`.
You can also specify a second partial to be rendered between instances of the main partial by using the `:spacer_template` option:
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 1a415449d1..eaa2353701 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Use Ids instead of memory addresses when displaying references in scaffold views.
+
+ Fixes #29200.
+
+ *Rasesh Patel*
+
* Adds support for multiple databases to `rails db:migrate:status`.
Subtasks are also added to get the status of individual databases (eg. `rails db:migrate:status:animals`).
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt
index e1ede7c713..2cf4e5c9d0 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt
@@ -16,7 +16,7 @@
<%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
<tr>
<% attributes.reject(&:password_digest?).each do |attribute| -%>
- <td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td>
+ <td><%%= <%= singular_table_name %>.<%= attribute.column_name %> %></td>
<% end -%>
<td><%%= link_to 'Show', <%= model_resource_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_route_name %>_path(<%= singular_table_name %>) %></td>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
index 5e634153be..7deba07926 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
+++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
@@ -3,7 +3,7 @@
<% attributes.reject(&:password_digest?).each do |attribute| -%>
<p>
<strong><%= attribute.human_name %>:</strong>
- <%%= @<%= singular_table_name %>.<%= attribute.name %> %>
+ <%%= @<%= singular_table_name %>.<%= attribute.column_name %> %>
</p>
<% end -%>
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index a1d91a2f98..be97abfa52 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -209,6 +209,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_new_application_doesnt_need_defaults
+ run_generator
assert_no_file "config/initializers/new_framework_defaults_6_0.rb"
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 21b5013484..3cffdddd38 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -435,8 +435,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
end
- def test_scaffold_generator_belongs_to
- run_generator ["account", "name", "currency:belongs_to"]
+ def test_scaffold_generator_belongs_to_and_references
+ run_generator ["account", "name", "currency:belongs_to", "user:references"]
assert_file "app/models/account.rb", /belongs_to :currency/
@@ -449,7 +449,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "app/controllers/accounts_controller.rb" do |content|
assert_instance_method :account_params, content do |m|
- assert_match(/permit\(:name, :currency_id\)/, m)
+ assert_match(/permit\(:name, :currency_id, :user_id\)/, m)
end
end
@@ -457,6 +457,16 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_match(/^\W{4}<%= form\.text_field :name %>/, content)
assert_match(/^\W{4}<%= form\.text_field :currency_id %>/, content)
end
+
+ assert_file "app/views/accounts/index.html.erb" do |content|
+ assert_match(/^\W{8}<td><%= account\.name %><\/td>/, content)
+ assert_match(/^\W{8}<td><%= account\.user_id %><\/td>/, content)
+ end
+
+ assert_file "app/views/accounts/show.html.erb" do |content|
+ assert_match(/^\W{2}<%= @account\.name %>/, content)
+ assert_match(/^\W{2}<%= @account\.user_id %>/, content)
+ end
end
def test_scaffold_generator_database
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index e44f21380e..d4eed69a87 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -16,6 +16,9 @@ require "active_support/testing/autorun"
require "active_support/testing/stream"
require "active_support/testing/method_call_assertions"
require "active_support/test_case"
+require "minitest/retry"
+
+Minitest::Retry.use!(verbose: false, retry_count: 1)
RAILS_FRAMEWORK_ROOT = File.expand_path("../../..", __dir__)