I have the following setup:
class UsersController < ApplicationController
...
end
class Admin::BaseController < ApplicationController
...
end
class Admin::UsersController < Admin::BaseController
...
end
And likewise specs:
#spec/controllers/users_controller_spec.rb:
describe UsersController do
...
end
#spec/controllers/admin/users_controller_spec.rb
describe Admin::UsersController do
...
end
All the specs run fine when run independantly, however when I run all together I get the warning:
toplevel constant UsersController referenced by Admin::UsersController
And the specs from the admin controller don't pass.
Routes file:
...
resources :users
namespace "admin" do
resources :users
end
...
Rails 4, Rspec 2.14
Can I not use the same name for controllers in different namespaces?
This happens when a top level class get autoloaded before a namespaced one is used. If you have this code without any class preloaded :
UsersController
module AdminArea
UsersController
end
The first line will trigger constant missing hook : "ok, UsersController does not exist, so let's try to load it".
But then, reaching the second line, UsersController is indeed already defined, at top level. So, there's no const_missing hook triggered, and app will try to use the known constant.
To avoid that, explicitly require proper classes on top of your spec files :
#spec/controllers/users_controller_spec.rb:
require 'users_controller'
And
#spec/controllers/admin/users_controller_spec.rb
require 'admin/users_controller'
Related
Rails 3.2
In my controllers/admin/accounts_receivables_contoller.rb, I have:
class Admin::AccountsReceivables < Admin::ApplicationController
def index
...
end
and in one of the views, I have:
= link_to admin_accounts_receivables_path
In my config/routes.rb, I have:
namespace :admin do
resources :accounts_receivables do
collection do
get 'admin_report'
get 'customer_report'
post 'process_invoices'
end
end
end
rake routes, produces:
admin_accounts_receivables GET admin/accounts_receivables(.:format) admin/accounts_receivables#index
However, when I click on the link, I get (in the browser, but no entry in the log file):
uninitialized constant Admin::AccountsReceivablesController
I do not have a corresponding AccountsReceivable model, as I don't need it.
Any ideas?
The class should be named AccountsReceivablesController and you should nest the class explicitly instead of using the scope resolution operator so that it has the correct module nesting:
module Admin
class AccountsReceivablesController < ApplicationController
def index
# ...
end
end
end
When you use the scope resolution operator class Admin::AccountsReceivablesController - the module nesting is resolved to the point of definition which is Main (the global scope) and not Admin. For example:
module Admin
FOO = "this is what we expected"
end
FOO = "but this is what we will actually get"
class Admin::AccountsReceivablesController < Admin::ApplicationController
def index
render plain: FOO
end
end
See The Ruby Style Guide - namespaces.
class Admin::AccountsReceivables < Admin::ApplicationController
should be...
class Admin::AccountsReceivablesController < Admin::ApplicationController
I have little problem about my code I make controller/admin/moderators_controller.rb.
I got an error on compilator uninitialized constant Admin (NameError) in moderators_controller.rb.
In navigator I got this error:
superclass must be a Class (Module given)
navigator error
class Admin::ModeratorsController < ActionController
def index
end
end
This is my routes.rb file:
Rails.application.routes.draw do
namespace :admin do
resources :moderators, only: [:index]
end
end
You’ve accidentally made your controller inherit from ActionController (a module) instead of ActionController::Base (a class). You need to add ::Base to the end there.
If this is Rails 5, the common convention now is to have a ApplicationController class in your app/controllers folder, and have all controllers inherit from that (it’s just a class that inherits from ActionController::Base, but gives you a place to put common methods).
Rails 5
class Admin::ModeratorsController < ApplicationController
def index
end
end
Rails 4 or below
class Admin::ModeratorsController < ActionController::Base
def index
end
end
I'm having trouble starting to build my own admin section. I get this error when trying to view example.com/admin:
TypeError in Admin::AdminController#dashboard
"superclass mismatch for class AdminController"
My admin controller is in app/controllers/admin/admin_controller.rb
Here is my routes.rb:
Rails.application.routes.draw do
namespace :admin do
root :to => "admin#dashboard"
resources :posts
end
Here is my AdminController:
class AdminController < ApplicationController
def dashboard
print "Dashboard"
end
end
My plan is to have example.com/admin go to the admin dashboard. To edit/create posts: /admin/posts.
You already have a Admin::AdminController class defined elsewhere. Which inherits from different class other then ApplicationController
If you have not created a second Admin::AdminController class yourself, it is likely one of your Gems or plugins already defines it.
If you are using active_admin or rails_admin gem , maybe it does have class with the above name AdminController
You can cross check by replacing the AdminController with some other name maybe AdminController2
Given the following controller structure:
# application_controller.rb
class ApplicationController < ActiveController::Base; end
# pages_controller.rb
class PagesController < ApplicationController; end
# admin/application_controller.rb
module Admin
class ApplicationController < ::ApplicationController; end
end
# admin/pages_controller.rb
module Admin
class PagesController < ApplicationController; end
end
One would expect Admin::PagesController to inherit from Admin::ApplicationController and it does. But I have noticed that sometimes it inherits from ::ApplicationController.
So I decided not to risk it and changed declaration of all controllers in /admin to specifically target Admin::ApplicationController
# admin/pages_controller.rb
module Admin
class PagesController < Admin::ApplicationController; end
end
Okay that works, but from what I know it was correct in the first place. Why Rails inherits from a wrong controller sometimes?
Admin::PagesController sometimes inherits from ApplicationController instead of Admin::ApplicationController despite both being in the same module Admin
The problem here is rails' development mode code loading: in general code is loaded when you try to do something with a constant (eg subclass from it) and that constant doesn't exist. This results in const_missing being called and rails uses it this to try to load the class (for a detailed description see the guide).
If neither ApplicationController nor Admin::ApplicationController exist then when you access your admin pages controller ruby will hit that const_missing and try to load admin/application_controller.rb
However if ApplicationController is already loaded then ruby won't fire const_missing since it perfectly legal for a class in the admin module to inherit from something at the toplevel.
The solution as you say is to make explicit what you are inheriting from. Personally in my own apps I use Admin::BaseController as the base class.
Another option is to use require_dependency to point Rails to the correct file:
# admin/application_controller.rb
require_dependency 'admin/application_controller'
module Admin
class PagesController < ApplicationController
end
end
I have a controller like follows:
namespace :admin do
resources :posts
end
# app/controllers/admin_posts_controller.rb
module Admin
class PostsController < ApplicationController
end
end
The problem is I don't know where Admin::PostsControllerTest goes.
I was expecting something like following works:
# test/functional/admin/posts_controller_test.rb
module Admin
class PostsControllerTest < ActionController::TestCase
end
end
But if I do that I get the following error when I run rake test:functionals :
RuntimeError: #controller is nil: make sure you set it in your test's setup method.
You've got the right location. Try this:
class Admin::PostsControllerTest < ActionController::TestCase
#put your tests here
end