Filter defined in lib file required in environment.rb disappears from filter_chain in production environment. Why?

Hi all,

I have initially posted this question to stack overflow but I think
it's specific enough that it warrants being posted here. I hope this
is not bashed as crossposting.

Here's the original question:
http://stackoverflow.com/questions/620146/rails-filter-defined-in-lib-file-required-in-environment-rb-disappears-from-filt

It's up to you whether you want to answer here or there, I'll make
sure that the final solution is posted to both places.

So here's the question text reproduced:

In my rails application, I have a file in lib that, among other
things, sets up a filter that runs on all controllers.

When running under development environment, everything runs fine.
However, under production the filter goes missing. Funny thing is, by
inspecting the `filter_chain`, I noticed other filters remain, eg.
those defined in plugins, or later in the specific controller class.

I've tested this with both rails edge and v2.3.0.

I've isolated the behavior to the following tiny test case:

    # app/controllers/foo_controller.rb
    class FooController < ApplicationController
      def index
        render :text => 'not filtered'
      end
    end

    # lib/foobar.rb
    ActionController::Base.class_eval do
      before_filter :foobar
      def foobar
        render :text => 'hi from foobar filter'
      end
    end

    # config/environment.rb (at end of file)
    require 'foobar'

Because cache_classes does a little more than just that. The way
before filters work, if Foo < Bar then only those filters defined in
Bar at that point will be inherited by Foo - adding them to Bar at a
later date will not do anythingn

In development mode, the app starts, your file is required, the filter
added to ActionController::Base. Later the first request comes along,
the controller is loaded and it inherits that filter.

When cache_classes is true then all of your application classes are
loaded ahead of time. This happens before your file is required, so
all of your controllers already exist when that file is run and so it
has no effect. You could solve this by requiring this file from an
initializer (ensuring it runs before app classes are loaded), but
really why wouldn;t you just put this in application.rb ?

Fred

Hi, thanks for the detailed answer, I thought in production files were
still loaded on-demand, just never reloaded. (I guess that's how it
was aeons ago).

has no effect. You could solve this by requiring this file from an
initializer (ensuring it runs before app classes are loaded), but

In my minimal test case, yes that did it. In my actual application the
code in the lib file depended on libraries that were being required
later in the environment, so I had to do a bit more of moving code
around, but it seems to have worked.

really why wouldn;t you just put this in application.rb ?

Oh that's a long story… bottomline is that it's a file that may be
shared among many applications.

But really, then the question is, why not make it a plugin, and well,
maybe I should do it, but I was still curious about what in the
loading process was causing the code to break, since I knew it worked
in older rails apps I had.

Hi, thanks for the detailed answer, I thought in production files were
still loaded on-demand, just never reloaded. (I guess that's how it
was aeons ago).

That changed in 2.2. The change is mainly needed for thread safeness,
since require is fundamentally thread dangerous in ruby so you need to
get all of that out of the way at app startup.

Fred

In general you don't want to be doing anything at the bottom of
environment.rb because of this whole loading models/controllers ahead
of time (eg if you required a gem that a model needs when it is loaded
then your app will die on startup in production mode).

Fred

In general you don't want to be doing anything at the bottom of
environment.rb because of this whole loading models/controllers ahead
of time (eg if you required a gem that a model needs when it is loaded
then your app will die on startup in production mode).

Yeah. My real case was way more involved, and this is the way I found
to work solve the issue:

    config.after_initialize do
      require 'foobar'
    end

The `after_initialize` runs after the framework has been initialized
but before it loads the application files, hence, it'll affect
ActionPack::Base after it's been loaded, but before the application
controllers are.

I guess that's the safe way to deal with all the preloading that goes
on.