GingerTech
GingerTech

Reputation: 303

Ruby namespacing

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

Answers (3)

Pramod Shinde
Pramod Shinde

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

apneadiving
apneadiving

Reputation: 115511

When you use

#The code in question
module Api
  module V1
    class UserController < ApplicationController
    end
  end
end

ApplicationControllerdefinition 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

Jorge de los Santos
Jorge de los Santos

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:

Rails Controller Namespace

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

Related Questions