161

I'm pretty sure the error has nothing to do with the actual content of the TenantIdLoader module. Instead, it has something to do with ActiveSupport Dependencies.

I can't seem to get past this error. From what I've read, it's because either ActiveRecord::Base is getting reloaded or Company::TenantIdLoader is getting reloaded, and it's somehow not communicating that. Help, please! I'd really like to be able to get upgraded to Rails 4.2.

EDIT

I've now learned that it's because I'm referencing Tenant which is getting reloaded automatically. I need to be able to actually reference the class though, so does anyone know how to get around this?

config/application.rb

config.autoload_paths += %W( #{config.root}/lib/company )

config/initializers/company.rb

ActionMailer::Base.send(:include, Company::TenantIdLoader)

lib/company/tenant_id_loader.rb

module Company
  module TenantIdLoader

    extend ActiveSupport::Concern

    included do
      cattr_accessor :tenant_dependency
      self.tenant_dependency = {}
  
      after_initialize do
        self.tenant_id = Tenant.active.id if self.class.tenant_dependent? and self.new_record? and Tenant.active.present? and !Tenant.active.zero?
      end
    end

    # class methods to be mixed in
    module ClassMethods
  
      # returns true if this model's table has a tenant_id
      def tenant_dependent?
        self.tenant_dependency[self.table_name] ||= self.column_names.include?('tenant_id')
      end
  
    end

  end
end
Community
  • 1
  • 1
kddeisz
  • 5,162
  • 3
  • 21
  • 44
  • 3
    Does this answer help at all? http://stackoverflow.com/questions/17561697/argumenterror-a-copy-of-applicationcontroller-has-been-removed-from-the-module/23008837#23008837 – Waynn Lue Apr 17 '15 at 17:45
  • Are you sure the Tenant class is involved? If you stub out the bits of that code that use Tenant do you still get an error? – Frederick Cheung Apr 17 '15 at 17:46
  • @WaynnLue yeah I think that's the reason, I just don't know how to fix it. – kddeisz Apr 17 '15 at 17:48
  • @FrederickCheung I've got another file similar to this one that is erroring in the same way, and it always errors on the line related to Tenant, so it'd my best guess. – kddeisz Apr 17 '15 at 17:49
  • Why guess when you could be sure by just hardcoding default_url_options ? – Frederick Cheung Apr 17 '15 at 17:50
  • @FrederickCheung after further testing, yes it definitely has to do with Tenant. – kddeisz Apr 17 '15 at 17:55
  • Which is the line that throws the error? – Frederick Cheung Apr 17 '15 at 17:56
  • I've edited to show the simpler module. It errors on the self.tenant_id = line. – kddeisz Apr 17 '15 at 17:58
  • 1
    Although you are not using Wisper in Rails here, it may be useful to other people to note that Wisper causes this problem fairly consistently if you do not follow the advice in this thread: https://stackoverflow.com/questions/28346609/reload-wisper-listeners-automatically-at-every-request/28362286#28362286 – Steve N Feb 04 '16 at 09:51

6 Answers6

221

Tenant is sort of a red herring - the error would occur if you referenced any bit of app that needs to be loaded by rails' const_missing trick.

The problem is that you are taking something reloadable (your module) and then including it in something not reloadable (ActiveRecord::Base or, in your earlier example ActionMailer::Base). At some point your code is reloaded and now ActiveRecord still has this module included in it even though rails thinks it has unloaded it. The error occurs when you reference Tenant because that causes rails to run its const_missing hooks to find out where Tenant should be loaded from and that code freaks out because the module where the constant search is starting from shouldn't be there.

There are 3 possible solutions:

  1. Stop including your module into non reloadable classes - either include into individual models, controllers as needed or create an abstract base class and include the module in there.

  2. Make this module non reloadable by storing it somewhere that isn't in autoload_paths (you'll have to require it explicitly since rails will no longer load it magically for you)

  3. Changing Tenant to ::Tenant (Object.const_missing will then be invoked, not Tenant.const_missing)

Paweł Gościcki
  • 9,066
  • 5
  • 70
  • 81
Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
  • 40
    I seem to have found a third solution, though I was wondering if you know why it works. If I reference it was ::Tenant, everything works out magically. Possibly because it's then loading it as a top-level constant? Maybe? – kddeisz Apr 19 '15 at 13:46
  • 3
    then it's Object.const_missing that will be invoked not YourModule.const_missing so things should work out – Frederick Cheung Apr 19 '15 at 13:48
  • 7
    Backing out to top-level using `::` worked for me, too! – Alex Moore-Niemi May 15 '15 at 20:43
  • 12
    I had this issue occurring from time to time and in my case it was related to spring, so doing `./bin/spring stop` was resolving it. – santuxus Feb 08 '17 at 14:31
  • instead of including in AR::Base i just include everything into ApplicationRecord since Rails5 hast that included. great idea ;) – Tim Kretschmer Feb 10 '17 at 06:22
  • The Rails is not getting the 'Tenant' constant. So mention the Full scope of the Tenant name to make it work. It has some namespace issue I guess – Abhi Apr 03 '17 at 10:30
  • Adding my variation on this issue — it can be difficult to figure out what the "offending" class/module actually is. In my case, it was a Rails model being referenced in a class that was in a *subfolder* of an autoload path (and thus didn't seem to count as autoloading). Prefixing the model with `::` fixed it. – FeifanZ Feb 27 '18 at 01:16
  • 7
    I LOVE that this is a *runtime* Ruby/Rails error - unlike any other language, dynamic or not, Ruby gives developers the true unlimited flexibility to have literally no idea where modules are defined until your program *executes* (and in what *order* it executes). It's so well designed. – Andy Ray Sep 21 '18 at 22:24
  • @FrederickCheung I want to try creating an abstract base class and including modules there. Can you point to any resources on how to do that? – bigmugcup Jul 15 '20 at 02:16
52

Changing ModuleName to ::ModuleName worked for me.

Aman Kumar
  • 987
  • 1
  • 9
  • 14
7

Not sure if this will help anyone, but I had this suddenly start happening after a change that seemed unrelated. It went away after I restarted the application server.

beef_boolean
  • 147
  • 3
  • 3
0

Changing ModuleName to 'ModuleName'.constantize solved the issue for me.

Qortex
  • 7,087
  • 3
  • 42
  • 59
0

Another way to solve this issue is to require the module directly in the file that is not reloadable.

At the top of lib/company/tenant_id_loader.rb put require_relative '../../app/models/tenant' or whatever the path is relative to the id loader to the tenant model.

derigible
  • 964
  • 5
  • 15
  • 32
-4

What worked for me:

Update config.eager_load = false to true

in config/environments/development.rb

Ruby 2.6.5
Rails 5.1.6

Jan Werkhoven
  • 2,656
  • 1
  • 22
  • 31
  • 4
    Yeah definitely don't do this. That's going to kill your ability to reload code in development. – kddeisz Mar 03 '20 at 17:00