先日のパッチ会で kamipo さんにもらったアドバイスを書き残しておく。
TL;DR としては表題そのまま。キーワード引数の分離への対応にRuby 2.8.0-devを使うというもの。
Ruby 2.7.0 を使ってキーワード引数の分離への警告のみでそれを抑制しようとする場合は、スーパーハードモードルビーとパッチ会で呼ばれた変更箇所の特定が難しいケースになる場合がある。
スーパーハードモード (Ruby 2.7.0)
Ruby 3.0 に向けてキーワード引数の分離が必要になる場合は、Ruby 2.7.0 を使うと以下のような警告が表示される。
% ruby -v
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
% bundle exec rake
(略)
/home/vagrant/.rvm/gems/ruby-2.7.0/bundler/gems/rails-80e72c5eb7d4/activerecord/lib/active_record/migration.rb:907: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/home/vagrant/src/oracle-enhanced/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb:199: warning: The called method `create_table' is defined here
まず warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
となっている Active Record のコードの場所を示す。
def method_missing(method, *arguments, &block)
arg_list = arguments.map(&:inspect) * ", "
say_with_time "#{method}(#{arg_list})" do
unless connection.respond_to? :revert
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
arguments[0] = proper_table_name(arguments.first, table_name_options)
if [:rename_table, :add_foreign_key].include?(method) ||
(method == :remove_foreign_key && !arguments.second.is_a?(Hash))
arguments[1] = proper_table_name(arguments.second, table_name_options)
end
end
end
return super unless connection.respond_to?(method)
-> connection.send(method, *arguments, &block)
end
end
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
次に warning: The called method
create_table' is defined here` となっている Oracle enhanced adapter のコードの場所を示す。
-> def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
create_sequence = id != false
td = create_table_definition(
table_name, **options.extract!(:temporary, :options, :as, :comment, :tablespace, :organization)
)
結論としてはどちらもキーワード引数の分離への変更対象ではない。メソッドの定義側も呼び出し側いずれのコードも期待から外れたものではなかった。
これが kamipo さんの "warningみてもどこ直せばいいかまったくといっていいほどわからんからな" の一例である。
次に Ruby 2.8.0-dev を使うと警告ではなくエラーになることでバックトレースが見えるイージーモードに切り替えてみよう。
イージーモード (Ruby 2.8.0-dev)
Ruby 2.8.0-dev を使った結果は以下となる。
Failures:
1) OracleEnhancedAdapter schema dump tables should not include ignored
table names in schema dump
Failure/Error:
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
create_sequence = id != false
td = create_table_definition(
table_name, **options.extract!(:temporary, :options, :as, :comment, :tablespace, :organization)
)
if id && !td.as
pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
if pk.is_a?(Array)
ArgumentError:
wrong number of arguments (given 2, expected 1)
# ./lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb:199:in `create_table'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/migration.rb:907:in `block in method_missing'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/migration.rb:875:in `block in say_with_time'
# /home/travis/.rvm/rubies/ruby-head/lib/ruby/2.8.0/benchmark.rb:293:in `measure'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/migration.rb:875:in `say_with_time'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/migration.rb:896:in `method_mi
# ./spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb:24:in `block in create_test_posts_table'
# ./spec/spec_helper.rb:121:in `instance_eval'
# ./spec/spec_helper.rb:121:in `block (2 levels) in schema_define'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/migration.rb:884:in `suppress_messages'
# ./spec/spec_helper.rb:120:in `block in schema_define'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/schema.rb:50:in `instance_eval'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/schema.rb:50:in `define'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/schema.rb:46:in `define'
# ./spec/spec_helper.rb:119:in `schema_define'
(以下略)
Ruby 2.7.0 で表示されている警告は ArgumentError
からのバックトレースの以下2行となる。そして、Ruby 2.8.0-dev では警告ではなくエラーになることでその先もバックトレースとして表示されている。
ArgumentError:
wrong number of arguments (given 2, expected 1)
# ./lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb:199:in `create_table'
# /home/travis/.rvm/gems/ruby-head/bundler/gems/rails-d80714e0834f/activerecord/lib/active_record/migration.rb:907:in `block in method_missing'
これが示すことはバックトレースで問題の箇所を探索することができるようになるということ。この例だとバックトレース中に示されている spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb:24
が修正対象となるものだった。
これはテストコード中の以下を示している。
def create_test_posts_table(options = {})
options[:force] = true
schema_define do
-> create_table :test_posts, options do |t|
t.string :title
t.timestamps null: true
end
add_index :test_posts, :title
end
end
変更するべきはこのテストコードで次のようになる。
diff --git a/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb b/spec/active_record/connec
index f3b1df0..8330a95 100644
--- a/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb
+++ b/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb
@@ -21,7 +21,7 @@ describe "OracleEnhancedAdapter schema dump" do
def create_test_posts_table(options = {})
options[:force] = true
schema_define do
- create_table :test_posts, options do |t|
+ create_table :test_posts, **options do |t|
t.string :title
t.timestamps null: true
end
これによって解決している PR が以下です。
github.com
このように、キーワード引数の分離への対応には Ruby 2.8.0-dev を使った方がよりヒントを得られるケースがあるので、困った時は rbenv install 2.8.0-dev
などで master の Ruby を使ってみることを考慮してみると良いでしょう。
この問題に2ヶ月間取り組んだ kamipo さんからの知見に感謝します。
次回のパッチ会は 2020年2月27日(木)です。
blog.agile.esm.co.jp