Include Module into ruby class in a Rails initializer

2.5k views Asked by At

Inside of an initializer in Rails i am trying to include a module in a rails Model

User.send :include, Something

This works for the first request but does not for the second/third/etc. due to rails model reloading. I tried using ActionDispatch::Callbacks.to_prepare (ActionDispatch::Callbacks.to_prepare, i'm using Rails 3.0.9)

ActionDispatch::Callbacks.to_prepare do
  User.send :include, Something
end

but i keep getting a NoMethod error from a user instance when i try to call a method defined in my module module on the second/third/etc. request.

My question is this: Is there a way to reliably include a module into a Rails Model in an initializer without any development weirdness?

Update: Not Possible

Apparently this is impossible to do without middleware. If you disagree, add an answer below. This wasn't an acceptable solution for my use case, so I actually didn't even try it. Good luck.

Edit: Updated Debugging Info

I was playing around with the ActionDispatch::Callbacks.to_prepare a bit more and i noticed something strange when i put this in my initializer:

ActionDispatch::Callbacks.to_prepare do
  puts "to_prepare ==:#{User.object_id}"
end

and this in my controller

puts "controller ==:#{User.object_id}"

on the first request I get this:

to_prepare ==: 2297196200
controller ==: 2297196200
to_prepare ==: 2297196200
to_prepare ==: 2324202920
to_prepare ==: 2318560780

on the second request i get this:

to_prepare ==: 2326823900
controller ==: 2326823900
to_prepare ==: 2317901920
to_prepare ==: 2326746040
to_prepare ==: 2314369160

The first thing i noticed was the multiple calls to to_prepare which is weird. The second thing i noticed was in the first request (which works) the object_id directly before and after controller are called are the same, and they are not in any subsequent calls. If anyone could shed some light on why this is happening and how to get around it, it would be much appreciated.

1

There are 1 answers

2
d11wtq On BEST ANSWER

Does using a config.after_initialize hook help? This is a development-environment-specific problem, due to class reloading taking place.

The one thing that would almost certainly work would be to inject your module via a middleware, though it's difficult to know if that's a suitable solution for whatever you're doing.

class AddYourModuleMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    User.send(:include, YourModule) unless User < YourModule
    @app.call(env)
  end
end

Add it to the middleware chain with config.middleware.use AddYourModuleMiddleware, or alternatively by placing use AddYourMiddleware at the start of ApplicationController.