Rails 3.1: how to run an initializer only for the web app (rails server/unicorn/etc)
Asked Answered
P

2

7

My webapp needs to encrypt its session data. What I setup is:

config/initializers/encryptor.rb:

require 'openssl'
require 'myapp/encryptor'

MyApp::Encryptor.config[ :random_key ] = OpenSSL::Random.random_bytes( 128 )
Session.delete_all

app/models/session.rb:

require 'attr_encrypted'

class Session < ActiveRecord::Base
  attr_accessible :session_id, :data
  attr_encryptor :data, :key => proc { MyApp::Encryptor.config[ :random_key ] }, :marshal => true

  # Rest of model stuff
end

That all works great, and keeps the session data secured. Here's the problem: when I run my custom rake tasks it loads the initializer and clears all the sessions. Not good!

What can I put in my initializer to make sure it ONLY runs for the webapp initialization? Or, what can I put in my initializer to make it NOT run for rake tasks?

Update: OK, what I've done for the moment is add MYAPP_IN_RAKE = true unless defined? MYAPP_IN_RAKE to my .rake file. And then in my initializer I do:

unless defined?( MYAPP_IN_RAKE ) && MYAPP_IN_RAKE
    # Web only initialization
end

Seems to work. But I'm open to other suggestions.

Prudy answered 22/9, 2011 at 0:15 Comment(3)
This seems liable to bite you in other ways in the future. Any time the server is restarted, you're going to lose all your sessions (which seems to be the intended behavior), but that includes the server getting autokilled by using too much memory, having a request run too long, etc.Maidenhair
I understand the downsides, but I think it is worth it. App being restarted resetting sessions: acceptable. Too much memory or too long of a request? Under unicorn these situations should only happen in a worker process. And since I set preload_app = true the initialization only happens in the master. So unicorn can respawn workers without causing a re-initialization.Prudy
Possible duplicate of Rails 3 initializers that run only on `rails server` and not `rails generate`, etcOttinger
B
9

You might make a modification to your application in `config/application.rb' like this:

module MyApp
  def self.rake?
    !!@rake
  end

  def self.rake=(value)
    @rake = !!value
  end

Then in your Rakefile you'd add this:

MyApp.rake = true

It's nice to use methods rather than constants since sometimes you'd prefer to change or redefine them later. Plus, they don't pollute the root namespace.

Here's a sample config/initializers/rake_environment_test.rb script:

if (MyApp.rake?)
  puts "In rake"
else
  puts "Not in rake"
end

The programmable nature of the Rakefile affords you significant flexibility.

Basilio answered 22/9, 2011 at 2:7 Comment(6)
OK, I like this. I've changed the setter to @rake = !!value unless defined? @rake to make sure it's only set once. Then I also added to environment.rb MyApp.rake = false. Sound reasonable?Prudy
Why would you worry about setting it only once? A better practice than simply ignoring the duplicate set is to at least emit a warning, or better, to throw an exception so you can track down the source of the problem.Basilio
I guess I'm concerned that while running as webapp code somehow the Rakefile will get loaded. I'm thinking symmetrically: as rake, the webapp environment is loaded, so I should also handle the case where as the webapp, rake gets loaded.Prudy
If you load your Rakefile from inside your app, you are doing something very peculiar. I don't think that even works.Basilio
Thanks, i was looking for this. It feels a little hacky, is this the only way to achieve this?Endblown
If by "hacky" you mean "not a standard use case" then yes, it is.Basilio
P
2

There is another work around:

unless ENV["RAILS_ENV"].nil? || ENV["RAILS_ENV"] == 'test'

When you launch with rake your ENV["RAILS_ENV"] will be nil. The test for 'test' is to avoid to run when using rspec.

I know that is reckon to use Rails.env but it return "development" when it is not initialised.

http://apidock.com/rails/Rails/env/class

# File railties/lib/rails.rb, line 55
def env
  @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] 
     || ENV["RACK_ENV"] || "development")
end
Phenocryst answered 17/2, 2014 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.