Techioz Blog

ラムダはキーワード引数を受け入れません

概要

アプリケーションを Ruby 2.7.5 から 3.2.2 にアップグレードした後、この問題が発生しました。 Railsのバージョンは6.0.6.1です。

スコープで使用されるラムダがあり、位置引数とキーワード引数の両方があります。アップグレード以降、キーワード引数は受け入れられず、引数の数が間違っていることを示す ArgumentError がスローされます。

Ruby 3.0 では、位置引数とキーワード引数に関連する重大な変更が導入されました (ここを参照) が、これでは問題の説明がつかないと思います。

以下に例を示します。

class TestClass
  scope :test_scope, lambda do |arg1, arg2:|
    puts arg1
    puts arg2
  end
end

スコープを呼び出すと、次のエラーが発生します。

> TestClass.test_scope
wrong number of arguments (given 0, expected 1; required keyword: arg2) (ArgumentError)

> TestClass.test_scope('abc')
missing keyword: :arg2 (ArgumentError)

> TestClass.test_scope('abc', arg2: 'def')
wrong number of arguments (given 2, expected 1; required keyword: arg2) (ArgumentError)

いくつかのバリエーションを試してみましたが、どれもうまくいかないようです。同じシグネチャを持つクラス内に通常のメソッドを作成すると、このエラーは発生しません。

ここで何が問題になっているのでしょうか?

アップデート

コメントで提供された例に基づいた完全なスタック トレースを次に示します。

例:

ExampleClass.test_scope('abc', arg2: 'def')

スタックトレース:

