Geokit Rails在Heroku环境地理编码失败(本地正常):安全异常
环境信息
- Ruby:
3.2.2 - 依赖Gem:
geokit-rails,~> 2.3,>= 2.3.1rails,~> 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
相关产品推荐
相关产品推荐

