"Unable to autoload constant User" error when changed code in development
Asked Answered
A

4

10

I have a problem with my rails application. After an Update from Rails 3 to 4. When I surf through the pages after starting the server in development mode everything is fine.

But after a single code change (even adding a space) every page request shows the following error.

Unable to autoload constant User, expected /path/to/my/rails-app/app/models/user.rb to define it

The file lives exactly there and defines the class:

class User < ActiveRecord::Base
  …

I tried many things with config.autoload_paths and config.eager_load_paths in application.rb but with no luck. Deactivating spring did not help either.

Developing an app and having to restart the server after every single change seems so 90s.

$ rails -v
Rails 4.2.4
$ ruby -v
ruby 2.1.7p400 (2015-08-18 revision 51632) [x86_64-linux]

Some relevant configs:

development.rb

MyApp::Application.configure do
    # Settings specified here will take precedence over those in config/application.rb

  # In the development environment your application's code is reloaded on
  # every request.  This slows down response time but is perfect for development
  # since you don't have to restart the webserver when you make code changes.
  config.cache_classes = false

  # Do not eager load code on boot. This avoids loading your whole application
  # just for the purpose of running a single test. If you are using a tool that
  # preloads Rails for running tests, you may have to set it to true.
  config.eager_load = false

  # Show full error reports and disable caching
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Don't care if the mailer can't send
  config.action_mailer.raise_delivery_errors = false

  # Print deprecation notices to the Rails logger
  config.active_support.deprecation = :log

  # Only use best-standards-support built into browsers
  config.action_dispatch.best_standards_support = :builtin

  # Do not compress assets
  config.assets.compress = false

  # Expands the lines which load the assets
  config.assets.debug = true

  config.action_mailer.delivery_method = :test
  config.action_mailer.default_url_options = {
    host: 'localhost',
    port: 3000
  }

end

application.rb

module Serviceportal
  class Application < Rails::Application    
    # Enable the asset pipeline
    config.assets.enabled = true

    # Version of your assets, change this if you want to expire all your assets
    config.assets.version = '1.0'

    [… some asset precompile stuff …]

    # Configure the default encoding used in templates for Ruby 1.9.
    config.encoding = 'utf-8'

    # Custom directories with classes and modules you want to be autoloadable.
    config.autoload_paths += Dir["#{config.root}/app/mailers",
        "#{config.root}/app/controllers/concerns",
        "#{config.root}/app/models/concerns",
        "#{config.root}/app/decorators/concerns",
        "#{config.root}/lib",
        "#{config.root}/lib/shared"
    ]
    config.eager_load_paths += Dir["#{config.root}/app/mailers",
        "#{config.root}/app/controllers/concerns",
        "#{config.root}/app/models/concerns",
        "#{config.root}/app/decorators/concerns",
        "#{config.root}/lib",
        "#{config.root}/lib/shared"]

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    config.time_zone = 'Berlin'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    config.i18n.default_locale = :de

    [… some SQL and active support stuff …]

    config.action_controller.include_all_helpers = false

    config.action_controller.action_on_unpermitted_parameters = :raise

    # Do not swallow errors in after_commit/after_rollback callbacks.
    config.active_record.raise_in_transactional_callbacks = true
  end
end

Edit: The error mostly shows up in lib/auth/user_proxy.rb in the following function. Maybe this helps to narrow the range of possible causes.

def self.usertype_allowed?(type)
  [ User, TempCustomer ].include? type.classify.safe_constantize rescue false
end

Edit 2: Stringify the class names in Edit 1 helped (thanks @Benjamin Sinclaire). But only leads to the next errors. I could also avoid using classes. But at the following error in app/controllers/concerns/security.rb there is nothing can change?

Unable to autoload constant User, expected /path/to/my/rails-app/app/models/user.rb to define it

code:

def set_current_user
  User.current = current_user
end