ArgumentError: wrong number of arguments (given 2, expected 1; required keyword: arg2)
  /app/app/models/example_class.rb:2:in `block in <class:ExampleClass>'
  /bundle/vendor/ruby/3.2.0/gems/activerecord-6.1.7.3/lib/active_record/relation.rb:411:in `instance_exec'
  /bundle/vendor/ruby/3.2.0/gems/activerecord-6.1.7.3/lib/active_record/relation.rb:411:in `block in _exec_scope'
  /bundle/vendor/ruby/3.2.0/gems/activerecord-6.1.7.3/lib/active_record/relation.rb:804:in `_scoping'
  /bundle/vendor/ruby/3.2.0/gems/activerecord-6.1.7.3/lib/active_record/relation.rb:411:in `_exec_scope'
  /bundle/vendor/ruby/3.2.0/gems/activerecord-6.1.7.3/lib/active_record/scoping/named.rb:176:in `block in scope'
  /app/config/initializers/test_init.rb:13:in `<main>'
  /bundle/vendor/ruby/3.2.0/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:39:in `load'
  /bundle/vendor/ruby/3.2.0/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:39:in `load'
  /bundle/vendor/ruby/3.2.0/gems/activesupport-6.1.7.3/lib/active_support/dependencies.rb:326:in `block in load'
  /bundle/vendor/ruby/3.2.0/gems/activesupport-6.1.7.3/lib/active_support/dependencies.rb:299:in `load_dependency'
  /bundle/vendor/ruby/3.2.0/gems/activesupport-6.1.7.3/lib/active_support/dependencies.rb:326:in `load'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/engine.rb:681:in `block in load_config_initializer'
  /bundle/vendor/ruby/3.2.0/gems/activesupport-6.1.7.3/lib/active_support/notifications.rb:205:in `instrument'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/engine.rb:680:in `load_config_initializer'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/engine.rb:634:in `block (2 levels) in <class:Engine>'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/engine.rb:633:in `each'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/engine.rb:633:in `block in <class:Engine>'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/initializable.rb:32:in `instance_exec'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/initializable.rb:32:in `run'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/initializable.rb:61:in `block in run_initializers'
  /usr/local/lib/ruby/3.2.0/tsort.rb:228:in `block in tsort_each'
  /usr/local/lib/ruby/3.2.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
  /usr/local/lib/ruby/3.2.0/tsort.rb:422:in `block (2 levels) in each_strongly_connected_component_from'
  /usr/local/lib/ruby/3.2.0/tsort.rb:431:in `each_strongly_connected_component_from'
  /usr/local/lib/ruby/3.2.0/tsort.rb:421:in `block in each_strongly_connected_component_from'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/initializable.rb:50:in `each'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/initializable.rb:50:in `tsort_each_child'
  /usr/local/lib/ruby/3.2.0/tsort.rb:415:in `call'
  /usr/local/lib/ruby/3.2.0/tsort.rb:415:in `each_strongly_connected_component_from'
  /usr/local/lib/ruby/3.2.0/tsort.rb:349:in `block in each_strongly_connected_component'
  /usr/local/lib/ruby/3.2.0/tsort.rb:347:in `each'
  /usr/local/lib/ruby/3.2.0/tsort.rb:347:in `call'
  /usr/local/lib/ruby/3.2.0/tsort.rb:347:in `each_strongly_connected_component'
  /usr/local/lib/ruby/3.2.0/tsort.rb:226:in `tsort_each'
  /usr/local/lib/ruby/3.2.0/tsort.rb:205:in `tsort_each'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/initializable.rb:60:in `run_initializers'
  /bundle/vendor/ruby/3.2.0/gems/railties-6.1.7.3/lib/rails/application.rb:391:in `initialize!'
  /app/config/environment.rb:5:in `<top (required)>'
  config.ru:3:in `require_relative'
  config.ru:3:in `block in <main>'
  /bundle/vendor/ruby/3.2.0/gems/rack-2.2.7/lib/rack/builder.rb:116:in `eval'
  /bundle/vendor/ruby/3.2.0/gems/rack-2.2.7/lib/rack/builder.rb:116:in `new_from_string'
  /bundle/vendor/ruby/3.2.0/gems/rack-2.2.7/lib/rack/builder.rb:105:in `load_file'
  /bundle/vendor/ruby/3.2.0/gems/rack-2.2.7/lib/rack/builder.rb:66:in `parse_file'
  /bundle/vendor/ruby/3.2.0/gems/puma-5.6.5/lib/puma/configuration.rb:348:in `load_rackup'
  /bundle/vendor/ruby/3.2.0/gems/puma-5.6.5/lib/puma/configuration.rb:270:in `app'
  /bundle/vendor/ruby/3.2.0/gems/puma-5.6.5/lib/puma/runner.rb:150:in `load_and_bind'
  /bundle/vendor/ruby/3.2.0/gems/puma-5.6.5/lib/puma/single.rb:44:in `run'
  /bundle/vendor/ruby/3.2.0/gems/puma-5.6.5/lib/puma/launcher.rb:193:in `run'
  /bundle/vendor/ruby/3.2.0/gems/puma-5.6.5/lib/puma/cli.rb:81:in `run'
  /bundle/vendor/ruby/3.2.0/gems/puma-5.6.5/bin/puma:10:in `<top (required)>'
  /bundle/vendor/ruby/3.2.0/bin/puma:25:in `load'
  /bundle/vendor/ruby/3.2.0/bin/puma:25:in `<top (required)>'

解決策

今日、Rails 6.1 アプリを Ruby 3.2 を使用するようにアップグレードする作業をしていたときに、これと同じ問題に遭遇しました。

問題は、Rails の Ruby 3.2 互換性修正は現在 6-1-stable ブランチにありますが、この記事の執筆時点での最新の 6.1 リリース (6.1.7.3) にはそのコミットが含まれていないことです。

新しい 6.1 リリースがそのコミットとともに出荷されない限り、Rails 6.1 を Ruby 3.2 で動作させる唯一の方法は、GitHub 上の 6.1 安定版ブランチを直接指定することだと思われます (ただし、私はこれを試していません)。

詳細については、https://github.com/rails/rails/pull/46895 を参照してください。