提交 657520fa 编写于 作者: J Jean Boussier

Fix most Ruby 2.7 warnings in Active Record 6.0

上级 52a13732
......@@ -476,7 +476,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.4.1)
sqlite3 (1.4.2)
stackprof (0.2.13)
sucker_punch (2.1.2)
concurrent-ruby (~> 1.0)
......
......@@ -166,7 +166,7 @@ def self.start_application # :nodoc:
def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {}, &capabilities)
driver_options = { using: using, screen_size: screen_size, options: options }
self.driver = SystemTesting::Driver.new(driver, driver_options, &capabilities)
self.driver = SystemTesting::Driver.new(driver, **driver_options, &capabilities)
end
driven_by :selenium
......
......@@ -1857,7 +1857,7 @@ def destroy_associations
hm_options[k] = options[k] if options.key? k
end
has_many name, scope, hm_options, &extension
has_many name, scope, **hm_options, &extension
_reflections[name.to_s].parent_reflection = habtm_reflection
end
end
......
......@@ -49,7 +49,7 @@ def reload(*)
# +to+ When passed, this method will return false unless the value was
# changed to the given value
def saved_change_to_attribute?(attr_name, **options)
mutations_before_last_save.changed?(attr_name.to_s, options)
mutations_before_last_save.changed?(attr_name.to_s, **options)
end
# Returns the change to an attribute during the last save. If the
......@@ -99,7 +99,7 @@ def saved_changes
# +to+ When passed, this method will return false unless the value will be
# changed to the given value
def will_save_change_to_attribute?(attr_name, **options)
mutations_from_database.changed?(attr_name.to_s, options)
mutations_from_database.changed?(attr_name.to_s, **options)
end
# Returns the change to an attribute that will be persisted during the
......
......@@ -314,7 +314,7 @@ def destroy #:nodoc:
@_destroy_callback_already_called = false
end
def touch(*) #:nodoc:
def touch(*, **) #:nodoc:
_run_touch_callbacks { super }
end
......
......@@ -27,7 +27,7 @@ def visit_AlterTable(o)
end
def visit_ColumnDefinition(o)
o.sql_type = type_to_sql(o.type, o.options)
o.sql_type = type_to_sql(o.type, **o.options)
column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
column_sql
......
......@@ -139,7 +139,8 @@ def initialize(
def add_to(table)
columns.each do |column_options|
table.column(*column_options)
kwargs = column_options.extract_options!
table.column(*column_options, **kwargs)
end
if index
......@@ -199,7 +200,7 @@ module ColumnMethods
# Appends a primary key definition to the table definition.
# Can be called multiple times, but this is probably not a good idea.
def primary_key(name, type = :primary_key, **options)
column(name, type, options.merge(primary_key: true))
column(name, type, **options.merge(primary_key: true))
end
##
......@@ -226,7 +227,7 @@ def primary_key(name, type = :primary_key, **options)
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{column_type}(*names, **options)
raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
names.each { |name| column(name, :#{column_type}, options) }
names.each { |name| column(name, :#{column_type}, **options) }
end
RUBY
end
......@@ -375,7 +376,7 @@ def column(name, type, **options)
index_options = options.delete(:index)
index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
@columns_hash[name] = new_column_definition(name, type, options)
@columns_hash[name] = new_column_definition(name, type, **options)
self
end
......@@ -408,8 +409,8 @@ def timestamps(**options)
options[:precision] = 6
end
column(:created_at, :datetime, options)
column(:updated_at, :datetime, options)
column(:created_at, :datetime, **options)
column(:updated_at, :datetime, **options)
end
# Adds a reference.
......@@ -421,7 +422,7 @@ def timestamps(**options)
# See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
def references(*args, **options)
args.each do |ref_name|
ReferenceDefinition.new(ref_name, options).add_to(self)
ReferenceDefinition.new(ref_name, **options).add_to(self)
end
end
alias :belongs_to :references
......@@ -479,7 +480,7 @@ def drop_foreign_key(name)
def add_column(name, type, options)
name = name.to_s
type = type.to_sym
@adds << AddColumnDefinition.new(@td.new_column_definition(name, type, options))
@adds << AddColumnDefinition.new(@td.new_column_definition(name, type, **options))
end
end
......@@ -540,7 +541,7 @@ def initialize(table_name, base)
# See TableDefinition#column for details of the options you can use.
def column(column_name, type, **options)
index_options = options.delete(:index)
@base.add_column(name, column_name, type, options)
@base.add_column(name, column_name, type, **options)
index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
end
......@@ -550,7 +551,7 @@ def column(column_name, type, **options)
#
# See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?]
def column_exists?(column_name, type = nil, options = {})
@base.column_exists?(name, column_name, type, options)
@base.column_exists?(name, column_name, type, **options)
end
# Adds a new index to the table. +column_name+ can be a single Symbol, or
......@@ -662,7 +663,7 @@ def rename(column_name, new_column_name)
# See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
def references(*args, **options)
args.each do |ref_name|
@base.add_reference(name, ref_name, options)
@base.add_reference(name, ref_name, **options)
end
end
alias :belongs_to :references
......@@ -675,7 +676,7 @@ def references(*args, **options)
# See {connection.remove_reference}[rdoc-ref:SchemaStatements#remove_reference]
def remove_references(*args, **options)
args.each do |ref_name|
@base.remove_reference(name, ref_name, options)
@base.remove_reference(name, ref_name, **options)
end
end
alias :remove_belongs_to :remove_references
......@@ -686,8 +687,8 @@ def remove_references(*args, **options)
# t.foreign_key(:authors, column: :author_id, primary_key: "id")
#
# See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
def foreign_key(*args)
@base.add_foreign_key(name, *args)
def foreign_key(*args, **options)
@base.add_foreign_key(name, *args, **options)
end
# Removes the given foreign key from the table.
......@@ -696,8 +697,8 @@ def foreign_key(*args)
# t.remove_foreign_key(column: :author_id)
#
# See {connection.remove_foreign_key}[rdoc-ref:SchemaStatements#remove_foreign_key]
def remove_foreign_key(*args)
@base.remove_foreign_key(name, *args)
def remove_foreign_key(*args, **options)
@base.remove_foreign_key(name, *args, **options)
end
# Checks to see if a foreign key exists.
......@@ -705,8 +706,8 @@ def remove_foreign_key(*args)
# t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
#
# See {connection.foreign_key_exists?}[rdoc-ref:SchemaStatements#foreign_key_exists?]
def foreign_key_exists?(*args)
@base.foreign_key_exists?(name, *args)
def foreign_key_exists?(*args, **options)
@base.foreign_key_exists?(name, *args, **options)
end
end
end
......
......@@ -292,7 +292,7 @@ def primary_key(table_name)
#
# See also TableDefinition#column for details on how to create columns.
def create_table(table_name, **options)
td = create_table_definition(table_name, options)
td = create_table_definition(table_name, **options)
if options[:id] != false && !options[:as]
pk = options.fetch(:primary_key) do
......@@ -302,7 +302,7 @@ def create_table(table_name, **options)
if pk.is_a?(Array)
td.primary_keys pk
else
td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
td.primary_key pk, options.fetch(:id, :primary_key), **options.except(:comment)
end
end
......@@ -378,7 +378,7 @@ def create_join_table(table_1, table_2, column_options: {}, **options)
t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
create_table(join_table_name, options.merge!(id: false)) do |td|
create_table(join_table_name, **options.merge!(id: false)) do |td|
td.references t1_ref, column_options
td.references t2_ref, column_options
yield td if block_given?
......@@ -783,7 +783,7 @@ def rename_column(table_name, column_name, new_column_name)
#
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
def add_index(table_name, column_name, options = {})
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, **options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
end
......@@ -1196,7 +1196,7 @@ def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc
if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
end
index_columns = quoted_columns_for_index(column_names, options).join(", ")
index_columns = quoted_columns_for_index(column_names, **options).join(", ")
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
end
......@@ -1253,7 +1253,7 @@ def options_for_index_columns(options)
# the PostgreSQL adapter for supporting operator classes.
def add_options_for_index_columns(quoted_columns, **options)
if supports_index_sort_order?
quoted_columns = add_index_sort_order(quoted_columns, options)
quoted_columns = add_index_sort_order(quoted_columns, **options)
end
quoted_columns
......@@ -1263,7 +1263,7 @@ def quoted_columns_for_index(column_names, **options)
return [column_names] if column_names.is_a?(String)
quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
add_options_for_index_columns(quoted_columns, options).values
add_options_for_index_columns(quoted_columns, **options).values
end
def index_name_for_remove(table_name, options = {})
......
......@@ -141,8 +141,8 @@ def open?; !closed?; end
end
class SavepointTransaction < Transaction
def initialize(connection, savepoint_name, parent_transaction, *args)
super(connection, *args)
def initialize(connection, savepoint_name, parent_transaction, *args, **options)
super(connection, *args, **options)
parent_transaction.state.add_child(@state)
......
......@@ -154,8 +154,8 @@ def schema_creation
MySQL::SchemaCreation.new(self)
end
def create_table_definition(*args)
MySQL::TableDefinition.new(self, *args)
def create_table_definition(*args, **options)
MySQL::TableDefinition.new(self, *args, **options)
end
def new_column_from_field(table_name, field)
......
......@@ -390,7 +390,7 @@ def rename_table(table_name, new_name)
rename_table_indexes(table_name, new_name)
end
def add_column(table_name, column_name, type, options = {}) #:nodoc:
def add_column(table_name, column_name, type, **options) #:nodoc:
clear_cache!
super
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
......
......@@ -87,8 +87,8 @@ def schema_creation
SQLite3::SchemaCreation.new(self)
end
def create_table_definition(*args)
SQLite3::TableDefinition.new(self, *args)
def create_table_definition(*args, **options)
SQLite3::TableDefinition.new(self, *args, **options)
end
def new_column_from_field(table_name, field)
......
......@@ -244,7 +244,7 @@ def rename_table(table_name, new_name)
rename_table_indexes(table_name, new_name)
end
def add_column(table_name, column_name, type, options = {}) #:nodoc:
def add_column(table_name, column_name, type, **options) #:nodoc:
if invalid_alter_table_type?(type, options)
alter_table(table_name) do |definition|
definition.column(column_name, type, options)
......@@ -398,7 +398,7 @@ def move_table(from, to, options = {}, &block)
def copy_table(from, to, options = {})
from_primary_key = primary_key(from)
options[:id] = false
create_table(to, options) do |definition|
create_table(to, **options) do |definition|
@definition = definition
if from_primary_key.is_a?(Array)
@definition.primary_keys from_primary_key
......
......@@ -54,11 +54,11 @@ def no_touching?
NoTouching.applied_to?(self.class)
end
def touch_later(*) # :nodoc:
def touch_later(*, **) # :nodoc:
super unless no_touching?
end
def touch(*) # :nodoc:
def touch(*, **) # :nodoc:
super unless no_touching?
end
end
......
......@@ -466,8 +466,8 @@ def persisted?
#
# Attributes marked as readonly are silently ignored if the record is
# being updated.
def save(*args, &block)
create_or_update(*args, &block)
def save(*args, **options, &block)
create_or_update(*args, **options, &block)
rescue ActiveRecord::RecordInvalid
false
end
......@@ -499,8 +499,8 @@ def save(*args, &block)
# being updated.
#
# Unless an error is raised, returns true.
def save!(*args, &block)
create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
def save!(*args, **options, &block)
create_or_update(*args, **options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
end
# Deletes the record in the database and freezes this instance to
......
......@@ -110,8 +110,12 @@ def method_missing(method, *args, &block)
end
module ClassMethods # :nodoc:
def create(klass, *args)
def create(klass, *args, **options)
if options.empty?
relation_class_for(klass).new(klass, *args)
else
relation_class_for(klass).new(klass, *args, **options)
end
end
private
......
......@@ -103,7 +103,7 @@ class << self
module ClassMethods
def store(store_attribute, options = {})
serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder])
store_accessor(store_attribute, options[:accessors], options.slice(:prefix, :suffix)) if options.has_key? :accessors
store_accessor(store_attribute, options[:accessors], **options.slice(:prefix, :suffix)) if options.has_key? :accessors
end
def store_accessor(store_attribute, *keys, prefix: nil, suffix: nil)
......
......@@ -40,11 +40,11 @@ def suppress(&block)
end
end
def save(*) # :nodoc:
def save(*, **) # :nodoc:
SuppressorRegistry.suppressed[self.class.name] ? true : super
end
def save!(*) # :nodoc:
def save!(*, **) # :nodoc:
SuppressorRegistry.suppressed[self.class.name] ? true : super
end
end
......
......@@ -9,7 +9,7 @@ module TouchLater # :nodoc:
before_commit_without_transaction_enrollment :touch_deferred_attributes
end
def touch_later(*names) # :nodoc:
def touch_later(*names, **) # :nodoc:
unless persisted?
raise ActiveRecordError, <<-MSG.squish
cannot touch on a new or destroyed record object. Consider using
......
......@@ -208,8 +208,8 @@ module Transactions
# Note that "TRUNCATE" is also a MySQL DDL statement!
module ClassMethods
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
def transaction(options = {}, &block)
connection.transaction(options, &block)
def transaction(**options, &block)
connection.transaction(**options, &block)
end
def before_commit(*args, &block) # :nodoc:
......@@ -310,15 +310,15 @@ def destroy #:nodoc:
with_transaction_returning_status { super }
end
def save(*) #:nodoc:
def save(*, **) #:nodoc:
with_transaction_returning_status { super }
end
def save!(*) #:nodoc:
def save!(*, **) #:nodoc:
with_transaction_returning_status { super }
end
def touch(*) #:nodoc:
def touch(*, **) #:nodoc:
with_transaction_returning_status { super }
end
......
......@@ -43,13 +43,13 @@ module Validations
# The validation context can be changed by passing <tt>context: context</tt>.
# The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
# with this when the validations module is mixed in, which it is by default.
def save(options = {})
def save(**options)
perform_validations(options) ? super : false
end
# Attempts to save the record just like {ActiveRecord::Base#save}[rdoc-ref:Base#save] but
# will raise an ActiveRecord::RecordInvalid exception instead of returning +false+ if the record is not valid.
def save!(options = {})
def save!(**options)
perform_validations(options) ? super : raise_validation_error
end
......
......@@ -16,17 +16,17 @@ def test_insert
id = 1_000_000
assert_difference "Book.count", +1 do
Book.insert(id: id, name: "Rework", author_id: 1)
Book.insert({ id: id, name: "Rework", author_id: 1 })
end
Book.upsert(id: id, name: "Remote", author_id: 1)
Book.upsert({ id: id, name: "Remote", author_id: 1 })
assert_equal "Remote", Book.find(id).name
end
def test_insert!
assert_difference "Book.count", +1 do
Book.insert! name: "Rework", author_id: 1
Book.insert!({ name: "Rework", author_id: 1 })
end
end
......@@ -136,7 +136,7 @@ def test_skip_duplicates_strategy_does_not_secretly_upsert
book = Book.create!(author_id: 8, name: "Refactoring", format: "EXPECTED")
assert_no_difference "Book.count" do
Book.insert(author_id: 8, name: "Refactoring", format: "UNEXPECTED")
Book.insert({ author_id: 8, name: "Refactoring", format: "UNEXPECTED" })
end
assert_equal "EXPECTED", book.reload.format
......@@ -185,7 +185,7 @@ def test_insert_logs_message_including_model_name
skip unless supports_insert_conflict_target?
capture_log_output do |output|
Book.insert(name: "Rework", author_id: 1)
Book.insert({ name: "Rework", author_id: 1 })
assert_match "Book Insert", output.string
end
end
......@@ -203,7 +203,7 @@ def test_upsert_logs_message_including_model_name
skip unless supports_insert_on_duplicate_update?
capture_log_output do |output|
Book.upsert(name: "Remote", author_id: 1)
Book.upsert({ name: "Remote", author_id: 1 })
assert_match "Book Upsert", output.string
end
end
......
......@@ -19,28 +19,44 @@ def with_change_table
def test_references_column_type_adds_id
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_reference, nil, [:delete_me, :customer, {}]
else
@connection.expect :add_reference, nil, [:delete_me, :customer]
end
t.references :customer
end
end
def test_remove_references_column_type_removes_id
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :remove_reference, nil, [:delete_me, :customer, {}]
else
@connection.expect :remove_reference, nil, [:delete_me, :customer]
end
t.remove_references :customer
end
end
def test_add_belongs_to_works_like_add_references
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_reference, nil, [:delete_me, :customer, {}]
else
@connection.expect :add_reference, nil, [:delete_me, :customer]
end
t.belongs_to :customer
end
end
def test_remove_belongs_to_works_like_remove_references
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :remove_reference, nil, [:delete_me, :customer, {}]
else
@connection.expect :remove_reference, nil, [:delete_me, :customer]
end
t.remove_belongs_to :customer
end
end
......@@ -110,24 +126,39 @@ def test_primary_key_creates_primary_key_column
def test_integer_creates_integer_column
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_column, nil, [:delete_me, :foo, :integer, {}]
@connection.expect :add_column, nil, [:delete_me, :bar, :integer, {}]
else
@connection.expect :add_column, nil, [:delete_me, :foo, :integer]
@connection.expect :add_column, nil, [:delete_me, :bar, :integer]
end
t.integer :foo, :bar
end
end
def test_bigint_creates_bigint_column
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_column, nil, [:delete_me, :foo, :bigint, {}]
@connection.expect :add_column, nil, [:delete_me, :bar, :bigint, {}]
else
@connection.expect :add_column, nil, [:delete_me, :foo, :bigint]
@connection.expect :add_column, nil, [:delete_me, :bar, :bigint]
end
t.bigint :foo, :bar
end
end
def test_string_creates_string_column
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_column, nil, [:delete_me, :foo, :string, {}]
@connection.expect :add_column, nil, [:delete_me, :bar, :string, {}]
else
@connection.expect :add_column, nil, [:delete_me, :foo, :string]
@connection.expect :add_column, nil, [:delete_me, :bar, :string]
end
t.string :foo, :bar
end
end
......@@ -135,16 +166,26 @@ def test_string_creates_string_column
if current_adapter?(:PostgreSQLAdapter)
def test_json_creates_json_column
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_column, nil, [:delete_me, :foo, :json, {}]
@connection.expect :add_column, nil, [:delete_me, :bar, :json, {}]
else
@connection.expect :add_column, nil, [:delete_me, :foo, :json]
@connection.expect :add_column, nil, [:delete_me, :bar, :json]
end
t.json :foo, :bar
end
end
def test_xml_creates_xml_column
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_column, nil, [:delete_me, :foo, :xml, {}]
@connection.expect :add_column, nil, [:delete_me, :bar, :xml, {}]
else
@connection.expect :add_column, nil, [:delete_me, :foo, :xml]
@connection.expect :add_column, nil, [:delete_me, :bar, :xml]
end
t.xml :foo, :bar
end
end
......@@ -152,21 +193,33 @@ def test_xml_creates_xml_column
def test_column_creates_column
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_column, nil, [:delete_me, :bar, :integer, {}]
else
@connection.expect :add_column, nil, [:delete_me, :bar, :integer]
end
t.column :bar, :integer
end
end
def test_column_creates_column_with_options
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_column, nil, [:delete_me, :bar, :integer, { null: false }]
else
@connection.expect :add_column, nil, [:delete_me, :bar, :integer, { null: false }]
end
t.column :bar, :integer, null: false
end
end
def test_column_creates_column_with_index
with_change_table do |t|
if RUBY_VERSION < "2.7"
@connection.expect :add_column, nil, [:delete_me, :bar, :integer, {}]
else
@connection.expect :add_column, nil, [:delete_me, :bar, :integer]
end
@connection.expect :add_index, nil, [:delete_me, :bar, {}]
t.column :bar, :integer, index: true
end
......
......@@ -94,10 +94,17 @@ def test_invert_change_table
t.rename :kind, :cultivar
end
end
if RUBY_VERSION < "2.7"
assert_equal [
[:rename_column, [:fruits, :cultivar, :kind]],
[:remove_column, [:fruits, :name, :string, {}], nil],
], @recorder.commands
else
assert_equal [
[:rename_column, [:fruits, :cultivar, :kind]],
[:remove_column, [:fruits, :name, :string], nil],
], @recorder.commands
end
assert_raises(ActiveRecord::IrreversibleMigration) do
@recorder.revert do
......
......@@ -193,7 +193,13 @@ def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
method_names = methods.map do |method|
# Attribute writer methods only accept one argument. Makes sure []=
# methods still accept two arguments.
definition = /[^\]]=$/.match?(method) ? "arg" : "*args, &block"
definition = if /[^\]]=$/.match?(method)
"arg"
elsif RUBY_VERSION >= "2.7"
"..."
else
"*args, &block"
end
# The following generated method calls the target exactly once, storing
# the returned value in a dummy variable.
......
......@@ -14,14 +14,21 @@ def initialize(context, options)
private
def method_missing(method, *arguments, &block)
options = nil
if arguments.first.is_a?(Proc)
proc = arguments.pop
arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
elsif arguments.last.respond_to?(:to_hash)
options = @options.deep_merge(arguments.pop)
else
arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
options = @options
end
if options
@context.__send__(method, *arguments, **options, &block)
else
@context.__send__(method, *arguments, &block)
end
end
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册