Scott Milella
Scott Milella

Reputation: 487

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

Upvotes: 4

Views: 7547

Answers (2)

Scott Milella
Scott Milella

Reputation: 487

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

Upvotes: 9

ScottM
ScottM

Reputation: 10422

The recommendation to move invitations_controller.rb into an app/controllers/users folder is the correct one. That's the path that matches your route:

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

as well as your controller namespace:

class User::InvitationsController < Devise::InvitationsController

The key word is move here – if you left a copy of this file behind in app/controllers/invitations_controller.rb, Zeitwerk might still have an issue trying to preload your code in production because of the namespace/file system mismatch.

If you move the file into its expected location and are still getting an error, then something else is going on and would be worth investigating further.

Upvotes: 1

Related Questions