Rails Routing

Rails Routing

·

10 min read

Introduction

Routing is the process of mapping incoming requests to the appropriate controller actions in a web application. In Ruby on Rails, routing plays a crucial role in defining how requests are handled and how resources are exposed through the application's API. By defining routes, developers can create a clear and intuitive interface for accessing and modifying data. In order to build robust and scalable web applications, it's essential to have a solid understanding of routing in Ruby on Rails.

Basics of Routing in Rails

Before diving deeper into the routing in rails, this section includes some prerequisites that you need to know.

What is a resource?

In Ruby on Rails, a resource is an object or concept -it could be a databse table for example- that can be accessed and manipulated through a web application's API. A resource keyword in Rails is used to define resourceful routes and controllers that follow a RESTful approach to handling CRUD operations on resources. By using resourceful APIs, developers can create a scalable and maintainable application that enforces a consistent data model and provides a clear separation of concerns.

CRUD Operations

CRUD operations are a set of basic operations that are commonly used to manipulate data in a database or other storage system. CRUD stands for Create, Read, Update, and Delete, and these operations correspond to the basic actions that can be performed on data. In the context of web development, CRUD operations are often used to manipulate resources exposed through an API. By providing a standardized set of operations for accessing and modifying data, CRUD operations help developers create clear and intuitive interfaces for working with data.

How routing works in Rails

In Ruby on Rails, routes and controllers work together to map incoming requests to the appropriate controller actions in a web application. The routes.rb file is used to define the application's routes, while controllers are responsible for handling the logic and generating the response for each route. Here is an example of a route defined in a routes.rb file:

Rails.application.routes.draw do
  get '/users', to: 'users#index'
end

In this example, a GET request to the /users endpoint will be mapped to the index action in the UsersController (more on this later in Custom Routes).

Resources keyword

The resources keyword in Rails is used to define resourceful routes and controllers that provide a RESTful approach to handling CRUD operations on resources. When using the resources keyword, Rails generates a set of standard routes that map to the standard CRUD actions in the controller. For example:

Rails.application.routes.draw do
  resources :photos
end

This will generate seven routes for the Photo resource: (example from rails routing docuemntation)

HTTP VerbPathController#ActionUsed for
GET/photosphotos#indexdisplay a list of all photos
GET/photos/newphotos#newreturn an HTML form for creating a new photo
POST/photosphotos#createcreate a new photo
GET/photos/:idphotos#showdisplay a specific photo
GET/photos/:id/editphotos#editreturn an HTML form for editing a photo
PATCH/PUT/photos/:idphotos#updateupdate a specific photo
DELETE/photos/:idphotos#destroydelete a specific photo

Resource keyword

In contrast, the resource keyword in Rails is used to define a single resourceful route and controller that provides a RESTful approach to handling CRUD operations on a single resource. The resource keyword generates a set of standard routes that map to the standard CRUD actions in the controller, but only for a single resource. For example:

Rails.application.routes.draw do
  resource :profile
end

This will generate six routes for the Profile resource:

HTTP VerbPathController#ActionUsed for
GET/profile/newprofiles#newreturn an HTML form for creating the profile
POST/profileprofiles#createcreate the new profile
GET/profileprofiles#showdisplay the one and only profile resource
GET/profile/editprofiles#editreturn an HTML form for editing the profile
PATCH/PUT/profileprofiles#updateupdate the one and only profile resource
DELETE/profileprofiles#destroydelete the profile resource

Limiting generated resources

In Ruby on Rails, the only keyword can be used with the resources keyword to limit the set of routes that are generated for a particular resource. In some cases, not all of the generated actions are necessary. The only keyword can be used to specify a subset of these actions to generate routes for. For example:

Rails.application.routes.draw do
  resources :users, only: [:index, :show]
end

This will generate routes for the GET /users and GET /users/:id actions, but not for the POST, PATCH, or DELETE actions. By using the only keyword, developers can create a more focused and streamlined set of routes for each resource, which can help to reduce complexity and improve performance.

Grouping routes

In Ruby on Rails, there are three ways to group routes: using namespaces, scopes, and nested resources. Each grouping method affects the controller prefix and the path in a different way.

Namespace

Namespaces are used to group routes using a common module or path prefix. This creates a new module for the grouped routes, which allows for better encapsulation of the functionality. For example, if you have an admin section for managing products, you might define a namespace like this:

namespace :admin do
  resources :products
end

This will create a Admin::ProductsController controller and views in a separate folder structure (e.g. app/controllers/admin/products_controller.rb). The path for the routes will be prefixed with /admin, such as /admin/products, /admin/products/new, and /admin/products/:id/edit.

Scope

Scopes are used to group routes under a common path prefix without creating a new module. This means that the controller prefix and the path will be the same as the rest of the application. For example, if you have a section of your application that requires authentication, you might define a scope like this:

