6

我正在使用 active_admin,并且我在 Rails 3 应用程序的应用程序中有一个目录管理员,其中包含模型和页面的声明。时不时我也有一个类,当那个类有一个常量时,就像这样:

class Foo
  BAR = "bar"
end

然后,我在必须在我的 Rails 应用程序中重新加载某些代码的每个请求中都会收到此警告:

/Users/pupeno/helloworld/app/admin/billing.rb:12: warning: already initialized constant BAR

任何想法发生了什么以及如何避免这些警告?

4

1 回答 1

10

在纯红宝石中:

class A
  TEST = "foo"
end
puts A::TEST
# foo

class A
  TEST = "bar"
end
# (irb): warning: already initialized constant TEST
puts A::TEST
# bar

在 Ruby 中,您可以随时打开一个类并重新声明其中的任何内容。它只对常量发出警告,但它确实会继续进行更改。

让我们甚至更简洁地重写该代码:

class A
  TEST = "foo"
  TEST = "bar"
end
# (irb):3: warning: already initialized constant TEST

即使您没有真正更改常量,而只是将其设置为相同的值,也会出现警告。

class A
  TEST = "foo"
  TEST = "foo"
end
# (irb):3: warning: already initialized constant TEST

所以总的来说,它只是一个可以安全忽略的警告。

在 Rails 中:

在开发期间,Rails 会重新加载应用程序中任何更改的代码。较新版本的 Rails 在确定哪些文件实际发生了更改方面也非常聪明,然后只重新加载那些文件。所以假设你有这个控制器:

class TestController < ApplicationController
  FOO = "bar"

  def index
    ...
  end
end

如果您在其中进行任何更改,该文件将被重新加载。当文件重新加载时,FOO = "bar"再次解析,你最终会得到同样的警告。

解决方案:

  • 如果您使用的是 Rails 3.0 或 3.1,请尝试使用active_reload gem 并查看您的警告是否消失(它可能会在重新加载之前卸载您的类,这可能会导致警告消失)

  • 使用以下定义常量:

    FOO = "bar" unless const_defined?(:FOO)
    

    只有当它不存在时,这才会定义常量。所以你会避免警告。

  • 使用辅助方法来定义常量并自动执行此操作

    module ConstDefiner
      def define_constant(name, value)
        const_set(name, value) unless const_defined?(name)
      end
    end
    ActionController::Base.send :extend, ConstDefiner
    ActiveRecord::Base.send     :extend, ConstDefiner
    
    # Now in all your controllers/models:
    # instead of FOO = "value" unless const_defined?(:FOO), use:
    define_constant :FOO, "value"
    
  • 这些警告在开发模式下发生。您将不需要所有的unless const_defined?(:FOO)生产。通常不建议在生产中使用特定于开发的代码(除非它真的很重要)

  • 请记住,当您说 时FOO = "bar" unless const_defined?(:FOO),即使您真的对FOO常量进行了更改,它也不会在定义后重新加载。您必须停止并启动 rails 服务器才能重新加载它。但是与实际代码相比,在开发过程中修改常量的概率有点低,而且不是很频繁。如前所述,这不会以任何方式影响生产代码。

编辑:添加到 active_reload gem 的链接

于 2013-06-22T08:59:23.940 回答