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

Geokit Rails在Heroku环境地理编码失败(本地正常):安全异常

环境信息

  • Ruby:3.2.2
  • 依赖Gem:
    • geokit-rails, ~> 2.3, >= 2.3.1
    • rails, ~> 7.1.2
  • 生产环境:Heroku

问题描述

  • Property模型会在property_address字段更新后自动执行地理编码
  • 本地控制台执行property.update!(property_address: '1 Hacker Way, Menlo Park, CA')正常,但Heroku环境执行失败
  • 已配置Google地理编码API密钥,确认未超配额
  • 错误显示Geokit的@@secure类变量存在冲突,导致HTTPS配置异常,无法正常发送请求到Google API
  • 错误日志:
irb(main):005> p.update(property_address: '1 Hacker Way, Menlo Park, CA')
E, [2023-12-14T15:03:58.135258 #2] ERROR -- : Caught an error during Google geocoding call: class variable @@secure of Geokit::Geocoders::GoogleGeocoder is overtaken by Geokit::Geocoders
E, [2023-12-14T15:03:58.135412 #2] ERROR -- : (eval):6:in `secure'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/geocoders.rb:156:in `use_https?'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/geocoders.rb:160:in `protocol'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/geocoders/google.rb:90:in `submit_url'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/geocoders/google.rb:63:in `do_geocode'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/geocoders.rb:92:in `geocode'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/multi_geocoder.rb:34:in `block in do_geocode'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/multi_geocoder.rb:31:in `each'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/multi_geocoder.rb:31:in `do_geocode'
/app/vendor/bundle/ruby/3.2.0/gems/geokit-1.14.0/lib/geokit/geocoders.rb:92:in `geocode'
/app/app/models/property.rb:97:in `geocode_address'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:403:in `block in make_lambda'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:239:in `block in halting_and_conditional'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `block in invoke_after'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `each'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:602:in `invoke_after'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:111:in `run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:952:in `_run_update_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/callbacks.rb:449:in `_update_record'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/timestamp.rb:120:in `_update_record'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/persistence.rb:1220:in `create_or_update'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/callbacks.rb:441:in `block in create_or_update'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/autosave_association.rb:375:in `around_save_collection_association'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:130:in `block in run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:141:in `run_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/callbacks.rb:952:in `_run_save_callbacks'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/callbacks.rb:441:in `create_or_update'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/timestamp.rb:125:in `create_or_update'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/persistence.rb:718:in `save'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/validations.rb:49:in `save'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/transactions.rb:309:in `block in save'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/transactions.rb:365:in `block in with_transaction_returning_status'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/database_statements.rb:342:in `transaction'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/transactions.rb:361:in `with_transaction_returning_status'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/transactions.rb:309:in `save'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/suppressor.rb:52:in `save'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/persistence.rb:895:in `block in update'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/transactions.rb:365:in `block in with_transaction_returning_status'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/transaction.rb:535:in `block in within_new_transaction'
/app/vendor/bundle/ruby/3.2.0/gems/activesupport-7.1.2/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/transaction.rb:532:in `within_new_transaction'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/connection_adapters/abstract/database_statements.rb:344:in `transaction'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/transactions.rb:361:in `with_transaction_returning_status'
/app/vendor/bundle/ruby/3.2.0/gems/activerecord-7.1.2/lib/active_record/persistence.rb:893:in `update'
(irb):5:in `<main>'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb/workspace.rb:117:in `eval'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb/workspace.rb:117:in `evaluate'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb/context.rb:570:in `evaluate'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:516:in `block (2 levels) in eval_input'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:828:in `signal_status'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:509:in `block in eval_input'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:591:in `block in each_top_level_statement'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:586:in `loop'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:586:in `each_top_level_statement'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:508:in `eval_input'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:495:in `block in run'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:494:in `catch'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:494:in `run'
/app/vendor/bundle/ruby/3.2.0/gems/irb-1.10.1/lib/irb.rb:396:in `start'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/commands/console/console_command.rb:78:in `start'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/commands/console/console_command.rb:16:in `start'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/commands/console/console_command.rb:106:in `perform'
/app/vendor/bundle/ruby/3.2.0/gems/thor-1.3.0/lib/thor/command.rb:28:in `run'
/app/vendor/bundle/ruby/3.2.0/gems/thor-1.3.0/lib/thor/invocation.rb:127:in `invoke_command'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/command/base.rb:178:in `invoke_command'
/app/vendor/bundle/ruby/3.2.0/gems/thor-1.3.0/lib/thor.rb:527:in `dispatch'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/command/base.rb:73:in `perform'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/command.rb:71:in `block in invoke'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/command.rb:149:in `with_argv'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/command.rb:69:in `invoke'
/app/vendor/bundle/ruby/3.2.0/gems/railties-7.1.2/lib/rails/commands.rb:18:in `<main>'
<internal:/app/vendor/ruby-3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
<internal:/app/vendor/ruby-3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
/app/vendor/bundle/ruby/3.2.0/gems/bootsnap-1.17.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'

解决方法

这个错误是Ruby 3.2+对类变量访问的严格校验导致的——Geokit的Geokit::Geocoders模块和GoogleGeocoder类都定义了@@secure类变量,引发冲突。以下是几种可行的解决方式:

1. 直接强制启用HTTPS,绕开类变量冲突

在config/initializers目录下新建geokit.rb文件,添加以下代码:

Geokit::Geocoders::GoogleGeocoder.key = ENV['GOOGLE_GEOCODING_API_KEY']
# 直接重写protocol方法,跳过类变量的读取
Geokit::Geocoders::GoogleGeocoder.define_singleton_method(:protocol) do
  'https'
end

这样Geokit会直接使用HTTPS发送请求,完全避开@@secure类变量的冲突问题。

2. 更新Geokit到兼容版本

部分新版本的geokit和geokit-rails已经修复了Ruby 3.2下的类变量冲突。修改Gemfile:

gem 'geokit-rails', '~> 2.5' # 或更高的稳定版本

执行bundle update geokit-rails后重新部署到Heroku即可。

3. 临时降级Ruby版本(不推荐)

如果暂时无法更新Gem,可以将Ruby版本降到3.1.x,这个版本不会触发类变量冲突的报错。但这只是权宜之计,建议优先更新Gem。

验证修复

部署完成后,在Heroku控制台执行以下命令测试:

p = Property.first
p.update!(property_address: '1 Hacker Way, Menlo Park, CA')

如果没有报错,且模型的地理编码字段(如latitude、longitude)被正确填充,说明问题已解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 00:39:51