I refactored my rails app in a way that for every sub resources i create a controller is the according namespace.
api/v1/app/controller/manager.rb
api/v1/app/controller/manager/user.rb
api/v1/app/controller/manager/controller.rb
api/v1/app/controller/admin.rb
api/v1/app/controller/user.rb
api/v1/app/controller/controller.rb
The class definition of the user resource under the manager namespace looks like this
class Api::V1::Manager::UserController < ApplicationController
This controller is reachable through routes.rb:
resources :manager , only: [:show ] do
resources :user, only: [:index], controller: 'manager/user'
end
which generates
/api/v1/manager/:manager_id/user(.:format) api/v1/manager/manager#index {:format=>"json"}
The models are all under
app/models/manager.rb
app/models/user.rb
When i want to access now the Manager
model inside the api/v1/app/controller/manager/user.rb
controller or in api/v1/app/controller/manager.rb
e.g
class Api::V1::ManagerController < ApplicationController
def index
Manager.find(...)
end
end
class Api::V1::Manager::UserController < ApplicationController
def index
Manager.find(...)
end
end
i get these errors
{"error":"uninitialized constant Api::V1::Manager::UserController::Manager"}%
{"error":"uninitialized constant Api::V1::Manager::Manager"}%
The calls are handled by the correct controllers :
Processing by Api::V1::Manager::UserController#index as JSON
The solution is to use the double colon prefix with the call
`::Manager.find(...)`.
I can use all other models Admin.find(...)
or Controller.first
normally. Only the Manager.find(..)
is not working.
Renaming the namespace to ManagerResource
still produces the same error message.
I would like to be able to group controllers under different namespaces and still access all the models the same way how is that possible?
Update
Created
api/v1/app/controller/api/v1/foo/customer_controller.rb
api/v1/app/controller/api/v1/manager_customer_controller.rb
After starting the server (webrick) all endpoints are working.
Adding Manager.first
- to any controller- or changing something in a file which uses Manager...
returns these errors
`uninitialized constant Api::V1::Foo::UserController::Manager`
`uninitialized constant Api::V1::ManagerUserController::Manager`
`uninitialized constant Api::V1::*any_controller*::Manager`
Restarting the server solves this issue.
I am able to use Controller.first
or any other model in e.g. api/v1/app/controller/controller.rb
.The the server responds well.
Like @Andrey Deineko pointed out i understand now the module and class names should differ.
What i dont understand is why these errors occur only for a specific model when i substract controllers under a namespace which with a different name than the models?
Update II
I removed all manager related namespaces and controllers. So i am back to the original pre-controller-optimization state.
This error occurs only for the Manger
model. In the console Manager.class
shows in any case Class
.
But in the controller this happens:
module Api
module V1
class Manager < Api::ApiBaseController
def index
puts User.class #=> class
puts Manager.class #=> module
puts ::Manager.class #=> class
puts Controller.class #=> class
...
end
end
end
end
class Api::V1::Manager < Api::ApiBaseController
def index
puts User.class #=> class
puts Manager.class #=> {"error":"uninitialized constant Api::V1::ManagerController::Manager"}
puts ::Manager.class
puts Controller.class
...
end
end
when i change the order so that ::Manager
is first everything works as expected and also the classes then match
class Api::V1::Manager < Api::ApiBaseController
def index
puts User.class #=> class
::puts Manager.class #=> class
puts Manager.class #=> class
puts Controller.class #=> class
...
end
end
The namespace Api::V1::...
works for every other controller.
The reason is simple and the answer lies in Ruby's constants resolution mechanism.
Basically it is super bad idea to have a module and class with the same name.
But if you definitely need to have same name for both module and class, be sure to correctly reference each of them.
Meaning, that referencing Manager
class with ::
is your only solution if you don't want to change the naming.
Rails add some magic to auto/preloading classes in development and production mode, so you could face different issues in different modes.
You may want to read through this official guide on loading constants in Rails.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With