I have a controller Admin::AddressController that is a subclass of AdminController...and since "Address" comes before "Admin" I get a "uninitialized constant" error for the Admin Controller...
So to fix this, I've done a require ./app/controllers/admin/AdminController.rb before my AddressController class declaration.
Is the the right way to do things? Or is there a way I can tell rails to initialize the AdminController first?
Rails should be able to handle that automatically if you're following the conventions. If you're placing AdminController inside the admin folder, the class should be named Admin::AdminController. It's either that or:
module Admin
class AdminController
...
On AddressController you can use:
class Admin::AddressController < Admin::AdminController
...
or
module Admin
class AddressControler < AdminController
...
Related
I read here that a ruby class can only inherit from one class, and can include modules.
However, the devise module defines controllers like this:
class Users::PasswordsController < Devise::PasswordsController
...
end
Now, given that Users is probably a class, with PasswordsController being a method:
>> Devise::PasswordsController.class
=> Class
How is it that a method in a class inherits from another class?
class Users::PasswordsController < Devise::PasswordsController
...
end
In the above code, Users is the module and PasswordsController is the class inside Users module. Similarly Devise is the module and PasswordsController is the class inside Devise module.
so when you run
Users::PasswordsController.class
#=> Class
Users.class
#=>Module
What confuses you here is that you have wrong assumptions, namely:
Users is probably a class
Not necessarily. Here we have namespace with nesting, therefore Users can be either a class or a module. In fact classes are modules.
PasswordsController being a method
PasswordsController here is a class nested in the Users namespace. :: simply lets you go one level into the nesting tree.
Consider:
module Foo
class Bar
end
end
Foo::Bar.class # => class
From Rails naming convention, Users is most probably a module, and Users::PasswordsController is a class.
Note that :: is not for calling class methods (although it can be used this way). It's for accessing constants inside a module/class. For example
module Foo
BAR = 'bar'
end
Foo::BAR
#=> "bar"
In Ruby, a module/class name is a constant, and the value stored in it is the module/class. So :: also is used for accessing a module/class inside another module/class. For example
module Foo
class Bar
end
end
Foo::Bar
#=> Foo::Bar
Both Users and Device are modules, just used for scoping the real classes that are PasswordsController and PasswordsController.
So I wrote a little portfolio app in Rails 4. It has an admin namespace to allow me to only expose certain controllers to public.
Everything works great when in development, but when I deploy to a production env (or switch to production on my dev machine) I get the following error:
superclass mismatch for class WorksController (TypeError)
My controller structure looks like this:
controllers ->
admin ->
admin_controller.rb
portfolio ->
works_controller.rb
portfolio->
works_controller.rb
As you can see, I namespaced works inside of portfolio.
The controllers are declared as following:
/controllers/admin/admin_controller
class Admin::AdminController < ApplicationController
/controllers/admin/portfolio/works_controller
class Admin::Portfolio::WorksController < Admin::AdminController
/controllers/portfolio/works_controller.rb
class Portfolio::WorksController < ApplicationController
Now for my routes.rb, note I am using Friendly ID so I used a custom route to have a pretty client facing url.
get '/portfolio/:id' => 'portfolio/works#show', as: 'portfolio_work'
namespace :admin do
namespace :portfolio do
resources :works
end
end
So I know I must have screwed this up somehow, but I don't know how. The error I get is related to redeclaring a class, but I don't know how I managed to do so.
Any help is greatly appreciated.
Found this fix after #ReggieB tried to help me. Turns out having my Devise model 'Admin' and a namespace also called 'Admin' creates problems (as it should).
What happens if you redefine /controllers/admin/portfolio/works_controller line this:
module Admin
class Portfolio::WorksController < AdminController
I have a model called ToolFilter with a column of 'tool_type'. The string here refers to a class for a tool. I put a method in my application_controller called tools_list that gets the descendants of Tool.This works nicely in my frontend, but ToolFilter is complaining about the method tools_list.
class ToolFilter < ActiveRecord::Base
validate :existence_of_tool
def existence_of_tool
unless tools_list.include? tool_type
errors.add(:tool_type, "Invalid tool_type {{tool_type}}, use 'tools_list' to see a list of valid tool_object_types")
end
end
class ApplicationController < ActionController::Base
helper_method :tools_list
def tools_list
Rails.application.eager_load!
Tool.descendants
end
It's a bit strange to tell a model about other classes in the file system, but I need to validate that it is one of these. Should I put tools_list is a module and include it in ToolFilter? Any suggestions?
Write this to include helper in your model
ApplicationController.helpers.tool_list
Though I will not recommend calling helper in model.
And checking tools with classes is damm bad idea.
I ended up creating a module called ToolExtention which has these helper methods in them. I then included this module in my controllers wherever it was needed and moved my logic from the views into the controller which I believe is better practice.
module ToolExtension
def self.tools_list
Rails.application.eager_load!
Tool.descendants
end
...
class ProjectsController < ApplicationController
include ToolExtension
...
ToolExtension.tools_list
Can I have controllers in Rails that are 3 levels deep inheritance? One would think such a trivial thing is possible, but the concrete controller at the "third" level gives the generic/useless error of "uninitialized constant Ns2::SecondController"
This is basically with this code (I haven't tried this exact code)
module Ns3
class ThirdController < Ns2::SecondController
end
end
module Ns2
class SecondController< Ns1::FirstController
end
end
module Ns1
class FirstController< ApplicationController
end
end
NOTE: The use of namespaces, within the routes and all such directories should be set up properly.
I'm sure I could rearrange the logic and get something working with mixins or helpers. However, I'd like the immediate question answered for my own benefit. Either Y/N or a way passed the error. Not interested in a refactoring work-around solution ATM. Though I guess it couldn't hurt.
Thanks
This can be done.
However it appears RoR is weird, and that you have to implicitly specify the namespace for base classes. If you let it default to the current namespace it acts weird.
Its most likely a typo in either the class name or filename.
You need to put the classes in the correct file/directory structure for Rails autoloading to work, e.g:
#/controllers/ns3/third_controller.rb
module Ns3
class ThirdController < Ns2::SecondController
end
end
#/controllers/ns2/second_controller.rb
module Ns2
class SecondController < Ns1::FirstController
end
end
#/controllers/ns1/first_controller.rb
module Ns1
class FirstController < ApplicationController
end
end
Another thing to try is scoping from the root namespace so with a :: prefix, like so:
module Ns1
class SecondController < ::Ns1::FirstController
end
end
You could also try this:
#/controllers/ns3/third_controller.rb
class Ns3::ThirdController < ::Ns2::SecondController
end
I've decided to break up by Rails application into different modules for clarity, and as a part of this I'd like each module to have its own ApplicationController. So I went through and defined an application controller for each module like so:
module Login
class ApplicationController < ::ApplicationController
...
end
end
Then I'd create a different controller in the login module:
module Login
class HomeController < ApplicationController
end
end
My expectation was that this would first search the Login module for the application controller, but from what I'm seeing it's actually inheriting the global application controller (any declarations I make in Login::ApplicationController aren't picked up). I've searched around for some information on this, but haven't been able to figure out why it is using the ApplicationController in the top level name space and not the one in Login. It works just fine having HomeController inherit from Login::ApplicationController, but I'd like to learn why it doesn't work without the Login:: prefix for the future.
Try
module Login
class HomeController < Login::ApplicationController
end
end