views:

99

answers:

1

I've got a Rails metal class that basically does a one time check to make sure there is an admin user in the system, if not, it redirects user to create one. Right now I'm using the rack session to prevent double checking but that seems to have the following problems:

  • Still does one time check for each session
  • Slows things down by checking the session in the first place.

I wonder if its possible to direct Rails to "remove" or "unload" the class from the chain. Can this be done safely? Any other suggestions?

+2  A: 

A simplest solution (although not quite as clean) would be to store the fact that an admin user exists in the class.

class EnsureAdminUser
  def self.call(env)
    if @admin_defined or Admin.any?
      @admin_defined = true
      [404, {"Content-Type" => "text/html"}, "Not Found"]
    else
      …
    end
  end
end

This saves you the DB hit on each request.

To actually delete the metal you will need to do something a bit more radical (and evil):

ObjectSpace.each_object(Rails::Rack::Metal){|metal_handler|
  metal_handler.instance_eval{ @metals.delete(EnsureAdminUser) }
}
cwninja
This seems like a definite improvement since I am not relying on the session for storage. I was under the impression the metal classes are going to be loaded from scratch each request. If so, this doesn't appear to be any faster. Am I wrong about that assumption?
schof
Looking at the source (http://github.com/rails/rails/blob/2-3-stable/railties/lib/rails/rack/metal.rb), the metals are loaded when the app is started. I've added another answer with instructions on how to actually delete it. But it's a bad idea.
cwninja
Correction, edited original answer instead.
cwninja
Thanks for the insight. I agree its probably a bad idea to delete if we can essentially check only once. Is there any benefit to using @@admin_defined (class variable)? I see what you mean by metals being loaded when Rails starts. Are you sure the DB check will only happen a finite number of times when we've got a bunch of Passenger processes running? My knowledge of the server internals is a bit lacking.
schof
@admin_defined should be fine (it's on the class due to the call being a class method). There is only one instance of the metal for the lifetime of the passenger process. This time varies, but under load the passengers should remain up for a while (we have ours configured to restart once every 1,000 requests).
cwninja