scope :auth do
  resources :users
end

This will create a UsersController controller in the same folder structure as the rest of the application. The path for the routes will be prefixed with /auth, such as /auth/users, /auth/users/new, and /auth/users/:id/edit.

Nested Resources

Nested resources are used when one resource is related to another resource. This creates a nested hierarchy of routes that reflect the relationships between the resources. For example, if you have a blog website where articles can have comments, you might define nested resources like this:

resources :articles do
  resources :comments
end

This will create a CommentsController controller and views in the same folder structure as the rest of the application. The controller prefix will be the same as the parent resource (ArticlesController), and the path for the routes will be nested under the parent resource, such as /articles/:article_id/comments, /articles/:article_id/comments/new, and /articles/:article_id/comments/:id/edit.

When deciding which grouping method to use, it's important to consider the level of encapsulation and hierarchy needed for the grouped routes. By choosing the appropriate grouping method, developers can create a clean and maintainable routing structure that is tailored to the specific needs of their application.

Custom Routing

Custom routes in Ruby on Rails allow developers to define their own routes in non-resourceful way. This can be useful when creating custom actions or when you want to define a more specific route for a particular action. To define a custom route, you can specify a path and a controller action using any HTTP verb. For example, suppose you want to create a custom route for a search action in a ProductsController. You could define it like this:

Rails.application.routes.draw do
  get '/products/search', to: 'products#search'
end

This will create a route that maps a GET request to /products/search to the search action in the ProductsController.

Resourceful vs non-resourceful routes

In Ruby on Rails, resourceful routes are routes that map to resources in the application's data model. They provide a standardized and consistent approach to defining and manipulating resources through a set of HTTP verbs (GET, POST, PUT/PATCH, DELETE) and URL endpoints. Non-resourceful routes, on the other hand, do not map to resources in the data model and are typically used for custom actions or operations that do not fit the standard CRUD model. While non-resourceful routes can be useful in certain situations, they can also make the application's code more difficult to maintain and can lead to inconsistencies in the API. By using resourceful routes, developers can create a clear and intuitive interface for accessing and modifying data, while also enforcing a consistent and predictable data model.

Advanced Routing Concepts

Member vs Collection

In Ruby on Rails, member and collection are methods used to define additional actions for specific resources. A member method is used to apply actions on a specific member (with a certain ID), while a collection method is used when you need to apply actions on a collection of members. For example, suppose you have a ProductsController that handles a collection of products. You can use member to define a custom route that operates on a specific resource, such as a custom_function_member action. This would apply to a specific product with a certain ID:

resources :products do
  member do
    get 'custom_function_member'
  end
end

Alternatively, you can use collection to define a custom route that operates on the entire collection of resources, such as a custom_function_collection action:

resources :products do
  collection do
    get 'custom_function_collection'
  end
end

By using member and collection, developers have more flexibility in defining the routing structure of their application, allowing for additional actions to be defined beyond the standard RESTful routes.

Concerns quick overview

In Ruby on Rails, concerns can be used to DRY up (Don't Repeat Yourself) repetitive code in routes. A concern is a reusable group of routes that can be included in other routes. For example, suppose you have a set of routes that are used in multiple controllers. You can define a concern to group these routes together:

concern :searchable do
  get 'search', on: :collection
end

Then, you can include this concern in your controllers:

resources :products, concerns: :searchable
resources :users, concerns: :searchable

This will add the search action to both the products and users resources. By using concerns, you can avoid duplicating code and keep your routing structure organized and maintainable.

Scopes within Resources problem

If you try to add a scope inside a resources block, you will face the problem of the scopes path being prepended to the entire path. So for example, if you write the following code:

  resource :article do
    scope 'admin' do
      resources :comments
    end
  end

You will expect to have the following path: article/admin/comment

However, the generated path is: admin/article/comment

This is a common problem in Ruby on Rails and after referring rails issues on github, using nested do solves it. So, the following code:

 resource :article do
    nested do
      scope 'admin' do
        resources :comments
      end
    end
  end

will generate the following path: article/admin/comment

Conclusion

In conclusion, routing is an essential component of web development and plays a crucial role in defining how requests are handled in a web application. In Ruby on Rails, routing is particularly important, as it helps developers create a clear and intuitive interface for accessing and modifying data. By understanding the basics of routing, including resources, CRUD operations, and how routes and controllers work together, developers can create scalable and maintainable applications that enforce a consistent data model and provide a clear separation of concerns. Grouping routes using namespaces, scopes, and nested resources can help to improve encapsulation and reduce complexity, resulting in more efficient and manageable code. By using the various routing techniques available in Ruby on Rails, developers can create powerful and flexible web applications that meet the needs of their users.

Sources