Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #6001 (closed defect: fixed)

Opened 2 years ago

Last modified 2 years ago

ApplicationController is not reloaded

Reported by: technoweenie@gmail.com Assigned to: David
Priority: normal Milestone: 1.2
Component: ActiveSupport Version: edge
Severity: normal Keywords: dependencies
Cc: chad@ramped.com, obrie, Ulysses, mislav

Description

My ApplicationController isn't reloading on edge rails 4890, because it's manually required by Dispatcher#prepare_application. I found this out because it wasn't loading a mixin on ApplicationController and bombing out on every request with:

A copy of AuthenticatedSystem has been removed from the module tree but is still active!

As a workaround, I renamed app/controllers/application.rb to application_controller.rb, and added an empty application.rb to keep the Dispatcher happy.

Attachments

reproduce.tar (10.0 kB) - added by mislav on 12/13/06 11:15:19.
minimal test case (so you don't have to create it manually): untar this in your plain application skeleton and allow it to overwrite "application.rb"

Change History

(in reply to: ↑ description ) 10/06/06 19:51:13 changed by matrix9180

  • cc set to chad@ramped.com.

Replying to technoweenie@gmail.com:

My ApplicationController isn't reloading on edge rails [4890], because it's manually required by Dispatcher#prepare_application. I found this out because it wasn't loading a mixin on ApplicationController and bombing out on every request with: {{{ A copy of AuthenticatedSystem has been removed from the module tree but is still active! }}} As a workaround, I renamed app/controllers/application.rb to application_controller.rb, and added an empty application.rb to keep the Dispatcher happy.

11/09/06 06:30:49 changed by obrie

  • cc changed from chad@ramped.com to chad@ramped.com, obrie.

11/09/06 09:22:28 changed by bitsweat

  • cc changed from chad@ramped.com, obrie to chad@ramped.com, obrie, Ulysses.

11/10/06 00:05:52 changed by ulysses

prepare_application now uses require_dependency, which should ensure that ApplicationController is reloaded. If this fixes the issue would anyone like to close?

11/10/06 00:35:47 changed by obrie

  • status changed from new to closed.
  • resolution set to fixed.

Although my problem was not being caused by the same plugin in this ticket's description (I'm assuming it's acts_as_authenticated), it was producing the same error with a different class name. I can confirm that the workaround was fixing my problem and that with the latest version of Edge, the workaround is no longer required.

Fixed in [5471].

11/27/06 10:27:20 changed by lastobelus

  • status changed from closed to reopened.
  • resolution deleted.

I am still having this problem, with the latest (5640) version of edge rails.

If I do the suggested workaround, acts_as_authenticated reloads, but then the tabnav plugin does not. In this case, I do not get the "removed from module tree" error, but rather a method missing error for the ApplicationHelper methods that the tabnav plugin defines in it's ApplicationHelper mixin.

To clarify, after I renamed app/controllers/application.rb to application_controller.rb, and added an empty application.rb, authenticated system seems to reload correctly, but the mixins defined in tabnav plugin do not, and on the second request I get missing method errors for the tab_nav helpers.

11/27/06 13:16:43 changed by lastobelus

  • status changed from reopened to closed.
  • resolution set to fixed.

After some trial and error I discovered the problem was caused by Streamlined. I uninstalled Streamlined, and now things work as expected.

12/13/06 11:11:34 changed by mislav

  • cc changed from chad@ramped.com, obrie, Ulysses to chad@ramped.com, obrie, Ulysses, mislav.
  • status changed from closed to reopened.
  • resolution deleted.

I've encountered this problem when trying to have a controller in a plugin. It is very weird!

Steps to reproduce:

  • generate a new application
  • add a 'foo' controller with an index method that renders some text
  • add a 'bar' controller, but put this one in "vendor/plugins/barplug/lib"
  • write the following to "barplug/init.rb": config.controller_paths << lib_path

OK, we've added "barplug/lib" to controller_paths so the controller can be auto-discovered.

Fire up the console:

>> app.get 'foo'
=> 200
>> app.get 'bar'
=> 200
>> app.get 'bar'
=> 200

So far so good, but see what happens next! Add a Dummy (empty) class to "app/models/dummy.rb". Open up "app/controllers/application.rb", create a new before_filter and reference "Dummy" from it. The Dummy class should be now autoloaded before any controller action ... right??

Fire up the console again:

>> app.get 'foo'
=> 200
>> app.get 'foo'
=> 200
>> app.get 'bar'
=> 200
>> app.get 'bar'
=> 500

See the last result - server error. When we try to render some action from 'bar' more than once it errors (the same error this ticket is about). The error is raised when looking up "Dummy" in the before_filter, but it's not Dummy that causes this problems: it's Dependencies.load_missing_constant:

unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id
  raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
end

In this code bit, from_mod is ApplicationController.

I still don't know where lies the problem. We've got 2 controllers in different directories, one before_filter in ApplicationController and a model (Dummy). One controller works without problems, while the other fails on all requests after the first one! Remeber, the only difference between these controllers is their name and their parent directory.

12/13/06 11:15:19 changed by mislav

  • attachment reproduce.tar added.

minimal test case (so you don't have to create it manually): untar this in your plain application skeleton and allow it to overwrite "application.rb"

12/13/06 11:33:09 changed by mislav

I have packed up a minimal test case. Steps to reproduce this are really easy now:

rails testapp
cd testapp
rake rails:freeze:edge
[download reproduce.tar]
tar -xf reproduce.tar

script/console
>> app.get 'foo' #=> 200
>> app.get 'foo' #=> 200
>> app.get 'bar' #=> 200
>> app.get 'bar' #=> 500??

12/13/06 11:49:31 changed by mislav

Woah, I've forgot to tell you one important bit. When the console is loaded ApplicationController is required explicitly - we don't want that because this isn't what happens when server runs the application.

Uncomment these lines from "railties/lib/console_with_helpers.rb"

require 'application'
@controller = ApplicationController.new

If you don't do this, testing from the console will pass and you will have to fire up a server and require '/bar' 2 times to see the error.

12/13/06 12:23:05 changed by mislav

  • keywords set to dependencies.

Sorry, "uncomment" above should really be "comment".

I have narrowed the problem down. It isn't about ApplicationController not being unloaded properly (it is); it is about BarController never being unloaded. This is a Dependencies issue and I will try to fix it. (I have to learn Dependencies first.)

12/13/06 15:27:20 changed by mislav

  • status changed from reopened to closed.
  • resolution set to fixed.

I turns out this is by design, and the problem even wasn't related to the original problem at all (except for the same error which has led me to believe ApplicationController isn't reloaded properly).

The culprits are Dependencies.loadable_constants_for_path and load_once_paths. There is some heavy paths and module names hacking involved in Dependencies, but it suffices to say that stuff loaded from plugins' lib directories isn't meant to be reloaded. Rails Initializer adds plugins to load_once_paths and that makes Dependencies unable to mark these new constants (eg. "BarController") as unloadable.

The bottom line is: if you're going to subclass an unloadable class (ApplicationController), better ensure that your class is unloadable, too. So if it's in a plugin, mark it explicitly:

class BarController < ApplicationController
  unloadable

  def index
    # ...
  end
end

Problem solved.