ruby-on-railsrubyrubygemsdevise-invitable

on_file_autoloaded': expected file invitations_controller.rb to define constant InvitationsController, but didn't (Zeitwerk::NameError)


I am back again with my ctrlpanel application.

I have it 100% working in development and went through the process to get it loaded up to Heroku and got the app up, gems installed. DB is there (mostly) but I have an issue even before the DB. I am getting an error dealing with devise_invitable that I DO NOT get in Development. To my surprise I do get the same error when I launch production on my laptop which was shocking to me to say the least as everything works perfect in development. So I know it isn't a Heroku issue which I am happy about at least I can reproduce it. The full error is below here but the line that specifically deals with the error is:

"C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader/callbacks.rb:18:in `on_file_autoloaded': expected file D:/rails/ctrlpanel/app/controllers/invitations_controller.rb to define constant InvitationsController, but didn't (Zeitwerk::NameError)C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader/callbacks.rb:18:in `on_file_autoloaded': expected file D:/rails/ctrlpanel/app/controllers/invitations_controller.rb to define constant InvitationsController, but didn't (Zeitwerk::NameError)"

I did some searching online (a lot of it actually) and the only thing I could find was a recommendation to copy the invitations_controller.rb file into a folder called users under the controllers folder, which I did try, I made a copy of it actually and I did also try moving it. Neither of which helped. This works properly in development. SO I started comparing the 2 environment files.

I got past it by changing these 2 entries:

config.cache_classes = false in production.rb
config.eager_load = false in production.rb

However I have seen postings everyplace that turning those 2 options off is very bad and it will effect the views somehow and sure enough my bootstrap is all caddywhompused which I can only assume is due to those 2 options being off. I'm sure someone else must have seen this before but I can't seem to find anything. I checked the devise_invitable docs, updated it to a v .02 higher (no effect).
The error is so long I am having a hard time find a good search term to get results.

I am confused as everything must be correct or it wouldn't be working right in development or I wouldn't think so? If there is any other file that needs to be seen please let me know and I will be happy to display it.

Thank you in advance for any help or advice you can offer.

Scott

Here is the error:

scottm@RED-IT-LAP-0001 MINGW64 /d/rails/ctrlpanel (master)
$ rails server -e production
=> Booting Puma
=> Rails 6.1.3.2 application starting in production
=> Run `bin/rails server --help` for more startup options
Exiting
Traceback (most recent call last):
        67: from bin/rails:14:in `<main>'
        66: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
        65: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
        64: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
        63: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
        62: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
        61: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/commands.rb:18:in `<main>'
        60: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/command.rb:50:in `invoke'
        59: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/command/base.rb:69:in `perform'
        58: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/thor-1.1.0/lib/thor.rb:392:in `dispatch'
        57: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/thor-1.1.0/lib/thor/invocation.rb:127:in `invoke_command'
        56: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/thor-1.1.0/lib/thor/command.rb:27:in `run'
        55: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/commands/server/server_command.rb:135:in `perform'
        54: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/commands/server/server_command.rb:135:in `tap'
        53: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/commands/server/server_command.rb:144:in `block in perform'
        52: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/commands/server/server_command.rb:39:in `start'
        51: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/server.rb:311:in `start'
        50: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/server.rb:379:in `handle_profiling'
        49: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/server.rb:312:in `block in start'
        48: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/server.rb:422:in `wrapped_app'
        47: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/server.rb:249:in `app'
        46: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/server.rb:349:in `build_app_and_options_from_config'
        45: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/builder.rb:66:in `parse_file'
        44: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/builder.rb:105:in `load_file'
        43: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/builder.rb:116:in `new_from_string'
        42: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/builder.rb:116:in `eval'
        41: from config.ru:3:in `block in <main>'
        40: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/activesupport-6.1.3.2/lib/active_support/dependencies.rb:332:in `require'
        39: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/activesupport-6.1.3.2/lib/active_support/dependencies.rb:299:in `load_dependency'
        38: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/activesupport-6.1.3.2/lib/active_support/dependencies.rb:332:in `block in require'
        37: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:34:in `require'
        36: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
        35: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
        34: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
        33: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
        32: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/bootsnap-1.7.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
        31: from D:/rails/ctrlpanel/config/environment.rb:5:in `<main>'
        30: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/railtie.rb:207:in `method_missing'
        29: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/railtie.rb:207:in `public_send'
        28: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/application.rb:384:in `initialize!'
        27: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/initializable.rb:60:in `run_initializers'
        26: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:205:in `tsort_each'
        25: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:226:in `tsort_each'
        24: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:347:in `each_strongly_connected_component'
        23: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:347:in `call'
        22: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:347:in `each'
        21: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:349:in `block in each_strongly_connected_component'
        20: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:431:in `each_strongly_connected_component_from'
        19: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
        18: from C:/Ruby27-x64/lib/ruby/2.7.0/tsort.rb:228:in `block in tsort_each'
        17: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/initializable.rb:61:in `block in run_initializers'
        16: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/initializable.rb:32:in `run'
        15: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/initializable.rb:32:in `instance_exec'
        14: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/railties-6.1.3.2/lib/rails/application/finisher.rb:133:in `block in <module:Finisher>'
        13: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:508:in `eager_load_all'
        12: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:508:in `each'
        11: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:393:in `eager_load'
        10: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:393:in `synchronize'
         9: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:404:in `block in eager_load'
         8: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:725:in `ls'
         7: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:725:in `foreach'
         6: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:733:in `block in ls'
         5: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:409:in `block (2 levels) in eager_load'
         4: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:409:in `const_get'
         3: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:26:in `require'
         2: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:26:in `tap'
         1: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:27:in `block in require'
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/zeitwerk-2.4.2/lib/zeitwerk/loader/callbacks.rb:18:in `on_file_autoloaded': expected file D:/rails/ctrlpanel/app/controllers/invitations_controller.rb to define constant InvitationsController, but didn't (Zeitwerk::NameError)

Here is the routes.rb file:

Catalog::Application.routes.draw do  

 resources :redline_filters
  resources :certifications
  resources :routes do
    resources :waypoints, only: [:show, :destroy]
  end

  resources :dealerships do
    collection do
      get 'index_public'
      get 'deduplicate'
    end
    resources :visits
  end

  resources :items do
    resources :variants, except: :index do
      resources :snapshots
    end
  end

  resources :crosses do
    collection do
      get 'index_public'
    end
  end
  

  root to: 'static_pages#home'

  devise_for :users, controllers: { invitations: 'users/invitations' }

  
  resources :locations, :categories, :gfe_users, :trips, :vendors, :work_orders
  resources :attachments, only: [:destroy]

  get '/gfe_reporting', to: 'gfe_users#reporting'

  resources :users do
    get 'toggle_suspend'
  end

  resources :charges do
    collection do
      post 'import'
    end
  end


  resources :products do
    match 'submit' => 'products#submit', via: [:get, :post]
    match 'publish' => 'products#publish', via: [:get, :post]
    match 'in_progress' => 'products#in_progress', via: [:get, :post]
    # collection { post :search, to: 'products#index' } ## ransack crap.
  end

  resources :devices do
    resources :trips
  end

  # match "/home" => 'static_pages#home'
  match "/catalog" => 'static_pages#catalog', via: [:get, :post]
  match "product/:name" => "products#index", via: [:get, :post]
  get "static_pages/products"
  get "static_pages/catalog"
  get "static_pages/help"
  get "static_pages/roi"
  get "/roi", to: "static_pages#roi"


  match 'device_lookup', to: 'devices#lookup', via: [:get, :post]
  match 'device_first_avaiable_serial', to: 'devices#first_available_serial', via: [:get, :post]
  match 'device_battery_is_dead' => 'devices#update_status_to_dead_battery', via: [:get, :post]
  match 'device_is_missing' => 'devices#is_missing', via: [:get, :post]
  match 'device_has_water_damage' => 'devices#update_status_to_water_damage', via: [:get, :post]
  match 'device_is_rma' => 'devices#update_status_to_rma', via: [:get, :post]
  match 'device_is_healthy' => 'devices#is_healthy', via: [:get, :post]
  match 'pending_trip' => 'trips#pending_trip', via: [:get, :post]
  match 'complete_trip' => 'trips#complete_trip', via: [:get, :post]

  # The priority is based upon order of creation:
  # first created -> highest priority.

  # Sample of regular route:
  #   match 'products/:id' => 'catalog#view'
  # Keep in mind you can assign values other than :controller and :action

  # Sample of named route:
  #   match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
  # This route can be invoked with purchase_url(:id => product.id)

  # Sample resource route (maps HTTP verbs to controller actions automatically):
  #   resources :products

  # Sample resource route with options:
  #   resources :products do
  #     member do
  #       get 'short'
  #       post 'toggle'
  #     end
  #
  #     collection do
  #       get 'sold'
  #     end
  #   end

  # Sample resource route with sub-resources:
  #   resources :products do
  #     resources :comments, :sales
  #     resource :seller
  #   end

  # Sample resource route with more complex sub-resources
  #   resources :products do
  #     resources :comments
  #     resources :sales do
  #       get 'recent', :on => :collection
  #     end
  #   end

  # Sample resource route within a namespace:
  #   namespace :admin do
  #     # Directs /admin/products/* to Admin::ProductsController
  #     # (app/controllers/admin/products_controller.rb)
  #     resources :products
  #   end

  # You can have the root of your site routed with "root"
  # just remember to delete public/index.html.
  # root :to => 'welcome#index'

  # See how all your routes lay out with "rake routes"

  # This is a legacy wild controller route that's not recommended for RESTful applications.
  # Note: This route will make all actions in every controller accessible via GET requests.
  # match ':controller(/:action(/:id))(.:format)'
end

Here is the invitations_controller.rb file:

class User::InvitationsController < Devise::InvitationsController
  def new
    if cannot?(:invite, User)
      raise CanCan::AccessDenied
    else
      super
    end
  end

  def create
    if cannot?(:invite, User)
      raise CanCan::AccessDenied
    else
      self.resource = resource_class.invite!(resource_params, current_inviter)

      unless resource.role.present?
        resource.role = "creator"
        resource.save
      end

      if resource.errors.empty?
        set_flash_message :notice, :send_instructions, :email => self.resource.email
        respond_with resource, :location => after_invite_path_for(resource)
      else
        respond_with_navigational(resource) { render :new }
      end
    end
  end

  private
  def resource_params
    params.permit(user: [:name, :email, :invitation_token, :location_id])[:user]
  end
end

Here is the application_controller.rb file:

class ApplicationController < ActionController::Base
   
  protect_from_forgery prepend: true

  #before_filter :configure_permitted_parameters, if: :devise_controller?
  before_action :configure_permitted_parameters, if: :devise_controller?

  #before_filter :authenticate_user!
  before_action :authenticate_user!

  #check_authorization :unless => :devise_controller?
  after_action :unless => :devise_controller?
 
  before_action :set_paper_trail_whodunnit

  rescue_from CanCan::AccessDenied do |exception|
    redirect_to root_url, :alert => exception.message
  end

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [ :first_name, :last_name, :email] )
    devise_parameter_sanitizer.permit(:account_update, keys: [ :first_name, :last_name, :phone, :email ] )
    devise_parameter_sanitizer.permit(:invite, keys: [ :name, :location_id ] )
  end
end

Just in case here is also the application.rb file

require_relative "boot"

require "rails/all"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Catalog #Ctrlpanel
  class Application < Rails::Application

    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.1

    config.before_configuration do
      env_file = File.join(Rails.root, 'config', 'local_env.yml')
      YAML.load(File.open(env_file)).each do |key, value|
        ENV[key.to_s] = value
      end if File.exists?(env_file)
    end

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

  config.encoding = "utf-8"
  config.time_zone = 'Pacific Time (US & Canada)'
  config.active_record.default_timezone = :local

  config.filter_parameters += [:password]

  config.active_support.escape_html_entities_in_json = true

  # Enable pdf.css precompiling for wicked_pdf
  config.assets.precompile += %w( pdf.css print.css awesome-bootstrap-checkbox.css jquery.dataTables.min.css )

  # Force Heroku to not access the DB or load models when precompiling your assets.
  config.assets.initialize_on_precompile = false

  # Use SQL instead of Active Record's schema dumper when creating the database.
  # This is necessary if your schema can't be completely dumped by the schema dumper,
  # like if you have constraints or database-specific column types
  # config.active_record.schema_format = :sql

  # Enforce whitelist mode for mass assignment.
  # This will create an empty whitelist of attributes available for mass-assignment for all models
  # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
  # parameters by using an attr_accessible or attr_protected declaration.
  
  # This breaks the application 3/30/2021 Scott Milella
  config.active_record.whitelist_attributes = true

  # 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'

    # Configuration for the application, engines, and railties goes here.
    #
    # These settings can be overridden in specific environments using the files
    # in config/environments, which are processed later.
    #
    # config.time_zone = "Central Time (US & Canada)"
    # config.eager_load_paths << Rails.root.join("extras")

  # No Method Error message 3/31/2021 Scott Milella
  #config.active_record.raise_in_transactional_callbacks = true

  end
end

Solution

  • I figured it out this morning.

    I learned about a command called: rails zeitwerk:check --trace

    When I ran it, it gave me this specific error: Hold on, I am eager loading the application. expected file app/controllers/users/invitations_controller.rb to define constant Users::InvitationsController

    When I looked at my user model the main class declaration was written as: class User::InvitationsController < Devise::InvitationsController

    The name of the model is user.rb so based on my experience with other languages I assumed the class name and the model name had to match, but I tried to rename it to: class Users::InvitationsController < Devise::InvitationsController
    I added the s in the Users::InvitationsController and then I ran the zeitwerk:check --trace again and it passed, I then tried to load the application into Production and what do you know it worked?

    SO the SOLUTION in my case was in 2 parts: 1 Thank you @Scott Matthewman who informed me I needed to in fact MOVE the invitations_contoller.rb into a folder under controllers called users /app/controllers/users.
    2 I had to rename the User to Users in the class declaration (if that is the term in Ruby/Rails). FROM: User::InvitationsController < Devise::InvitationsController TO: Users::InvitationsController < Devise::InvitationsController