先日のパッチ会で kamipo さんにもらったアドバイスを書き残しておく。
TL;DR としては表題そのまま。キーワード引数の分離への対応にRuby 2.8.0-devを使うというもの。
2.8.0-devを使えばイージーモードだけど2.7.0縛りプレイだと常人にはクリア不能のむずかしさ https://t.co/tpJGTARwAc
— Ryuta Kamizono (@kamipo) 2020年1月24日
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) # ここが `activerecord/lib/active_record/migration.rb:907` 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) # ここが `oracle_enhanced/schema_statements.rb:199` create_sequence = id != false td = create_table_definition( table_name, **options.extract!(:temporary, :options, :as, :comment, :tablespace, :organization) )
結論としてはどちらもキーワード引数の分離への変更対象ではない。メソッドの定義側も呼び出し側いずれのコードも期待から外れたものではなかった。
これが kamipo さんの "warningみてもどこ直せばいいかまったくといっていいほどわからんからな" の一例である。
warningみてもどこ直せばいいかまったくといっていいほどわからんからな
— Ryuta Kamizono (@kamipo) 2020年1月24日
次に 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| # ここが `oracle_enhanced/schema_dumper_spec.rb:24` 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 が以下です。
このように、キーワード引数の分離への対応には Ruby 2.8.0-dev を使った方がよりヒントを得られるケースがあるので、困った時は rbenv install 2.8.0-dev
などで master の Ruby を使ってみることを考慮してみると良いでしょう。
この問題に2ヶ月間取り組んだ kamipo さんからの知見に感謝します。
この問題に取り組んだ2ヶ月間が僕をここまで強くした
— Ryuta Kamizono (@kamipo) 2020年1月24日
次回のパッチ会は 2020年2月27日(木)です。