with current user saved in the Thread (code from /path/to/my/rails-app/app/models/user.rb

def self.current
  Thread.current['current_user']
end

def self.current=(user)
  Thread.current['current_user'] = user
end

Just to make it clear again: It works after server restart in development until I change some code somewhere.

Adultery answered 23/9, 2015 at 7:3 Comment(12)
maybe the User class is also declared somewhere else in your code?Caesaria
where is the class located ?Dc
The class is exactly where the error message wants it to be. And I also do not have another class. Please not that it works until I change code. When I restart the server everything is fine again until the next change.Adultery
What tutorial did you follow? Have you tried this tutorial railscasts.com/episodes/415-upgrading-to-rails-4?Zee
Can you share your project or at least give us code of the User model?Vermeil
Sandro, we can't help you, without seeing your configs :(Dc
I followed the offical upgrade guidelines: edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html (up to 4.2).Adultery
Can you share the project?Frock
It is a closed-source website for a customer so I am unfortunately not able share the whole code. I added some configs to the question. Maybe this helps? The user model is nothing special. As I said, everything works in production but not for the second request after starting the server in development.Adultery
I had this problem a while ago. In my case I used modules (API::V1::UserController) which throwed this autoload constant once in a while. It's definitely one of the most painful bugs/errors in Rails because it doesn't give much detail. Do you use modules in your Rails app? I will look into this tomorrow & try to reproduce it somehow...Duckpin
Yepp, I use many modules. I also have API::V1::Foo style Modules. I am very excited, what you will find out and how you solved your problem.Adultery
@bigsolom answer my attention to the fact that the error happens mostly on the same place. I showed this in my current edit of the question. Is this a help?Adultery
A
0

During a Rails/Ruby Update I found time to look into this and finally found the cause.

The user class had an unloadable in it for years. That caused the problems since Rails 4. Now I removed this and found no issues after that.

Adultery answered 14/5, 2019 at 7:58 Comment(0)
G
6

1 See if you have any multiple-level class or module declaration done one one line and change them to be declared in several lines.

Instead of

class Parent::Sub::Child
end

Do

module Parent
  module Sub
    class Child
    end
  end
end

2 Check your model association definitions, and ensure you are never using constant. Use string instead.

Instead of

belongs_to :manager, class_name: User

Do

belongs_to :manager, class_name: 'User'

3 Just saw your edit. Can you refactor like this?

# I assume `type` is a string or such, so we can compare classes
# names instead of constants, and get rid of `safe_constantize`
def self.usertype_allowed?(type)
  ['User', 'TempCustomer'].include? type.classify rescue false
end

4 Not a good idea to serialize an active record object in the Thread storage. Change it to store the user id instead, like this:

def set_current_user
  User.current = current_user.id
end

def self.current
  Thread.current['current_user_id']
end

def self.current=(user_id)
  Thread.current['current_user_id'] = user_id
end
Guanabana answered 29/9, 2015 at 8:44 Comment(9)
I change every multi-level class I found but this did not help. I did not find any model association definition without an string. So no luck :(Adultery
I did not specified, but I assume you restarted your server after your changes?Guanabana
At that point I would be searching for all occurrences of User in my code and try laboriously to detect an incorrect usage of the constant. BTW are you using nested form with accepts_nested_attributes_for? Because I've seen cases where the "blank" form partial for the nested model causes problems.Guanabana
Thanks for your edit #3. This helped but led to some errors. See my edit #2.Adultery
Could you share the definition of User#current please?Guanabana
Let us continue this discussion in chat.Adultery
Sorry I missed your invitation. I'm not at home. I shall be back tonight (UTC).Guanabana
Let me know for Edit#4, I have another idea if this is not workingGuanabana
I made the suggested changes. I had to save also the class of the current user. But that did not help. It now has an error in app/controllers/concerns/security.rb method login_from_session at the line self.current_user = ::Auth::UserProxy.find(Object.const_get(session[:user_type]), session[:user_id]) if session[:user_id]. It also says Unable to autoload constant User. Again only when I changed something in code after restarting the server.Adultery
T
1

You don't need include app/models/concerns and app/controllers/concerns in your autoload/ eagerload paths as they are included by default in Rails 4: https://signalvnoise.com/posts/3372-put-chubby-models-on-a-diet-with-concerns

Also make sure that your concerns are defined as modules, extend ActiveSupport::Concern and with the appropriate file name

#taggable.rb
module Taggable
  extend ActiveSupport::Concern
end

Another cause of your problem might be that some modules/ classes in app/decorators/concerns, lib, lib/shared are using the User class which is not loaded yet or some of it's dependencies are not loaded so try adding require_relative path_to_user.rb at the top of those files

-----Edit-------

Try adding at the top of lib/auth/user_proxy.rb

require_dependency 'app/models/user'

This way you'll remove any ambiguity in autoloading the User class and you won't mess around with Rails autoloading see more here: http://guides.rubyonrails.org/autoloading_and_reloading_constants.html#require-dependency , http://guides.rubyonrails.org/autoloading_and_reloading_constants.html#common-gotchas

Ternan answered 29/9, 2015 at 11:52 Comment(3)
I removed the concerns from eager loading. Thank you for the hint. I also required the User class in lib/auth/user_proxy.rb, but that also did not help. But this brought my attention to the fact, that the error happens mostly on the same place. I showed this in my current edit of the question.Adultery
Thank you. Looks very promising and the links are very interesting. But it did not resolve the problem. There was now a problem in app/controllers/concernc/security.rb. There it says it can not find User class and require_dependency it too does also not help. Kind of frustrating.Adultery
@SandroL can you post the code of the concern that's causing the error and how are you including it in your controllersTernan
M
1

Same problem but in an engine w/ namespaces. No issues in production or in development until a code-change / autoload.

The solution was to

  1. checking for double definitions (there were none)
  2. checking if the module nesting strictly follows rails conventions in the filesystem.

I've had myns under myengine/app/myns/subns/obj.rb but myns is being ignored as it is at the root of the app folder, so moving the myns folder into a subfolder myengine/app/lib/myns solved the issue.

Note: the rails error message was very explicit about the module nesting (while still pointing to the wrong .rb file in the filesystem) so look closely at the error. The error was 'Unable to autoload constant subns/obj.rb in .../myns/subns/obj.rb'. Rails suggesting the incorrect file-location (which exists) is misleading in this case.

Monometallism answered 13/2, 2019 at 21:13 Comment(0)
A
0

During a Rails/Ruby Update I found time to look into this and finally found the cause.

The user class had an unloadable in it for years. That caused the problems since Rails 4. Now I removed this and found no issues after that.

Adultery answered 14/5, 2019 at 7:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.