Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constants in active_admin's directory warn about re-declaration

I'm using active_admin and I have a directory admin in app in a Rails 3 app with the declarations of models and pages. Every now and then I also have a class and when that class has a constant, like this:

class Foo
  BAR = "bar"
end

then, I get this warning in every request that has to re-load some code in my Rails app:

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

Any ideas what's going and how to avoid these warnings?

like image 580
pupeno Avatar asked Jun 10 '13 16:06

pupeno


1 Answers

In Pure Ruby:

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

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

In Ruby, you can open a class anytime and redeclare whatever is inside it. It raises a warning only for constants, but it does go ahead and make the change though.

Let's even re-write that code a bit more terse:

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

The warning appears even if you're not really changing the constant, but just setting it to the same value.

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

So overall its just a warning that can be safely ignored.

In Rails:

During Development, Rails reloads whatever changed code in your app. Newer versions of Rails are also pretty smart in figuring out which files have actually changed, and then reloads only those files. So assume you have this controller:

class TestController < ApplicationController
  FOO = "bar"

  def index
    ...
  end
end

If you make any change inside this, this file will get reloaded. When the file is reloaded, FOO = "bar" is parsed again, and you end up getting the same warning.

Solutions:

  • If you're using Rails 3.0 or 3.1, try the active_reload gem and see if your warning goes away (it might unload your classes before reloading them, which might cause the warning to go away)

  • Use the following to define constants:

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

    This will define the constant only if it is not already present. So you will avoid the warnings.

  • Use a helper method to define constants and automate this

    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"
    
  • These warnings happen only in Development mode. You won't be needing all the unless const_defined?(:FOO) in production. Its generally not recommended to have development-specific code in production (unless its really really essential)

  • Remember that when you say FOO = "bar" unless const_defined?(:FOO), then even if you really make a change to the FOO constant, it won't get reloaded once its defined. You'll have to stop and start rails server in order to reload it. But the probability of modifying constants during development is a little low when compared to actual code, and not something very frequent. As already said, this doesn't affect production code in any way.

Edit: Added link to active_reload gem

like image 68
Subhas Avatar answered Nov 16 '22 19:11

Subhas