aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md16
-rw-r--r--activerecord/Rakefile4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/core.rb40
-rw-r--r--activerecord/lib/active_record/railtie.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb4
-rw-r--r--activerecord/test/cases/filter_attributes_test.rb32
-rw-r--r--activerecord/test/config.example.yml6
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb2
10 files changed, 71 insertions, 43 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 4487d70ce9..0bb5dfe313 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,12 +1,24 @@
+* Use MySQL utf8mb4 character set by default.
+
+ `utf8mb4` character set with 4-Byte encoding supports supplementary characters including emoji.
+ The previous default 3-Byte encoding character set `utf8` is not enough to support them.
+
+ *Yasuo Honda*
+
* Fix duplicated record creation when using nested attributes with `create_with`.
*Darwin Wu*
-* Configuration item `config.filter_parameters` could also filter out sensitive value of database column when call `#inspect`.
+* Configuration item `config.filter_parameters` could also filter out
+ sensitive values of database columns when call `#inspect`.
+ We also added `ActiveRecord::Base::filter_attributes`/`=` in order to
+ specify sensitive attributes to specific model.
```
Rails.application.config.filter_parameters += [:credit_card_number]
- Account.last.inspect # => #<Account id: 123, credit_card_number: [FILTERED] ...>
+ Account.last.inspect # => #<Account id: 123, name: "DHH", credit_card_number: [FILTERED] ...>
+ SecureAccount.filter_attributes += [:name]
+ SecureAccount.last.inspect # => #<SecureAccount id: 42, name: [FILTERED], credit_card_number: [FILTERED] ...>
```
*Zhang Kang*
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 170c95b827..fae56a51bb 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -94,8 +94,8 @@ namespace :db do
desc "Build the MySQL test databases"
task :build do
config = ARTest.config["connections"]["mysql2"]
- %x( mysql --user=#{config["arunit"]["username"]} --password=#{config["arunit"]["password"]} -e "create DATABASE #{config["arunit"]["database"]} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci ")
- %x( mysql --user=#{config["arunit2"]["username"]} --password=#{config["arunit2"]["password"]} -e "create DATABASE #{config["arunit2"]["database"]} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci ")
+ %x( mysql --user=#{config["arunit"]["username"]} --password=#{config["arunit"]["password"]} -e "create DATABASE #{config["arunit"]["database"]} DEFAULT CHARACTER SET utf8mb4" )
+ %x( mysql --user=#{config["arunit2"]["username"]} --password=#{config["arunit2"]["password"]} -e "create DATABASE #{config["arunit2"]["database"]} DEFAULT CHARACTER SET utf8mb4" )
end
desc "Drop the MySQL test databases"
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 88fff83a9e..2d287d56e8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -241,7 +241,7 @@ module ActiveRecord
end
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
- # Charset defaults to utf8.
+ # Charset defaults to utf8mb4.
#
# Example:
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
@@ -251,7 +251,7 @@ module ActiveRecord
if options[:collation]
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
else
- execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8mb4')}"
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 82cf7563a2..392602bc0f 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -3,6 +3,7 @@
require "active_support/core_ext/hash/indifferent_access"
require "active_support/core_ext/string/filters"
require "concurrent/map"
+require "set"
module ActiveRecord
module Core
@@ -125,9 +126,7 @@ module ActiveRecord
class_attribute :default_connection_handler, instance_writer: false
- ##
- # Specifies columns which don't want to be exposed while calling #inspect
- class_attribute :filter_attributes, instance_writer: false, default: []
+ self.filter_attributes = []
def self.connection_handler
ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
@@ -140,7 +139,7 @@ module ActiveRecord
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
end
- module ClassMethods # :nodoc:
+ module ClassMethods
def initialize_find_by_cache # :nodoc:
@find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
end
@@ -217,7 +216,7 @@ module ActiveRecord
generated_association_methods
end
- def generated_association_methods
+ def generated_association_methods # :nodoc:
@generated_association_methods ||= begin
mod = const_set(:GeneratedAssociationMethods, Module.new)
private_constant :GeneratedAssociationMethods
@@ -227,8 +226,22 @@ module ActiveRecord
end
end
+ # Returns columns which shouldn't be exposed while calling +#inspect+.
+ def filter_attributes
+ if defined?(@filter_attributes)
+ @filter_attributes
+ else
+ superclass.filter_attributes
+ end
+ 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
+
# Returns a string like 'Post(id:integer, title:string, body:text)'
- def inspect
+ def inspect # :nodoc:
if self == Base
super
elsif abstract_class?
@@ -244,7 +257,7 @@ module ActiveRecord
end
# Overwrite the default class equality method to provide support for decorated models.
- def ===(object)
+ def ===(object) # :nodoc:
object.is_a?(self)
end
@@ -493,13 +506,12 @@ module ActiveRecord
# Returns the contents of the record as a nicely formatted string.
def inspect
- filter_attributes = self.filter_attributes.map(&:to_s).to_set
# We check defined?(@attributes) not to issue warnings if the object is
# allocated but not initialized.
inspection = if defined?(@attributes) && @attributes
self.class.attribute_names.collect do |name|
if has_attribute?(name)
- if filter_attributes.include?(name) && !read_attribute(name).nil?
+ if filter_attribute?(name)
"#{name}: #{ActiveRecord::Core::FILTERED}"
else
"#{name}: #{attribute_for_inspect(name)}"
@@ -517,21 +529,19 @@ module ActiveRecord
# when pp is required.
def pretty_print(pp)
return super if custom_inspect_method_defined?
- filter_attributes = self.filter_attributes.map(&:to_s).to_set
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|
- column_value = read_attribute(column_name)
pp.breakable " "
pp.group(1) do
pp.text column_name
pp.text ":"
pp.breakable
- if filter_attributes.include?(column_name) && !column_value.nil?
+ if filter_attribute?(column_name)
pp.text ActiveRecord::Core::FILTERED
else
- pp.pp column_value
+ pp.pp read_attribute(column_name)
end
end
end
@@ -583,5 +593,9 @@ module ActiveRecord
def custom_inspect_method_defined?
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?
+ end
end
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 47351588d3..b213754641 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -180,9 +180,7 @@ end_warning
end
initializer "active_record.set_executor_hooks" do
- ActiveSupport.on_load(:active_record) do
- ActiveRecord::QueryCache.install_executor_hooks
- end
+ ActiveRecord::QueryCache.install_executor_hooks
end
initializer "active_record.add_watchable_files" do |app|
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 6fc9df5083..261fee13eb 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -106,7 +106,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
end
def test_create_mysql_database_with_encoding
- assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
+ assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8mb4`", create_database(:matt)
assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, charset: "latin1")
assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT COLLATE `utf8mb4_bin`", create_database(:matt_aimonetti, collation: "utf8mb4_bin")
end
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 0c0e2a116e..3103589186 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -104,8 +104,8 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
def test_mysql_connection_collation_is_configured
- assert_equal "utf8_unicode_ci", @connection.show_variable("collation_connection")
- assert_equal "utf8_general_ci", ARUnit2Model.connection.show_variable("collation_connection")
+ assert_equal "utf8mb4_unicode_ci", @connection.show_variable("collation_connection")
+ assert_equal "utf8mb4_general_ci", ARUnit2Model.connection.show_variable("collation_connection")
end
def test_mysql_default_in_strict_mode
diff --git a/activerecord/test/cases/filter_attributes_test.rb b/activerecord/test/cases/filter_attributes_test.rb
index f88cecfe2b..af5badd87d 100644
--- a/activerecord/test/cases/filter_attributes_test.rb
+++ b/activerecord/test/cases/filter_attributes_test.rb
@@ -10,11 +10,12 @@ class FilterAttributesTest < ActiveRecord::TestCase
fixtures :"admin/users", :"admin/accounts"
setup do
+ @previous_filter_attributes = ActiveRecord::Base.filter_attributes
ActiveRecord::Base.filter_attributes = [:name]
end
teardown do
- ActiveRecord::Base.filter_attributes = []
+ ActiveRecord::Base.filter_attributes = @previous_filter_attributes
end
test "filter_attributes" do
@@ -35,20 +36,23 @@ class FilterAttributesTest < ActiveRecord::TestCase
assert_equal 1, account.inspect.scan("[FILTERED]").length
end
- Admin::Account.filter_attributes = []
-
- # Above changes should not impact other models
- Admin::User.all.each do |user|
- assert_includes user.inspect, "name: [FILTERED]"
- assert_equal 1, user.inspect.scan("[FILTERED]").length
+ begin
+ previous_account_filter_attributes = Admin::Account.filter_attributes
+ Admin::Account.filter_attributes = []
+
+ # Above changes should not impact other models
+ Admin::User.all.each do |user|
+ assert_includes user.inspect, "name: [FILTERED]"
+ assert_equal 1, user.inspect.scan("[FILTERED]").length
+ end
+
+ Admin::Account.all.each do |account|
+ assert_not_includes account.inspect, "name: [FILTERED]"
+ assert_equal 0, account.inspect.scan("[FILTERED]").length
+ end
+ ensure
+ Admin::Account.filter_attributes = previous_account_filter_attributes
end
-
- Admin::Account.all.each do |account|
- assert_not_includes account.inspect, "name: [FILTERED]"
- assert_equal 0, account.inspect.scan("[FILTERED]").length
- end
-
- Admin::Account.filter_attributes = [:name]
end
test "filter_attributes should not filter nil value" do
diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml
index 4bcb2aeea6..be337ddcd8 100644
--- a/activerecord/test/config.example.yml
+++ b/activerecord/test/config.example.yml
@@ -54,11 +54,11 @@ connections:
mysql2:
arunit:
username: rails
- encoding: utf8
- collation: utf8_unicode_ci
+ encoding: utf8mb4
+ collation: utf8mb4_unicode_ci
arunit2:
username: rails
- encoding: utf8
+ encoding: utf8mb4
oracle:
arunit:
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 0f2f6ddd68..c384297658 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -36,7 +36,7 @@ ActiveRecord::Schema.define do
t.index :var_binary
end
- create_table :key_tests, force: true, options: "ENGINE=MyISAM" do |t|
+ create_table :key_tests, force: true do |t|
t.string :awesome
t.string :pizza
t.string :snacks