aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/no-response.yml9
-rw-r--r--actionpack/lib/action_controller/metal/live.rb48
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb3
-rw-r--r--activemodel/lib/active_model/dirty.rb2
-rw-r--r--activerecord/CHANGELOG.md38
-rw-r--r--activerecord/lib/active_record/associations.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb45
-rw-r--r--activerecord/lib/active_record/connection_handling.rb15
-rw-r--r--activerecord/lib/active_record/core.rb41
-rw-r--r--activerecord/lib/active_record/fixture_set/table_rows.rb3
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/test/cases/associations/eager_test.rb26
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb39
-rw-r--r--activerecord/test/cases/defaults_test.rb7
-rw-r--r--activerecord/test/cases/filter_attributes_test.rb56
-rw-r--r--activerecord/test/cases/finder_test.rb22
-rw-r--r--activerecord/test/cases/fixtures_test.rb4
-rw-r--r--activerecord/test/cases/helper.rb9
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb7
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb2
-rw-r--r--activerecord/test/models/country.rb2
-rw-r--r--activerecord/test/models/parrot.rb6
-rw-r--r--activerecord/test/models/treaty.rb2
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb3
-rw-r--r--activerecord/test/schema/schema.rb103
-rw-r--r--activesupport/CHANGELOG.md4
-rw-r--r--activesupport/lib/active_support/dependencies.rb6
-rw-r--r--activesupport/lib/active_support/parameter_filter.rb62
-rw-r--r--activesupport/lib/active_support/reloader.rb1
-rw-r--r--activesupport/lib/active_support/testing/file_fixtures.rb2
-rw-r--r--activesupport/lib/active_support/testing/method_call_assertions.rb2
-rw-r--r--activesupport/lib/active_support/testing/parallelization.rb1
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb5
-rw-r--r--activesupport/test/autoloading_fixtures/module_folder/nested_with_require.rb8
-rw-r--r--activesupport/test/autoloading_fixtures/nested_with_require_parent.rb5
-rw-r--r--activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb2
-rw-r--r--activesupport/test/dependencies/module_folder/lib_class.rb8
-rw-r--r--activesupport/test/dependencies_test.rb26
-rw-r--r--activesupport/test/parameter_filter_test.rb54
-rw-r--r--activesupport/test/testing/method_call_assertions_test.rb12
-rw-r--r--guides/bug_report_templates/action_controller_gem.rb10
-rw-r--r--guides/bug_report_templates/action_controller_master.rb7
-rw-r--r--guides/bug_report_templates/active_job_gem.rb10
-rw-r--r--guides/bug_report_templates/active_job_master.rb10
-rw-r--r--guides/bug_report_templates/active_record_gem.rb10
-rw-r--r--guides/bug_report_templates/active_record_master.rb7
-rw-r--r--guides/bug_report_templates/active_record_migrations_gem.rb10
-rw-r--r--guides/bug_report_templates/active_record_migrations_master.rb10
-rw-r--r--guides/bug_report_templates/benchmark.rb7
-rw-r--r--guides/bug_report_templates/generic_gem.rb10
-rw-r--r--guides/bug_report_templates/generic_master.rb7
-rw-r--r--guides/source/association_basics.md4
-rw-r--r--guides/source/configuring.md1
-rw-r--r--guides/source/documents.yaml22
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt1
-rw-r--r--railties/test/application/configuration_test.rb2
58 files changed, 541 insertions, 306 deletions
diff --git a/.github/no-response.yml b/.github/no-response.yml
index 7193eaa3b2..326fa84b7e 100644
--- a/.github/no-response.yml
+++ b/.github/no-response.yml
@@ -6,8 +6,7 @@ daysUntilClose: 14
responseRequiredLabel: more-information-needed
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
- This issue has been automatically closed because there has been no response
- to our request for more information from the original author. With only the
- information that is currently in the issue, we don't have enough information
- to take action. Please reach out if you have or find the answers we need so
- that we can investigate further.
+ This issue has been automatically closed because there has been no follow-up
+ response from the original author. We currently don't have enough
+ information in order to take action. Please reach out if you have any additional
+ information that will help us move this issue forward.
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index 1482b2999a..083b762f5a 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -280,33 +280,35 @@ module ActionController
raise error if error
end
- # Spawn a new thread to serve up the controller in. This is to get
- # around the fact that Rack isn't based around IOs and we need to use
- # a thread to stream data from the response bodies. Nobody should call
- # this method except in Rails internals. Seriously!
- def new_controller_thread # :nodoc:
- Thread.new {
- t2 = Thread.current
- t2.abort_on_exception = true
- yield
- }
+ def response_body=(body)
+ super
+ response.close if response
end
- def log_error(exception)
- logger = ActionController::Base.logger
- return unless logger
+ private
- logger.fatal do
- message = +"\n#{exception.class} (#{exception.message}):\n"
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
- message << " " << exception.backtrace.join("\n ")
- "#{message}\n\n"
+ # Spawn a new thread to serve up the controller in. This is to get
+ # around the fact that Rack isn't based around IOs and we need to use
+ # a thread to stream data from the response bodies. Nobody should call
+ # this method except in Rails internals. Seriously!
+ def new_controller_thread # :nodoc:
+ Thread.new {
+ t2 = Thread.current
+ t2.abort_on_exception = true
+ yield
+ }
end
- end
- def response_body=(body)
- super
- response.close if response
- end
+ def log_error(exception)
+ logger = ActionController::Base.logger
+ return unless logger
+
+ logger.fatal do
+ message = +"\n#{exception.class} (#{exception.message}):\n"
+ message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
+ message << " " << exception.backtrace.join("\n ")
+ "#{message}\n\n"
+ end
+ end
end
end
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 6e769aa560..c2caa77afb 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -1130,6 +1130,9 @@ module ActionView
# text_field(:post, :title, class: "create_input")
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
#
+ # text_field(:post, :title, maxlength: 30, class: "title_input")
+ # # => <input type="text" id="post_title" name="post[title]" maxlength="30" size="30" value="#{@post.title}" class="title_input" />
+ #
# text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>
#
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 093052a70c..f0e1242554 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -151,7 +151,7 @@ module ActiveModel
@mutations_from_database = nil
end
- # Returns +true+ if any of the attributes have unsaved changes, +false+ otherwise.
+ # Returns +true+ if any of the attributes has unsaved changes, +false+ otherwise.
#
# person.changed? # => false
# person.name = 'bob'
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 97b7ad93d1..d4f6e6ae48 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,35 @@
+* Add support for hash and url configs in database hash of `ActiveRecord::Base.connected_to`.
+
+ ````
+ User.connected_to(database: { writing: "postgres://foo" }) do
+ User.create!(name: "Gannon")
+ end
+
+ config = { "adapter" => "sqlite3", "database" => "db/readonly.sqlite3" }
+ User.connected_to(database: { reading: config }) do
+ User.count
+ end
+ ````
+
+ *Gannon McGibbon*
+
+* Support default expression for MySQL.
+
+ MySQL 8.0.13 and higher supports default value to be a function or expression.
+
+ https://dev.mysql.com/doc/refman/8.0/en/create-table.html
+
+ *Ryuta Kamizono*
+
+* Support expression indexes for MySQL.
+
+ MySQL 8.0.13 and higher supports functional key parts that index
+ expression values rather than column or column prefix values.
+
+ https://dev.mysql.com/doc/refman/8.0/en/create-index.html
+
+ *Ryuta Kamizono*
+
* Fix collection cache key with limit and custom select to avoid ambiguous timestamp column error.
Fixes #33056.
@@ -144,13 +176,13 @@
specify sensitive attributes to specific model.
```
- Rails.application.config.filter_parameters += [:credit_card_number]
- Account.last.inspect # => #<Account id: 123, name: "DHH", credit_card_number: [FILTERED] ...>
+ Rails.application.config.filter_parameters += [:credit_card_number, /phone/]
+ Account.last.inspect # => #<Account id: 123, name: "DHH", credit_card_number: [FILTERED], telephone_number: [FILTERED] ...>
SecureAccount.filter_attributes += [:name]
SecureAccount.last.inspect # => #<SecureAccount id: 42, name: [FILTERED], credit_card_number: [FILTERED] ...>
```
- *Zhang Kang*
+ *Zhang Kang*, *Yoshiyuki Kinjo*
* Deprecate `column_name_length`, `table_name_length`, `columns_per_table`,
`indexes_per_table`, `columns_per_multicolumn_index`, `sql_query_length`,
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index ab1e7ad269..fb1df00dc8 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1294,7 +1294,7 @@ module ActiveRecord
# * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
# * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
# * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
- # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records.
+ # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there are any associated records.
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
#
# If using with the <tt>:through</tt> option, the association on the join model must be
@@ -1437,7 +1437,7 @@ module ActiveRecord
# * <tt>:destroy</tt> causes the associated object to also be destroyed
# * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
# * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
- # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
+ # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there is an associated record
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
#
# Note that <tt>:dependent</tt> option is ignored when using <tt>:through</tt> option.
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index eeb88e4b7a..1e92ee3b96 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -329,14 +329,7 @@ module ActiveRecord
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
def attribute_for_inspect(attr_name)
value = read_attribute(attr_name)
-
- if value.is_a?(String) && value.length > 50
- "#{value[0, 50]}...".inspect
- elsif value.is_a?(Date) || value.is_a?(Time)
- %("#{value.to_s(:db)}")
- else
- value.inspect
- end
+ format_for_inspect(value)
end
# Returns +true+ if the specified +attribute+ has been set by the user or by a
@@ -456,6 +449,16 @@ module ActiveRecord
end
end
+ def format_for_inspect(value)
+ if value.is_a?(String) && value.length > 50
+ "#{value[0, 50]}...".inspect
+ elsif value.is_a?(Date) || value.is_a?(Time)
+ %("#{value.to_s(:db)}")
+ else
+ value.inspect
+ end
+ end
+
def readonly_attribute?(name)
self.class.readonly_attributes.include?(name)
end
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 0f73641369..13c799b64a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -72,6 +72,10 @@ module ActiveRecord
!mariadb? && version >= "8.0.1"
end
+ def supports_expression_index?
+ !mariadb? && version >= "8.0.13"
+ end
+
def supports_transaction_isolation?
true
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
index e167c01802..4894fd1c08 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
@@ -35,13 +35,39 @@ module ActiveRecord
]
end
- indexes.last[-2] << row[:Column_name]
- indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
- indexes.last[-1][:orders].merge!(row[:Column_name] => :desc) if row[:Collation] == "D"
+ if row[:Expression]
+ expression = row[:Expression]
+ expression = +"(#{expression})" unless expression.start_with?("(")
+ indexes.last[-2] << expression
+ indexes.last[-1][:expressions] ||= {}
+ indexes.last[-1][:expressions][expression] = expression
+ indexes.last[-1][:orders][expression] = :desc if row[:Collation] == "D"
+ else
+ indexes.last[-2] << row[:Column_name]
+ indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
+ indexes.last[-1][:orders][row[:Column_name]] = :desc if row[:Collation] == "D"
+ end
end
end
- indexes.map { |index| IndexDefinition.new(*index) }
+ indexes.map do |index|
+ options = index.last
+
+ if expressions = options.delete(:expressions)
+ orders = options.delete(:orders)
+ lengths = options.delete(:lengths)
+
+ columns = index[-2].map { |name|
+ [ name.to_sym, expressions[name] || +quote_column_name(name) ]
+ }.to_h
+
+ index[-2] = add_options_for_index_columns(
+ columns, order: orders, length: lengths
+ ).values.join(", ")
+ end
+
+ IndexDefinition.new(*index)
+ end
end
def remove_column(table_name, column_name, type = nil, options = {})
@@ -80,10 +106,13 @@ module ActiveRecord
def new_column_from_field(table_name, field)
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
- if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(field[:Default])
- default, default_function = nil, field[:Default]
- else
- default, default_function = field[:Default], nil
+ default, default_function = field[:Default], nil
+
+ if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
+ default, default_function = nil, default
+ elsif type_metadata.extra == "DEFAULT_GENERATED"
+ default = +"(#{default})" unless default.start_with?("(")
+ default, default_function = nil, default
end
MySQL::Column.new(
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 5141271db9..e3b59b8a22 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -109,12 +109,15 @@ module ActiveRecord
if database && role
raise ArgumentError, "connected_to can only accept a database or role argument, but not both arguments."
elsif database
- config_hash = resolve_config_for_connection(database)
- handler = lookup_connection_handler(database.to_sym)
-
- with_handler(database.to_sym) do
- handler.establish_connection(config_hash)
- return yield
+ database = { database => database } if database.is_a?(Symbol)
+ database.each do |role, database_key|
+ config_hash = resolve_config_for_connection(database_key)
+ handler = lookup_connection_handler(role.to_sym)
+
+ with_handler(role.to_sym) do
+ handler.establish_connection(config_hash)
+ return yield
+ end
end
elsif role
with_handler(role.to_sym, &blk)
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 0718688863..da3e2549a2 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -2,15 +2,13 @@
require "active_support/core_ext/hash/indifferent_access"
require "active_support/core_ext/string/filters"
+require "active_support/parameter_filter"
require "concurrent/map"
-require "set"
module ActiveRecord
module Core
extend ActiveSupport::Concern
- FILTERED = "[FILTERED]" # :nodoc:
-
included do
##
# :singleton-method:
@@ -239,9 +237,7 @@ module ActiveRecord
end
# Specifies columns which shouldn't be exposed while calling +#inspect+.
- def filter_attributes=(attributes_names)
- @filter_attributes = attributes_names.map(&:to_s).to_set
- end
+ attr_writer :filter_attributes
# Returns a string like 'Post(id:integer, title:string, body:text)'
def inspect # :nodoc:
@@ -502,11 +498,14 @@ module ActiveRecord
inspection = if defined?(@attributes) && @attributes
self.class.attribute_names.collect do |name|
if has_attribute?(name)
- if filter_attribute?(name)
- "#{name}: #{ActiveRecord::Core::FILTERED}"
+ attr = read_attribute(name)
+ value = if attr.nil?
+ attr.inspect
else
- "#{name}: #{attribute_for_inspect(name)}"
+ attr = format_for_inspect(attr)
+ inspection_filter.filter_param(name, attr)
end
+ "#{name}: #{value}"
end
end.compact.join(", ")
else
@@ -522,18 +521,16 @@ module ActiveRecord
return super if custom_inspect_method_defined?
pp.object_address_group(self) do
if defined?(@attributes) && @attributes
- column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
- pp.seplist(column_names, proc { pp.text "," }) do |column_name|
+ attr_names = self.class.attribute_names.select { |name| has_attribute?(name) }
+ pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
pp.breakable " "
pp.group(1) do
- pp.text column_name
+ pp.text attr_name
pp.text ":"
pp.breakable
- if filter_attribute?(column_name)
- pp.text ActiveRecord::Core::FILTERED
- else
- pp.pp read_attribute(column_name)
- end
+ value = read_attribute(attr_name)
+ value = inspection_filter.filter_param(attr_name, value) unless value.nil?
+ pp.pp value
end
end
else
@@ -585,8 +582,14 @@ module ActiveRecord
self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
end
- def filter_attribute?(attribute_name)
- self.class.filter_attributes.include?(attribute_name) && !read_attribute(attribute_name).nil?
+ def inspection_filter
+ @inspection_filter ||= begin
+ mask = DelegateClass(::String).new(ActiveSupport::ParameterFilter::FILTERED)
+ def mask.pretty_print(pp)
+ pp.text __getobj__
+ end
+ ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/fixture_set/table_rows.rb b/activerecord/lib/active_record/fixture_set/table_rows.rb
index e8335a2e10..3e3c0bc7ab 100644
--- a/activerecord/lib/active_record/fixture_set/table_rows.rb
+++ b/activerecord/lib/active_record/fixture_set/table_rows.rb
@@ -45,6 +45,9 @@ module ActiveRecord
# track any join tables we need to insert later
@tables = Hash.new { |h, table| h[table] = [] }
+ # ensure this table is loaded before any HABTM associations
+ @tables[table_name] = nil
+
build_table_rows_from(fixtures, config)
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 6f420fe6bb..afaa900442 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -363,7 +363,7 @@ module ActiveRecord
case conditions
when Array, Hash
- relation.where!(conditions)
+ relation.where!(conditions) unless conditions.empty?
else
relation.where!(primary_key => conditions) unless conditions == :none
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index ed7715c1e2..b37e59038e 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1618,32 +1618,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
- # Associations::Preloader#preloaders_on works with hash-like objects
- test "preloading works with an object that responds to :to_hash" do
- CustomHash = Class.new(Hash)
-
- assert_nothing_raised do
- Post.preload(CustomHash.new(comments: [{ author: :essays }])).first
- end
- end
-
- # Associations::Preloader#preloaders_on works with string-like objects
- test "preloading works with an object that responds to :to_str" do
- CustomString = Class.new(String)
-
- assert_nothing_raised do
- Post.preload(CustomString.new("comments")).first
- end
- end
-
- # Associations::Preloader#preloaders_on does not work with ranges
- test "preloading fails when Range is passed" do
- exception = assert_raises(ArgumentError) do
- Post.preload(1..10).first
- end
- assert_equal("1..10 was not recognized for preload", exception.message)
- end
-
private
def find_all_ordered(klass, include = nil)
klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a
diff --git a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
index d4e8cbee81..a520fb1303 100644
--- a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
@@ -120,6 +120,45 @@ module ActiveRecord
ENV["RAILS_ENV"] = previous_env
end
+ def test_switching_connections_with_database_url
+ previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
+ previous_url, ENV["DATABASE_URL"] = ENV["DATABASE_URL"], "postgres://localhost/foo"
+
+ ActiveRecord::Base.connected_to(database: { writing: "postgres://localhost/bar" }) do
+ handler = ActiveRecord::Base.connection_handler
+ assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
+ end
+ ensure
+ ActiveRecord::Base.establish_connection(:arunit)
+ ENV["RAILS_ENV"] = previous_env
+ ENV["DATABASE_URL"] = previous_url
+ end
+
+ def test_switching_connections_with_database_config_hash
+ previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
+ config = { "adapter" => "sqlite3", "database" => "db/readonly.sqlite3" }
+
+ ActiveRecord::Base.connected_to(database: { writing: config }) do
+ handler = ActiveRecord::Base.connection_handler
+ assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
+ end
+ ensure
+ ActiveRecord::Base.establish_connection(:arunit)
+ ENV["RAILS_ENV"] = previous_env
+ end
+
+ def test_switching_connections_with_database_symbol
+ previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
+
+ ActiveRecord::Base.connected_to(database: :arunit2) do
+ handler = ActiveRecord::Base.connection_handler
+ assert_equal handler, ActiveRecord::Base.connection_handlers[:arunit2]
+ end
+ ensure
+ ActiveRecord::Base.establish_connection(:arunit)
+ ENV["RAILS_ENV"] = previous_env
+ end
+
def test_connects_to_with_single_configuration
config = {
"development" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" },
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 0f957d41cf..5d02e59ef6 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -106,6 +106,13 @@ if current_adapter?(:Mysql2Adapter)
class MysqlDefaultExpressionTest < ActiveRecord::TestCase
include SchemaDumpingHelper
+ if supports_default_expression?
+ test "schema dump includes default expression" do
+ output = dump_table_schema("defaults")
+ assert_match %r/t\.binary\s+"uuid",\s+limit: 36,\s+default: -> { "\(uuid\(\)\)" }/i, output
+ end
+ end
+
if subsecond_precision_supported?
test "schema dump datetime includes default expression" do
output = dump_table_schema("datetime_defaults")
diff --git a/activerecord/test/cases/filter_attributes_test.rb b/activerecord/test/cases/filter_attributes_test.rb
index af5badd87d..47161a547a 100644
--- a/activerecord/test/cases/filter_attributes_test.rb
+++ b/activerecord/test/cases/filter_attributes_test.rb
@@ -4,6 +4,7 @@ require "cases/helper"
require "models/admin"
require "models/admin/user"
require "models/admin/account"
+require "models/user"
require "pp"
class FilterAttributesTest < ActiveRecord::TestCase
@@ -30,6 +31,32 @@ class FilterAttributesTest < ActiveRecord::TestCase
end
end
+ test "string filter_attributes perform pertial match" do
+ ActiveRecord::Base.filter_attributes = ["n"]
+ Admin::Account.all.each do |account|
+ assert_includes account.inspect, "name: [FILTERED]"
+ assert_equal 1, account.inspect.scan("[FILTERED]").length
+ end
+ end
+
+ test "regex filter_attributes are accepted" do
+ ActiveRecord::Base.filter_attributes = [/\An\z/]
+ account = Admin::Account.find_by(name: "37signals")
+ assert_includes account.inspect, 'name: "37signals"'
+ assert_equal 0, account.inspect.scan("[FILTERED]").length
+
+ ActiveRecord::Base.filter_attributes = [/\An/]
+ account = Admin::Account.find_by(name: "37signals")
+ assert_includes account.reload.inspect, "name: [FILTERED]"
+ assert_equal 1, account.inspect.scan("[FILTERED]").length
+ end
+
+ test "proc filter_attributes are accepted" do
+ ActiveRecord::Base.filter_attributes = [ lambda { |key, value| value.reverse! if key == "name" } ]
+ account = Admin::Account.find_by(name: "37signals")
+ assert_includes account.inspect, 'name: "slangis73"'
+ end
+
test "filter_attributes could be overwritten by models" do
Admin::Account.all.each do |account|
assert_includes account.inspect, "name: [FILTERED]"
@@ -37,7 +64,6 @@ class FilterAttributesTest < ActiveRecord::TestCase
end
begin
- previous_account_filter_attributes = Admin::Account.filter_attributes
Admin::Account.filter_attributes = []
# Above changes should not impact other models
@@ -51,7 +77,7 @@ class FilterAttributesTest < ActiveRecord::TestCase
assert_equal 0, account.inspect.scan("[FILTERED]").length
end
ensure
- Admin::Account.filter_attributes = previous_account_filter_attributes
+ Admin::Account.remove_instance_variable(:@filter_attributes)
end
end
@@ -63,6 +89,18 @@ class FilterAttributesTest < ActiveRecord::TestCase
assert_equal 0, account.inspect.scan("[FILTERED]").length
end
+ test "filter_attributes should handle [FILTERED] value properly" do
+ begin
+ User.filter_attributes = ["auth"]
+ user = User.new(token: "[FILTERED]", auth_token: "[FILTERED]")
+
+ assert_includes user.inspect, "auth_token: [FILTERED]"
+ assert_includes user.inspect, 'token: "[FILTERED]"'
+ ensure
+ User.remove_instance_variable(:@filter_attributes)
+ end
+ end
+
test "filter_attributes on pretty_print" do
user = admin_users(:david)
actual = "".dup
@@ -81,4 +119,18 @@ class FilterAttributesTest < ActiveRecord::TestCase
assert_not_includes actual, "name: [FILTERED]"
assert_equal 0, actual.scan("[FILTERED]").length
end
+
+ test "filter_attributes on pretty_print should handle [FILTERED] value properly" do
+ begin
+ User.filter_attributes = ["auth"]
+ user = User.new(token: "[FILTERED]", auth_token: "[FILTERED]")
+ actual = "".dup
+ PP.pp(user, StringIO.new(actual))
+
+ assert_includes actual, "auth_token: [FILTERED]"
+ assert_includes actual, 'token: "[FILTERED]"'
+ ensure
+ User.remove_instance_variable(:@filter_attributes)
+ end
+ end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 355fb4517f..52fd9291b2 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -20,6 +20,7 @@ require "models/matey"
require "models/dog"
require "models/car"
require "models/tyre"
+require "models/subscriber"
class FinderTest < ActiveRecord::TestCase
fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :author_addresses, :customers, :categories, :categorizations, :cars
@@ -167,6 +168,7 @@ class FinderTest < ActiveRecord::TestCase
assert_equal true, Topic.exists?(id: [1, 9999])
assert_equal false, Topic.exists?(45)
+ assert_equal false, Topic.exists?(9999999999999999999999999999999)
assert_equal false, Topic.exists?(Topic.new.id)
assert_raise(NoMethodError) { Topic.exists?([1, 2]) }
@@ -211,17 +213,23 @@ class FinderTest < ActiveRecord::TestCase
assert_equal false, relation.exists?(false)
end
+ def test_exists_with_string
+ assert_equal false, Subscriber.exists?("foo")
+ assert_equal false, Subscriber.exists?(" ")
+
+ Subscriber.create!(id: "foo")
+ Subscriber.create!(id: " ")
+
+ assert_equal true, Subscriber.exists?("foo")
+ assert_equal true, Subscriber.exists?(" ")
+ end
+
def test_exists_passing_active_record_object_is_not_permitted
assert_raises(ArgumentError) do
Topic.exists?(Topic.new)
end
end
- def test_exists_returns_false_when_parameter_has_invalid_type
- assert_equal false, Topic.exists?("foo")
- assert_equal false, Topic.exists?(("9" * 53).to_i) # number that's bigger than int
- end
-
def test_exists_does_not_select_columns_without_alias
assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
Topic.exists?
@@ -246,6 +254,10 @@ class FinderTest < ActiveRecord::TestCase
assert_equal true, Topic.first.replies.exists?
end
+ def test_exists_with_empty_hash_arg
+ assert_equal true, Topic.exists?({})
+ end
+
# Ensure +exists?+ runs without an error by excluding distinct value.
# See https://github.com/rails/rails/pull/26981.
def test_exists_with_order_and_distinct
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 82ca15b415..1092b9553f 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -619,6 +619,10 @@ class HasManyThroughFixture < ActiveRecord::TestCase
assert_equal load_has_and_belongs_to_many["parrots_treasures"], rows["parrot_treasures"]
end
+ def test_has_and_belongs_to_many_order
+ assert_equal ["parrots", "parrots_treasures"], load_has_and_belongs_to_many.keys
+ end
+
def load_has_and_belongs_to_many
parrot = make_model "Parrot"
parrot.has_and_belongs_to_many :treasures
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 68be685e4b..730cd663a2 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -48,6 +48,15 @@ def mysql_enforcing_gtid_consistency?
current_adapter?(:Mysql2Adapter) && "ON" == ActiveRecord::Base.connection.show_variable("enforce_gtid_consistency")
end
+def supports_default_expression?
+ if current_adapter?(:PostgreSQLAdapter)
+ true
+ elsif current_adapter?(:Mysql2Adapter)
+ conn = ActiveRecord::Base.connection
+ !conn.mariadb? && conn.version >= "8.0.13"
+ end
+end
+
def supports_savepoints?
ActiveRecord::Base.connection.supports_savepoints?
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index db13f20a39..dda3efa47c 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -229,11 +229,14 @@ class SchemaDumperTest < ActiveRecord::TestCase
if ActiveRecord::Base.connection.supports_expression_index?
def test_schema_dump_expression_indices
index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_expression_index/).first.strip
+ index_definition.sub!(/, name: "company_expression_index"\z/, "")
if current_adapter?(:PostgreSQLAdapter)
- assert_match %r{CASE.+lower\(\(name\)::text\)}i, index_definition
+ assert_match %r{CASE.+lower\(\(name\)::text\).+END\) DESC"\z}i, index_definition
+ elsif current_adapter?(:Mysql2Adapter)
+ assert_match %r{CASE.+lower\(`name`\).+END\) DESC"\z}i, index_definition
elsif current_adapter?(:SQLite3Adapter)
- assert_match %r{CASE.+lower\(name\)}i, index_definition
+ assert_match %r{CASE.+lower\(name\).+END\) DESC"\z}i, index_definition
else
assert false
end
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 4d6dff68f9..552e623fd4 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -272,7 +272,7 @@ if current_adapter?(:Mysql2Adapter)
def test_db_retrieves_collation
ActiveRecord::Base.stub(:connection, @connection) do
- assert_called_with(@connection, :collation) do
+ assert_called(@connection, :collation) do
ActiveRecord::Tasks::DatabaseTasks.collation @configuration
end
end
diff --git a/activerecord/test/models/country.rb b/activerecord/test/models/country.rb
index 0c84a40de2..4b4a276a98 100644
--- a/activerecord/test/models/country.rb
+++ b/activerecord/test/models/country.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
class Country < ActiveRecord::Base
- self.primary_key = :country_id
-
has_and_belongs_to_many :treaties
end
diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb
index ba9ddb8c6a..3bb5316eca 100644
--- a/activerecord/test/models/parrot.rb
+++ b/activerecord/test/models/parrot.rb
@@ -20,6 +20,12 @@ class Parrot < ActiveRecord::Base
def increment_updated_count
self.updated_count += 1
end
+
+ def self.delete_all(*)
+ connection.delete("DELETE FROM parrots_pirates")
+ connection.delete("DELETE FROM parrots_treasures")
+ super
+ end
end
class LiveParrot < Parrot
diff --git a/activerecord/test/models/treaty.rb b/activerecord/test/models/treaty.rb
index 5c1d75aa09..b87a757d2a 100644
--- a/activerecord/test/models/treaty.rb
+++ b/activerecord/test/models/treaty.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
class Treaty < ActiveRecord::Base
- self.primary_key = :treaty_id
-
has_and_belongs_to_many :countries
end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 499280cb0c..ccca9a2d9b 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -19,6 +19,9 @@ ActiveRecord::Schema.define do
t.datetime :fixed_time, default: "2004-01-01 00:00:00"
t.column :char1, "char(1)", default: "Y"
t.string :char2, limit: 50, default: "a varchar field"
+ if supports_default_expression?
+ t.binary :uuid, limit: 36, default: -> { "(uuid())" }
+ end
end
create_table :binary_fields, force: true do |t|
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 4ef463fdad..7034c773d2 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -216,7 +216,7 @@ ActiveRecord::Schema.define do
t.index [:firm_id, :type, :rating], name: "company_index", length: { type: 10 }, order: { rating: :desc }
t.index [:firm_id, :type], name: "company_partial_index", where: "(rating > 10)"
t.index :name, name: "company_name_index", using: :btree
- t.index "(CASE WHEN rating > 0 THEN lower(name) END)", name: "company_expression_index" if supports_expression_index?
+ t.index "(CASE WHEN rating > 0 THEN lower(name) END) DESC", name: "company_expression_index" if supports_expression_index?
end
create_table :content, force: true do |t|
@@ -601,33 +601,55 @@ ActiveRecord::Schema.define do
t.integer :non_poly_two_id
end
- create_table :parrots, force: true do |t|
- t.column :name, :string
- t.column :color, :string
- t.column :parrot_sti_class, :string
- t.column :killer_id, :integer
- t.column :updated_count, :integer, default: 0
- if subsecond_precision_supported?
- t.column :created_at, :datetime, precision: 0
- t.column :created_on, :datetime, precision: 0
- t.column :updated_at, :datetime, precision: 0
- t.column :updated_on, :datetime, precision: 0
- else
- t.column :created_at, :datetime
- t.column :created_on, :datetime
- t.column :updated_at, :datetime
- t.column :updated_on, :datetime
+ disable_referential_integrity do
+ create_table :parrots, force: :cascade do |t|
+ t.string :name
+ t.string :color
+ t.string :parrot_sti_class
+ t.integer :killer_id
+ t.integer :updated_count, :integer, default: 0
+ if subsecond_precision_supported?
+ t.datetime :created_at, precision: 0
+ t.datetime :created_on, precision: 0
+ t.datetime :updated_at, precision: 0
+ t.datetime :updated_on, precision: 0
+ else
+ t.datetime :created_at
+ t.datetime :created_on
+ t.datetime :updated_at
+ t.datetime :updated_on
+ end
end
- end
- create_table :parrots_pirates, id: false, force: true do |t|
- t.column :parrot_id, :integer
- t.column :pirate_id, :integer
- end
+ create_table :pirates, force: :cascade do |t|
+ t.string :catchphrase
+ t.integer :parrot_id
+ t.integer :non_validated_parrot_id
+ if subsecond_precision_supported?
+ t.datetime :created_on, precision: 6
+ t.datetime :updated_on, precision: 6
+ else
+ t.datetime :created_on
+ t.datetime :updated_on
+ end
+ end
- create_table :parrots_treasures, id: false, force: true do |t|
- t.column :parrot_id, :integer
- t.column :treasure_id, :integer
+ create_table :treasures, force: :cascade do |t|
+ t.string :name
+ t.string :type
+ t.references :looter, polymorphic: true
+ t.references :ship
+ end
+
+ create_table :parrots_pirates, id: false, force: true do |t|
+ t.references :parrot, foreign_key: true
+ t.references :pirate, foreign_key: true
+ end
+
+ create_table :parrots_treasures, id: false, force: true do |t|
+ t.references :parrot, foreign_key: true
+ t.references :treasure, foreign_key: true
+ end
end
create_table :people, force: true do |t|
@@ -673,19 +695,6 @@ ActiveRecord::Schema.define do
t.column :rainbow_color, :string
end
- create_table :pirates, force: true do |t|
- t.column :catchphrase, :string
- t.column :parrot_id, :integer
- t.integer :non_validated_parrot_id
- if subsecond_precision_supported?
- t.column :created_on, :datetime, precision: 6
- t.column :updated_on, :datetime, precision: 6
- else
- t.column :created_on, :datetime
- t.column :updated_on, :datetime
- end
- end
-
create_table :posts, force: true do |t|
t.references :author
t.string :title, null: false
@@ -916,14 +925,6 @@ ActiveRecord::Schema.define do
t.datetime :updated_at
end
- create_table :treasures, force: true do |t|
- t.column :name, :string
- t.column :type, :string
- t.column :looter_id, :integer
- t.column :looter_type, :string
- t.belongs_to :ship
- end
-
create_table :tuning_pegs, force: true do |t|
t.integer :guitar_id
t.float :pitch
@@ -983,14 +984,16 @@ ActiveRecord::Schema.define do
t.references :wheelable, polymorphic: true
end
- create_table :countries, force: true, id: false, primary_key: "country_id" do |t|
- t.string :country_id
+ create_table :countries, force: true, id: false do |t|
+ t.string :country_id, primary_key: true
t.string :name
end
- create_table :treaties, force: true, id: false, primary_key: "treaty_id" do |t|
- t.string :treaty_id
+
+ create_table :treaties, force: true, id: false do |t|
+ t.string :treaty_id, primary_key: true
t.string :name
end
+
create_table :countries_treaties, force: true, primary_key: [:country_id, :treaty_id] do |t|
t.string :country_id, null: false
t.string :treaty_id, null: false
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index a53e4eb5a2..92f5f64719 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Improve the logic that detects non-autoloaded constants.
+
+ *Jan Habermann*, *Xavier Noria*
+
* Deprecate `ActiveSupport::Multibyte::Unicode#pack_graphemes(array)` and `ActiveSuppport::Multibyte::Unicode#unpack_graphemes(string)`
in favor of `array.flatten.pack("U*")` and `string.scan(/\X/).map(&:codepoints)`, respectively.
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 66e0bea00e..d5d00b5e6e 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -103,6 +103,8 @@ module ActiveSupport #:nodoc:
# parent.rb then requires namespace/child.rb, the stack will look like
# [[Object], [Namespace]].
+ attr_reader :watching
+
def initialize
@watching = []
@stack = Hash.new { |h, k| h[k] = [] }
@@ -254,7 +256,9 @@ module ActiveSupport #:nodoc:
def load_dependency(file)
if Dependencies.load? && Dependencies.constant_watch_stack.watching?
- Dependencies.new_constants_in(Object) { yield }
+ descs = Dependencies.constant_watch_stack.watching.flatten.uniq
+
+ Dependencies.new_constants_in(*descs) { yield }
else
yield
end
diff --git a/activesupport/lib/active_support/parameter_filter.rb b/activesupport/lib/active_support/parameter_filter.rb
index 59945e9daa..1389d82523 100644
--- a/activesupport/lib/active_support/parameter_filter.rb
+++ b/activesupport/lib/active_support/parameter_filter.rb
@@ -28,22 +28,36 @@ module ActiveSupport
class ParameterFilter
FILTERED = "[FILTERED]" # :nodoc:
- def initialize(filters = [])
+ # Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
+ # Other types of filters are treated as +String+ using +to_s+.
+ # For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
+ #
+ # ==== Options
+ #
+ # * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
+ def initialize(filters = [], mask: FILTERED)
@filters = filters
+ @mask = mask
end
+ # Mask value of +params+ if key matches one of filters.
def filter(params)
compiled_filter.call(params)
end
+ # Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
+ def filter_param(key, value)
+ @filters.empty? ? value : compiled_filter.value_for_key(key, value)
+ end
+
private
def compiled_filter
- @compiled_filter ||= CompiledFilter.compile(@filters)
+ @compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
end
class CompiledFilter # :nodoc:
- def self.compile(filters)
+ def self.compile(filters, mask:)
return lambda { |params| params.dup } if filters.empty?
strings, regexps, blocks = [], [], []
@@ -65,42 +79,46 @@ module ActiveSupport
regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
- new regexps, deep_regexps, blocks
+ new regexps, deep_regexps, blocks, mask: mask
end
attr_reader :regexps, :deep_regexps, :blocks
- def initialize(regexps, deep_regexps, blocks)
+ def initialize(regexps, deep_regexps, blocks, mask:)
@regexps = regexps
@deep_regexps = deep_regexps.any? ? deep_regexps : nil
@blocks = blocks
+ @mask = mask
end
def call(params, parents = [], original_params = params)
filtered_params = params.class.new
params.each do |key, value|
- parents.push(key) if deep_regexps
- if regexps.any? { |r| key =~ r }
- value = FILTERED
- elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| joined =~ r }
- value = FILTERED
- elsif value.is_a?(Hash)
- value = call(value, parents, original_params)
- elsif value.is_a?(Array)
- value = value.map { |v| v.is_a?(Hash) ? call(v, parents, original_params) : v }
- elsif blocks.any?
- key = key.dup if key.duplicable?
- value = value.dup if value.duplicable?
- blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
- end
- parents.pop if deep_regexps
-
- filtered_params[key] = value
+ filtered_params[key] = value_for_key(key, value, parents, original_params)
end
filtered_params
end
+
+ def value_for_key(key, value, parents = [], original_params = nil)
+ parents.push(key) if deep_regexps
+ if regexps.any? { |r| r.match?(key) }
+ value = @mask
+ elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
+ value = @mask
+ elsif value.is_a?(Hash)
+ value = call(value, parents, original_params)
+ elsif value.is_a?(Array)
+ value = value.map { |v| v.is_a?(Hash) ? call(v, parents, original_params) : v }
+ elsif blocks.any?
+ key = key.dup if key.duplicable?
+ value = value.dup if value.duplicable?
+ blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
+ end
+ parents.pop if deep_regexps
+ value
+ end
end
end
end
diff --git a/activesupport/lib/active_support/reloader.rb b/activesupport/lib/active_support/reloader.rb
index b26d9c3665..fea18e9712 100644
--- a/activesupport/lib/active_support/reloader.rb
+++ b/activesupport/lib/active_support/reloader.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/execution_wrapper"
+require "active_support/executor"
module ActiveSupport
#--
diff --git a/activesupport/lib/active_support/testing/file_fixtures.rb b/activesupport/lib/active_support/testing/file_fixtures.rb
index ad923d1aab..4eb7a88576 100644
--- a/activesupport/lib/active_support/testing/file_fixtures.rb
+++ b/activesupport/lib/active_support/testing/file_fixtures.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/concern"
+
module ActiveSupport
module Testing
# Adds simple access to sample files called file fixtures.
diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb
index fdc70e1cd3..eba41aa907 100644
--- a/activesupport/lib/active_support/testing/method_call_assertions.rb
+++ b/activesupport/lib/active_support/testing/method_call_assertions.rb
@@ -17,7 +17,7 @@ module ActiveSupport
assert_equal times, times_called, error
end
- def assert_called_with(object, method_name, args = [], returns: nil)
+ def assert_called_with(object, method_name, args, returns: nil)
mock = Minitest::Mock.new
if args.all? { |arg| arg.is_a?(Array) }
diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb
index beeb470659..c03a07873d 100644
--- a/activesupport/lib/active_support/testing/parallelization.rb
+++ b/activesupport/lib/active_support/testing/parallelization.rb
@@ -2,6 +2,7 @@
require "drb"
require "drb/unix"
+require "active_support/core_ext/module/attribute_accessors"
module ActiveSupport
module Testing
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index fd07a3a6a2..d9e033e23b 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -182,8 +182,9 @@ module ActiveSupport
"Samoa" => "Pacific/Apia"
}
- UTC_OFFSET_WITH_COLON = "%s%02d:%02d"
- UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "")
+ UTC_OFFSET_WITH_COLON = "%s%02d:%02d" # :nodoc:
+ UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "") # :nodoc:
+ private_constant :UTC_OFFSET_WITH_COLON, :UTC_OFFSET_WITHOUT_COLON
@lazy_zones_map = Concurrent::Map.new
@country_zones = Concurrent::Map.new
diff --git a/activesupport/test/autoloading_fixtures/module_folder/nested_with_require.rb b/activesupport/test/autoloading_fixtures/module_folder/nested_with_require.rb
new file mode 100644
index 0000000000..f9d6e675d7
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/module_folder/nested_with_require.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require "dependencies/module_folder/lib_class"
+
+module ModuleFolder
+ class NestedWithRequire
+ end
+end
diff --git a/activesupport/test/autoloading_fixtures/nested_with_require_parent.rb b/activesupport/test/autoloading_fixtures/nested_with_require_parent.rb
new file mode 100644
index 0000000000..e8fb321077
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/nested_with_require_parent.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class NestedWithRequireParent
+ ModuleFolder::NestedWithRequire
+end
diff --git a/activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb b/activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb
index 4e8ff60eb3..a4abdd37b9 100644
--- a/activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_instrumentation_behavior.rb
@@ -2,7 +2,7 @@
module CacheInstrumentationBehavior
def test_fetch_multi_uses_write_multi_entries_store_provider_interface
- assert_called_with(@cache, :write_multi_entries) do
+ assert_called(@cache, :write_multi_entries) do
@cache.fetch_multi "a", "b", "c" do |key|
key * 2
end
diff --git a/activesupport/test/dependencies/module_folder/lib_class.rb b/activesupport/test/dependencies/module_folder/lib_class.rb
new file mode 100644
index 0000000000..c6b52610c1
--- /dev/null
+++ b/activesupport/test/dependencies/module_folder/lib_class.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+ConstFromLib = 1
+
+module ModuleFolder
+ class LibClass
+ end
+end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index e144971e9f..9f2755a25c 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -282,6 +282,32 @@ class DependenciesTest < ActiveSupport::TestCase
remove_constants(:ModuleFolder)
end
+ def test_module_with_nested_class_requiring_lib_class
+ with_autoloading_fixtures do
+ ModuleFolder::NestedWithRequire
+
+ assert defined?(ModuleFolder::LibClass)
+ assert_not ActiveSupport::Dependencies.autoloaded_constants.include?("ModuleFolder::LibClass")
+ assert_not ActiveSupport::Dependencies.autoloaded_constants.include?("ConstFromLib")
+ end
+ ensure
+ remove_constants(:ModuleFolder)
+ remove_constants(:ConstFromLib)
+ end
+
+ def test_module_with_nested_class_and_parent_requiring_lib_class
+ with_autoloading_fixtures do
+ NestedWithRequireParent
+
+ assert defined?(ModuleFolder::LibClass)
+ assert_not ActiveSupport::Dependencies.autoloaded_constants.include?("ModuleFolder::LibClass")
+ assert_not ActiveSupport::Dependencies.autoloaded_constants.include?("ConstFromLib")
+ end
+ ensure
+ remove_constants(:ModuleFolder)
+ remove_constants(:ConstFromLib)
+ end
+
def test_directories_may_manifest_as_nested_classes
with_autoloading_fixtures do
assert_kind_of Class, ClassFolder
diff --git a/activesupport/test/parameter_filter_test.rb b/activesupport/test/parameter_filter_test.rb
index 3403a3188b..d2dc71061d 100644
--- a/activesupport/test/parameter_filter_test.rb
+++ b/activesupport/test/parameter_filter_test.rb
@@ -36,6 +36,51 @@ class ParameterFilterTest < ActiveSupport::TestCase
end
end
+ test "filter should return mask option when value is filtered" do
+ mask = Object.new.freeze
+ test_hashes = [
+ [{ "foo" => "bar" }, { "foo" => "bar" }, %w'food'],
+ [{ "foo" => "bar" }, { "foo" => mask }, %w'foo'],
+ [{ "foo" => "bar", "bar" => "foo" }, { "foo" => mask, "bar" => "foo" }, %w'foo baz'],
+ [{ "foo" => "bar", "baz" => "foo" }, { "foo" => mask, "baz" => mask }, %w'foo baz'],
+ [{ "bar" => { "foo" => "bar", "bar" => "foo" } }, { "bar" => { "foo" => mask, "bar" => "foo" } }, %w'fo'],
+ [{ "foo" => { "foo" => "bar", "bar" => "foo" } }, { "foo" => mask }, %w'f banana'],
+ [{ "deep" => { "cc" => { "code" => "bar", "bar" => "foo" }, "ss" => { "code" => "bar" } } }, { "deep" => { "cc" => { "code" => mask, "bar" => "foo" }, "ss" => { "code" => "bar" } } }, %w'deep.cc.code'],
+ [{ "baz" => [{ "foo" => "baz" }, "1"] }, { "baz" => [{ "foo" => mask }, "1"] }, [/foo/]]]
+
+ test_hashes.each do |before_filter, after_filter, filter_words|
+ parameter_filter = ActiveSupport::ParameterFilter.new(filter_words, mask: mask)
+ assert_equal after_filter, parameter_filter.filter(before_filter)
+
+ filter_words << "blah"
+ filter_words << lambda { |key, value|
+ value.reverse! if key =~ /bargain/
+ }
+ filter_words << lambda { |key, value, original_params|
+ value.replace("world!") if original_params["barg"]["blah"] == "bar" && key == "hello"
+ }
+
+ parameter_filter = ActiveSupport::ParameterFilter.new(filter_words, mask: mask)
+ before_filter["barg"] = { :bargain => "gain", "blah" => "bar", "bar" => { "bargain" => { "blah" => "foo", "hello" => "world" } } }
+ after_filter["barg"] = { :bargain => "niag", "blah" => mask, "bar" => { "bargain" => { "blah" => mask, "hello" => "world!" } } }
+
+ assert_equal after_filter, parameter_filter.filter(before_filter)
+ end
+ end
+
+ test "filter_param" do
+ parameter_filter = ActiveSupport::ParameterFilter.new(["foo", /bar/])
+ assert_equal "[FILTERED]", parameter_filter.filter_param("food", "secret vlaue")
+ assert_equal "[FILTERED]", parameter_filter.filter_param("baz.foo", "secret vlaue")
+ assert_equal "[FILTERED]", parameter_filter.filter_param("barbar", "secret vlaue")
+ assert_equal "non secret value", parameter_filter.filter_param("baz", "non secret value")
+ end
+
+ test "filter_param can work with empty filters" do
+ parameter_filter = ActiveSupport::ParameterFilter.new
+ assert_equal "bar", parameter_filter.filter_param("foo", "bar")
+ end
+
test "parameter filter should maintain hash with indifferent access" do
test_hashes = [
[{ "foo" => "bar" }.with_indifferent_access, ["blah"]],
@@ -48,4 +93,13 @@ class ParameterFilterTest < ActiveSupport::TestCase
parameter_filter.filter(before_filter)
end
end
+
+ test "filter_param should return mask option when value is filtered" do
+ mask = Object.new.freeze
+ parameter_filter = ActiveSupport::ParameterFilter.new(["foo", /bar/], mask: mask)
+ assert_equal mask, parameter_filter.filter_param("food", "secret vlaue")
+ assert_equal mask, parameter_filter.filter_param("baz.foo", "secret vlaue")
+ assert_equal mask, parameter_filter.filter_param("barbar", "secret vlaue")
+ assert_equal "non secret value", parameter_filter.filter_param("baz", "non secret value")
+ end
end
diff --git a/activesupport/test/testing/method_call_assertions_test.rb b/activesupport/test/testing/method_call_assertions_test.rb
index 7438a0490e..669463bd31 100644
--- a/activesupport/test/testing/method_call_assertions_test.rb
+++ b/activesupport/test/testing/method_call_assertions_test.rb
@@ -60,12 +60,6 @@ class MethodCallAssertionsTest < ActiveSupport::TestCase
assert_match(/dang it.\nExpected increment/, error.message)
end
- def test_assert_called_with
- assert_called_with(@object, :increment) do
- @object.increment
- end
- end
-
def test_assert_called_with_arguments
assert_called_with(@object, :<<, [ 2 ]) do
@object << 2
@@ -88,12 +82,6 @@ class MethodCallAssertionsTest < ActiveSupport::TestCase
end
end
- def test_assert_called_with_returns
- assert_called_with(@object, :increment, returns: 1) do
- @object.increment
- end
- end
-
def test_assert_called_with_multiple_expected_arguments
assert_called_with(@object, :<<, [ [ 1 ], [ 2 ] ]) do
@object << 1
diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb
index f339635fb7..6c74200761 100644
--- a/guides/bug_report_templates/action_controller_gem.rb
+++ b/guides/bug_report_templates/action_controller_gem.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
@@ -42,9 +37,6 @@ end
require "minitest/autorun"
-# Ensure backward compatibility with minitest 4.
-Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
-
class BugTest < Minitest::Test
include Rack::Test::Methods
diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb
index ffd81c0079..6d53e957d9 100644
--- a/guides/bug_report_templates/action_controller_master.rb
+++ b/guides/bug_report_templates/action_controller_master.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
diff --git a/guides/bug_report_templates/active_job_gem.rb b/guides/bug_report_templates/active_job_gem.rb
index b260f0835b..eb9d1316e9 100644
--- a/guides/bug_report_templates/active_job_gem.rb
+++ b/guides/bug_report_templates/active_job_gem.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
@@ -19,9 +14,6 @@ end
require "minitest/autorun"
require "active_job"
-# Ensure backward compatibility with minitest 4.
-Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
-
class BuggyJob < ActiveJob::Base
def perform
puts "performed"
diff --git a/guides/bug_report_templates/active_job_master.rb b/guides/bug_report_templates/active_job_master.rb
index 894581da96..ae3ef7752f 100644
--- a/guides/bug_report_templates/active_job_master.rb
+++ b/guides/bug_report_templates/active_job_master.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
@@ -18,9 +13,6 @@ end
require "active_job"
require "minitest/autorun"
-# Ensure backward compatibility with minitest 4.
-Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
-
class BuggyJob < ActiveJob::Base
def perform
puts "performed"
diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb
index 5f70dbbe69..d88304a219 100644
--- a/guides/bug_report_templates/active_record_gem.rb
+++ b/guides/bug_report_templates/active_record_gem.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
@@ -21,9 +16,6 @@ require "active_record"
require "minitest/autorun"
require "logger"
-# Ensure backward compatibility with minitest 4.
-Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
-
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb
index 914f04f51a..780456b7b6 100644
--- a/guides/bug_report_templates/active_record_master.rb
+++ b/guides/bug_report_templates/active_record_master.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb
index 7f7359fa78..5dfd49fb38 100644
--- a/guides/bug_report_templates/active_record_migrations_gem.rb
+++ b/guides/bug_report_templates/active_record_migrations_gem.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
@@ -21,9 +16,6 @@ require "active_record"
require "minitest/autorun"
require "logger"
-# Ensure backward compatibility with minitest 4.
-Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
-
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb
index 106d94491c..b0fe3bc660 100644
--- a/guides/bug_report_templates/active_record_migrations_master.rb
+++ b/guides/bug_report_templates/active_record_migrations_master.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
@@ -20,9 +15,6 @@ require "active_record"
require "minitest/autorun"
require "logger"
-# Ensure backward compatibility with minitest 4.
-Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
-
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
diff --git a/guides/bug_report_templates/benchmark.rb b/guides/bug_report_templates/benchmark.rb
index 046572148b..4a8ce787c7 100644
--- a/guides/bug_report_templates/benchmark.rb
+++ b/guides/bug_report_templates/benchmark.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb
index aec5bf0577..3fd54437f7 100644
--- a/guides/bug_report_templates/generic_gem.rb
+++ b/guides/bug_report_templates/generic_gem.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
@@ -20,9 +15,6 @@ require "active_support"
require "active_support/core_ext/object/blank"
require "minitest/autorun"
-# Ensure backward compatibility with minitest 4.
-Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
-
class BugTest < Minitest::Test
def test_stuff
assert "zomg".present?
diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb
index 727f428960..ec65fee292 100644
--- a/guides/bug_report_templates/generic_master.rb
+++ b/guides/bug_report_templates/generic_master.rb
@@ -1,11 +1,6 @@
# frozen_string_literal: true
-begin
- require "bundler/inline"
-rescue LoadError => e
- $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
- raise e
-end
+require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 78a1f47407..f75517c5ab 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1258,7 +1258,7 @@ Controls what happens to the associated object when its owner is destroyed:
* `:destroy` causes the associated object to also be destroyed
* `:delete` causes the associated object to be deleted directly from the database (so callbacks will not execute)
* `:nullify` causes the foreign key to be set to `NULL`. Callbacks are not executed.
-* `:restrict_with_exception` causes an exception to be raised if there is an associated record
+* `:restrict_with_exception` causes an `ActiveRecord::DeleteRestrictionError` exception to be raised if there is an associated record
* `:restrict_with_error` causes an error to be added to the owner if there is an associated object
It's necessary not to set or leave `:nullify` option for those associations
@@ -1659,7 +1659,7 @@ Controls what happens to the associated objects when their owner is destroyed:
* `:destroy` causes all the associated objects to also be destroyed
* `:delete_all` causes all the associated objects to be deleted directly from the database (so callbacks will not execute)
* `:nullify` causes the foreign keys to be set to `NULL`. Callbacks are not executed.
-* `:restrict_with_exception` causes an exception to be raised if there are any associated records
+* `:restrict_with_exception` causes an `ActiveRecord::DeleteRestrictionError` exception to be raised if there are any associated records
* `:restrict_with_error` causes an error to be added to the owner if there are any associated objects
##### `:foreign_key`
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 61bb35cf93..d03943ba4a 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -1011,6 +1011,7 @@ If you choose to use MySQL or MariaDB instead of the shipped SQLite3 database, y
```yaml
development:
adapter: mysql2
+ encoding: utf8mb4
database: blog_development
pool: 5
username: root
diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml
index 551179c523..25c159d471 100644
--- a/guides/source/documents.yaml
+++ b/guides/source/documents.yaml
@@ -207,46 +207,46 @@
url: 6_0_release_notes.html
description: Release notes for Rails 6.0.
-
- name: 5.2 Release Notes
+ name: Version 5.2 - April 2018
url: 5_2_release_notes.html
description: Release notes for Rails 5.2.
-
- name: 5.1 Release Notes
+ name: Version 5.1 - April 2017
url: 5_1_release_notes.html
description: Release notes for Rails 5.1.
-
- name: 5.0 Release Notes
+ name: Version 5.0 - June 2016
url: 5_0_release_notes.html
description: Release notes for Rails 5.0.
-
- name: 4.2 Release Notes
+ name: Version 4.2 - December 2014
url: 4_2_release_notes.html
description: Release notes for Rails 4.2.
-
- name: 4.1 Release Notes
+ name: Version 4.1 - April 2014
url: 4_1_release_notes.html
description: Release notes for Rails 4.1.
-
- name: 4.0 Release Notes
+ name: Version 4.0 - June 2013
url: 4_0_release_notes.html
description: Release notes for Rails 4.0.
-
- name: 3.2 Release Notes
+ name: Version 3.2 - January 2012
url: 3_2_release_notes.html
description: Release notes for Rails 3.2.
-
- name: 3.1 Release Notes
+ name: Version 3.1 - August 2011
url: 3_1_release_notes.html
description: Release notes for Rails 3.1.
-
- name: 3.0 Release Notes
+ name: Version 3.0 - August 2010
url: 3_0_release_notes.html
description: Release notes for Rails 3.0.
-
- name: 2.3 Release Notes
+ name: Version 2.3 - March 2009
url: 2_3_release_notes.html
description: Release notes for Rails 2.3.
-
- name: 2.2 Release Notes
+ name: Version 2.2 - November 2008
url: 2_2_release_notes.html
description: Release notes for Rails 2.2.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt
index 5860563908..b6c2e7448a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt
@@ -11,6 +11,7 @@
#
default: &default
adapter: mysql2
+ encoding: utf8mb4
pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 9b01d42b1e..fa418f564b 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -2109,7 +2109,7 @@ module ApplicationTests
RUBY
app "development"
assert_equal [ :password, :credit_card_number ], Rails.application.config.filter_parameters
- assert_equal [ "password", "credit_card_number" ].to_set, ActiveRecord::Base.filter_attributes
+ assert_equal [ :password, :credit_card_number ], ActiveRecord::Base.filter_attributes
end
test "ActiveStorage.routes_prefix can be configured via config.active_storage.routes_prefix" do