This site provides the following access keys:

Brandan Lennox's

Namespaces in Rails Applications

In general, Rails doesn’t encourage developers to factor code into namespaces. Most apps end up with hundreds of files and very few directories in app/models and similarly flat hierarchies in other app directories. Rails Engines are a great way to pull your code into isolated, reusable modules, but there’s a significant amount of overhead associated with an Engine that might be too expensive for a small feature, or you might just have a few conceptually similar models that you want to “live near each other”.

Rails has support for namespacing in its MVC architecture, but there are a few things that might cause confusion if you haven’t used it before.

Pretend your app has a user-configurable dashboard. Users can add widgets — like charts and tables — to the dashboard, and they can lay the widgets out on the dashboard in rows and columns. You might model it like this:

  • Dashboard::Dashboard, which contains Rows
  • Dashboard::Row, which contains Widgets
  • Dashboard::Widget, which has a position within its Row

I’ll use these models in the examples below.

Models

Most of the gnarly bits of namespacing happen in the models, so I’ll start there.

First, you’ll need to create a module for your namespace and define the class method table_name_prefix (see Migrations):

# app/models/dashboard.rb
module Dashboard
  def self.table_name_prefix
    'dashboard_'
  end
end

Once you’re “inside” the namespace, you generally won’t refer to it when declaring associations. For example, Widget will belong to Row and Row will belong to Dashboard:

# app/models/dashboard/widget.rb
module Dashboard
  class Widget < ApplicationRecord
    belongs_to :row
  end
end

# app/models/dashboard/row.rb
module Dashboard
  class Row < ApplicationRecord
    belongs_to :dashboard
    has_many :widgets
  end
end

# app/models/dashboard/dashboard.rb
module Dashboard
  class Dashboard < ApplicationRecord
    has_many :rows
  end
end

Outside the namespace, though, you’ll have to tell Rails the class name of any association that uses your namespaced model. Otherwise, it will guess incorrectly:

# app/models/user.rb
class User < ApplicationRecord

  # WRONG => NameError: uninitialized constant User::DashboardDashboard
  has_many :dashboard_dashboards

  # RIGHT
  has_many :dashboard_dashboards, class_name: 'Dashboard::Dashboard'

  # ALSO RIGHT
  has_many :dashboards, class_name: 'Dashboard::Dashboard'
end

Migrations

Since most databases don’t support namespaces for tables, Rails expects you to prefix your table names with the “underscored” name of your namespace. You also won’t be able to pass foreign_key: true to the references helper method, so you’ll have to create constraints yourself.

To create the migration for Dashboard::Widget:

$ rails generate migration create_dashboard_widgets

Edit the generated file:

# db/migrate/YYYYMMDDHHMMSS_create_dashboard_widgets.rb
class CreateDashboardWidgets < ActiveRecord::Migration
  create_table :dashboard_widgets do |t|

    # adding "foreign_key: true" here will cause an error like this:
    #   PG::UndefinedTable: ERROR:  relation "rows" does not exist
    t.references :row, index: true
  end

  add_foreign_key :dashboard_widgets, :dashboard_rows, column: :row_id
end

Routes

Simply wrap your routes in a call to namespace:

# config/routes.rb
namespace :dashboard do
  resources :dashboards
  # ...
end

Your URLs will look like /dashboard/dashboards/1, and path helpers will look like edit_dashboard_dashboard_path(dashboard).

Controllers

Controllers are also straightforward:

# app/controllers/dashboard/dashboards_controller.rb
module Dashboard
  class DashboardsController < ApplicationController
    def index
      @dashboards = current_user.dashboards
    end

    # ...
  end
end

Views

You may get a confusing error if you try to render a partial of a model from outside the namespace.

Say one of your widgets renders a list of recently active users:

# app/views/dashboard/widgets/_active_user_widget.html.erb

<%# This can result in an error:
      Missing partial dashboard/users/_user %>
<%= render @widget.active_users %>

Rails is attempting to render a different partial than you might expect since you’re inside a namespace. The idea is that you might want a different _user partial in, say, an admin area than you do in the public area.

Since I’m generally using namespaces to isolate features rather than create distinct layouts, I turn this behavior off:

# config/application.rb
config.action_view.prefix_partial_path_with_controller_namespace = false

Unfortunately, it’s a global setting, so you can’t change it for each call to render. You can always explicitly render a template if you need the default behavior:

<%= render partial: 'admin/users/user', collection: @users %>

Factories

If you’re using FactoryGirl for testing, you’ll need to tell her the name of the factory to use for your namespaced associations:

# spec/factories/dashboard/widget.rb
FactoryGirl.define do
  factory :dashboard_widget, class: Dashboard::Widget do
    association :row, factory: :dashboard_row
  end
end

End

After many years of cramming everything directly into app/models, I’ve had great luck with namespacing. It keeps related concepts together and provides a nice level of isolation without the overhead of full-fledged Engines or microservices.

I hope your experience is as pleasant as mine!