Reputation: 303
I'm pretty new to ruby, coming from a php background, but something is not clicking with me.
So, let's say I have a Ruby on Rails application and I am versioning my API like so:
app
|_controllers
|_api
| |_v1
| | |_application_controller.rb
| | |_user_controller.rb
| |_application_controller.rb
|_application_controller.rb
With the class structure with
# Versioned API V1 app controller
module Api
module V1
class ApplicationController
end
end
end
# Versioned API app controller
module Api
class ApplicationController
end
end
# Root app controller
class ApplicationController
end
#The code in question
module Api
module V1
class UserController < ApplicationController
end
end
end
So the question is, does ruby look for Api::V1::ApplicationController
, Api::ApplicationController
, or ApllicationController
for extending?
Does the < ApplicationController
look for it's own namespace unless I specify Api::ApplicationController
? If so, how do I specify the root one?
Upvotes: 14
Views: 8338
Reputation: 1892
I would rather suggest don't write ApplicationController
in namespace, I would suggest to follow following
Note: If you are building professional api's it always good to have Api::V1::BaseController
inheriting from ActionController::Base
, though I am giving solution to your specific case
Ref this post: Implementing Rails APIs like a professional
1) Define application controller in usual way in app/controllers/application_controller.rb as
class ApplicationController < ActionController::Base
end
2) Define base api controller namely Api::V1::BaseController in app/controllers/api/v1/base_controller.rb which will inherit from ApplicationController
(your case) like
class Api::V1::BaseController < ApplicationController
end
3) Define your api controllers like Api::V1::UsersController
in app/controllers/api/v1/users_controller.rb which will inherit from Api::V1::BaseController
class Api::V1::UsersController < Api::V1::BaseController
end
4) Add all subsequent controllers like Api::V1::UsersController
(step 3)
Then routing will contain namespaces routing in config/routes.rb
namespace :api do
namespace :v1 do
resources :users do
#user routes goes here
end
# any new resource routing goes here
# resources :new_resource do
# end
end
end
Upvotes: 5
Reputation: 115511
When you use
#The code in question
module Api
module V1
class UserController < ApplicationController
end
end
end
ApplicationController
definition will be searched in Api::V1
then if not found in Api
then if not found in the root namespace.
I agree it could be confusing, that's why I tend to use absolute paths like so: ::ApplicationController
If ever I'd need Api::ApplicationController
, I'd write ::Api::ApplicationController
Basically the ::
tells ruby to start from the root namespace and not from where the code lives.
Sidenote
Be aware that there are vicious cases in Rails development mode. In order to gain speed, the strict minimum is loaded. Then Rails looks for classes definitions when needed.
But this sometimes fails big time example, when you have say ::User
already loaded, and then look for ::Admin::User
. Rails would not look for it, it will think ::User
does the trick.
This can be solved using require_dependency
statements in your code. Speed has a cost :)
Upvotes: 26
Reputation: 4633
You should check the guide to understand the routing: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing
I think this question is very similar to:
And as shortage:
Rails will detect the namespace automagically by the folder. So you don't need to append it to the name:
This blog post explains it so well:
http://blog.makandra.com/2014/12/organizing-large-rails-projects-with-namespaces/
Let's say we have an Invoice class and each invoice can have multiple invoice items:
class Invoice < ActiveRecord::Base has_many :items end class Item < ActiveRecord::Base belongs_to :invoice end
Clearly Invoice is a composition of Items and an Item cannot live without a containing Invoice. Other classes will probably interact with Invoice and not with Item. So let's get Item out of the way by nesting it into the Invoice namespace. This involves renaming the class to Invoice::Item and moving the source file to app/models/invoice/item.rb:
class Invoice::Item < ActiveRecord::Base belongs_to :invoice end
Same is applied to controllers and views.
Upvotes: 0