You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby 3.2.2升级后Rails Scope中Lambda无法接收关键字参数

问题

将应用从Ruby 2.7.5升级至3.2.2(Rails版本为6.0.6.1)后,发现带位置参数+关键字参数的Scope Lambda无法正常接收参数,触发ArgumentError提示参数数量错误。虽然Ruby 3.0有位置参数与关键字参数分离的破坏性变更,但普通方法使用相同签名可正常运行,因此判断问题不在Ruby本身。

示例代码:

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.0.x版本的Active Record对Scope参数的处理逻辑不兼容Ruby 3+的关键字参数分离规则。

从堆栈跟踪可以看到,Active Record的_exec_scope方法通过instance_exec调用Scope的lambda时,会把所有传入的参数(包括关键字参数)作为位置参数传递,而Ruby 3+会严格区分位置参数和关键字参数,导致lambda的参数签名不匹配,触发错误。而普通方法不受影响是因为Ruby直接处理方法调用的参数分离,而Scope的参数经过了Active Record的一层封装。

有两种解决方式:

方式一:修改Scope的参数签名,兼容现有Rails版本

把关键字参数改为哈希参数,或者用**kwargs捕获所有关键字参数,避免参数类型不匹配:

方案1:合并为哈希参数

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

# 调用方式不变:
TestClass.test_scope('abc', arg2: 'def')

方案2:用**kwargs捕获关键字参数

class TestClass
  scope :test_scope, lambda do |arg1, **kwargs|
    puts arg1
    puts kwargs[:arg2]
  end
end

# 调用方式不变:
TestClass.test_scope('abc', arg2: 'def')

方式二:升级Rails版本到6.1.x及以上

Rails 6.1版本已经修复了Ruby 3+下关键字参数的兼容问题,Active Record会正确分离位置参数和关键字参数传递给Scope的lambda。升级后可以保留原有的参数签名不变。

内容的提问来源于stack exchange,提问作者Henridv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 18:37:01