Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constants set in environment.rb disappear in development mode

Someone who understands how rails caching works can really help me out here. Here's the code, nested inside of the Rails::Initializer.run block:

config.after_initialize do
  SomeClass.const_set 'SOME_CONST', 'SOME_VAL'
end

Now if I run script/server and make a request, everything is dandy. However, on the second request to my Rails app, all goes to hell with an unitialized constant error. In production mode, I can make the second request successfully, meaning the constant is still there.

I've fixed the problem by changing the above to:

config.after_initialize do
  require 'some_class' # in RAILS_ROOT/lib/some_class.rb
  SomeClass.const_set 'SOME_CONST', 'SOME_VAL'
end

But now that means whenever I make a change to some_class.rb, I have to restart the server. Is there any way to set constants in an environment file and have them work correctly in development mode? Why does the constant exist on the first request, but not the following request?

UPDATE: Since environment.rb is only read when the Rails app is booted and I want both my lib files and models to be reloaded on each request, I was forced to move the constants into the some_class.rb file as follows:

if Rails.env.development?
  const_set 'SOME_CONST', 'SOME_DEVELOPMENT_VAL'
end

And in environments/production.rb, I have the old const_set code.

UPDATE: An even better method using config.to_prepare is detailed below.

like image 845
joshuaxls Avatar asked Apr 14 '09 02:04

joshuaxls


2 Answers

It only works on the first request in development mode because the classes are reloaded on each request. So on the first request the constant is set in the initializer, and all is good. Then on the next request, it reloads the class WITHOUT rerunning the bit from your initializer, so the constant isn't set from there on out.

It works in production mode because the classes aren't reloaded for each request, so you don't lose that bit of class state each time.

So you might want to set the constant either in the model, or in a config.to_prepare instead config.after_initialize. to_prepare is called before each request.

In the model:

class SomeClass < ActiveRecord::Base
  MY_CONST = "whatever"

  # You can access MY_CONST directly, but I tend to wrap them in a class 
  # method because literal constants often get refactored into the database.
  def self.my_const
    MY_CONST 
  end
end

In the config:

# This will run before every single request. You probably only want this in
# the development config.
config.to_prepare do
  SomeClass.const_set 'SOME_CONST', 'SOME_VAL'
end
like image 99
Sarah Mei Avatar answered Nov 10 '22 01:11

Sarah Mei


Production mode preloads all of the classes, whereas in development mode classes are loaded as needed, after the config files are read. Manually requiringing them in your configs forces the classes to be read before/during the config stage.

like image 1
Luke Avatar answered Nov 10 '22 02:11

Luke