0.0
Repository is archived
No commit activity in last 3 years
No release in over 3 years
Extend ActiveSupport to run blocks before or after a file is "require_or_load"ed into memory.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

>= 1.2.9

Runtime

= 3.0.0.beta4
 Project Readme

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 © 2010 Phil Smith. See LICENSE for details.