1

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.

Schneems
  • 14,918
  • 9
  • 57
  • 84
  • Is there a reason this should be done in an initializer? include Something in the User model would work in any environment. – bandito Oct 12 '11 at 20:27
  • @bandito, This is a simplified version of my problem. There is a reason that this needs to be done in an initializer... a bit too long to go into here. In short i'm baking this into a gem, and one class needs to know other classes that implement an interface. This isn't the first time i've wanted to do this sort of thing and been thwarted by Rails development model reloading. – Schneems Oct 12 '11 at 22:10
  • Rails reloads your controllers and models (and other things) between requests in development. The first request is not likely to be the same as all subsequent requests. I don't know what the internals are (you'd have to read the source code on github), but that's why you're seeing the object_id's change this way. It's also why I think an initializer is out of the question, as it doesn't run per-request. – d11wtq Oct 16 '11 at 00:47
  • If you're writing a gem, you could probably use railties to inject the middleware without concerning the user with it. – d11wtq Oct 16 '11 at 00:51

1 Answers1

1

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.

d11wtq
  • 34,788
  • 19
  • 120
  • 195
  • Have you tried this? Does the `User.object_id` differ between the controller and in the middleware? While this is an interesting suggestion, there should be a way to do this without requiring I put middleware in my gem. – Schneems Oct 15 '11 at 15:26
  • I haven't tried it, no, sorry... but I'm 99% it would work as we use middleware for other things we need to do to prepare a request. – d11wtq Oct 16 '11 at 00:48