Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

73 lines (55 sloc) 3.345 kB

require_hooks

This extension to ActiveSupport adds callbacks to be run before or after the dependency management loads specific files.

Register callbacks with #before_require and #after_require:

# app/models/post.rb
class Post < ActiveRecord::Base
end

# lib/my_hot_extension.rb
module MyHotExtension
  unloadable # prevent "TypeError: Can't dup NilClass"

  def self.included(post)
    post.instance_eval do
      has_many :whatnots
    end
  end
end

# config/initializers/my_hot_extension.rb
after_require 'post' do
  require_or_load 'my_hot_extension'
  Post.send :include, MyHotExtension
end

This can be handy if you want to transparently do things when a distant file is loaded (such as load local modifications), but you A) can't touch the distant file and B) you don't want to just eagerly load it, but react only if it is ever loaded.

“TypeError: Can't dup NilClass”

This problem isn't directly related to this gem, but it is certainly in the neighborhood of my intended usage for it.

Say you're writing a rails plugin that wants to provide an AR model or augment an application model. You put your extension in a module and have your users include it into their model class. When they fire up rails s, the first request they make works great, but the second one falls down crying something about duping NilClass. If they fire it up in production mode, it works fine.

You are being bit by some auto-loading magic rails does behind the scenes. Things in the user's app directory are auto-reloaded every request in development mode. Things in engines, plugins, etc, are auto-loaded once. This impedance mismatch between “every” and “once” is what's causing the pain: if you could change the “once”s to “every”s, you'd be fine.

There are some ways to proceed. The first is a monkey trick: explicitly mark your extension as “unloadable”, which tells rails to remove the module at the end of the request (in dev mode only.) This tricks the loader into concluding, on the next request, that your module hadn't been loaded after all (and so it hasn't been loaded once), and happily loads it again.

A better way may be to just ask rails to not treat your module as a “load once” dependency. In the console of an application using your extension, peek at ActiveSupport::Dependencies.autoload_paths and ActiveSupport::Dependencies.autoload_once_paths, which is a strict subset of the former. (Actually, rails 3 beta4 and earlier call them load_paths and load_once_paths.) Notice autoload_once_paths includes your extension's load paths; this is why rails treats it as a “load once” dependency. You can remove them from the array to fix the problem (they'll still be in autoload_paths.)

See also groups.google.com/group/rubyonrails-talk/browse_thread/thread/f54f18f4d4354926/ae7367ee33c07e97?show_docid=ae7367ee33c07e97

This area of the rails code appears to currently be in flux (as of just before 3.0.0 rc1.) If you are encountering this problem and the above doesn't look right or work for you, check out the autoload_once_paths and autoload_paths properties of your engine's configuration object.

Copyright

Copyright © 2010 Phil Smith. See LICENSE for details.

Jump to Line
Something went wrong with that request. Please try again.