Forum: Rails Engines solved: plugin models unloading after first request

D2e767681e3f0c502225d4733a8e939b?d=identicon&s=25 elijah (Guest)
on 2008-09-24 11:09
(Received via mailing list)
I haven't been able to find this advice anywhere else, so I am posting
here so that it may help another lost soul.

Some plugins have a problem: if a plugin applies a mixin directly to a
model in app/models, this mixin gets unloaded by rails after the first
request. This only happens in development mode. The symptom of this is
an application that works for the first request but fails on subsequent

A call to Dispatcher.to_prepare will get around this problem by
re-applying any mixin that modifies the core models on each request. In
production mode, the classes are not unloaded and so the mixin is only
applied once.

"Normal" plugins don't have this problem: they modify active record, and
then the core models call these extensions. When these core models are
reloaded by rails, the plugin code is then reloaded as well. The problem
only shows up when you want to modify a model in apps/model without
requiring any code change to that model.

This style of using plugins is not considered to be a good idea by many.
However, I am writing plugins that are designed exclusively for a
particular application. These plugins enable optional features of a
specific application--they do not try to modify the behavior of rails or
extend active record.

Here is the code:

(1) modify Engines:Plugin

require 'dispatcher'

Engines::Plugin.class_eval do
  def apply_mixin_to_model(model_class, mixin_module)
    Dispatcher.to_prepare {
      model_class  = Kernel.const_get(model_class.to_s)  # \ weird, yet
      mixin_module = Kernel.const_get(mixin_module.to_s) # / required.
      model_class.send(:extend,  mixin_module.const_get("ClassMethods"))
      model_class.instance_eval &(mixin_module.class_definition())

(2) in your plugin's init.rb:

# in this case, there is a model app/models/language.rb, and module
# vendor/plugins/myplugin/app/models/language_extension.rb

apply_mixin_to_model(Language, LanguageExtension)

(3) this is what my language_extension.rb looks like:

module LanguageExtension
  module ClassMethods

  module InstanceMethods
    def percent_complete()
      count = Key.count_all
      if count > 0
        (Key.translated(self).count / count * 100.0).round.to_s + '%'

  def self.class_definition
    lambda {
      has_many :translations, :dependent => :destroy


Most the code in apply_mixin_to_model is not really required. But it
seemed to me a little repetitive to type this over and over again for
each mixin:

module MyModule
  def self.included(base)
    base.instance_eval do
      include InstanceMethods
      has_many :somethings

So, most the code in apply_mixin_to_model is just doing that repeated
stuff for you, so long as the submodules are named right (ie
InstanceMethods, etc).

The simple thing to do is just put:

Dispatcher.to_prepare {
  Language.send(:include, LanguageExtension)

In the plugin's init.rb. But again, I am trying to get fancy, perhaps at
my own peril.

Hope that helps someone,
E4f80e42794094c98a40bfebef4ec292?d=identicon&s=25 Alex Sharp (ajsharp)
on 2008-11-17 09:22
I cannot thank you enough for posting this. Have you found a more
elegant way to handle this?
This topic is locked and can not be replied to.