views:

713

answers:

2

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.

+1  A: 

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.

Luke
I still wonder though why it works on the first request and not the following requests. And do you know of any way to set constants in an environment file such that they persist in development mode? I'm making lots of changes to some_class.rb and don't want to keep restarting the server.
joshuaxls
+5  A: 

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
Sarah Mei
Thank you, that's exactly the answer I was looking for.
joshuaxls