Rails Routes - Mastering Routing in Ruby on Rails: A Concise Guide with Code Examples
Tags: rails, ruby, routing
Published: 2023-09-07 01:31:27

## 1. Single Route Resources
Single route resources are a straightforward way to define routes for a resource with only one route. Let's create a route for a "book" resource using Rails resources method:

```ruby
# config/routes.rb
resources :books, only: [:show]
```
In this example, we've defined a single "show" route for the "books" resource, which maps to the "BooksController#show" action.
But if we want all the whole REST routes for books, we use this.

```ruby
resources :books
```

## 2. Nested Routes
Nested routes in Ruby on Rails involve physically nesting one `resource` inside a block supplied to another `resource` in the routes file. This allows for URLs like '/books/2/chapters/10', where chapters are nested under books.

Nested routes allow you to express hierarchical relationships between resources. For instance, if you have a 'Chapter' resource that belongs to a 'Book,' you can set up nested routes like this:

```ruby
resources :books do
  resources :chapters
end
```
of course we have defined associations between models

```ruby
# app/models/book.rb
class Book < ApplicationRecord
  has_many :chapters
end
```
and

```ruby
# app/models/chapter.rb
class Chapter < ApplicationRecord
  belongs_to :book
end
```


## 3. Member and Collection Routes

In Ruby on Rails, when defining routes for a resource, you have the flexibility to add custom actions using member and collection routes. These two types of routes serve different purposes and are suitable for distinct scenarios.

Suppose you have a "books" resource and want to add a custom "highlight" action for a specific book. You can define a member route like this:

```ruby
resources :books do
  member do
    get 'highlight'
  end
end

```
Now, you can access the "highlight" action for a specific book using a URL like "/books/1/highlight," where "1" represents the book's ID.

Suppose you want to add a custom "search" action that searches for books across the entire collection. You can define a collection route like this:

```ruby
resources :books do
  collection do
    get 'search'
  end
end
```
Now, you can perform a search action that applies to all books using a URL like "/books/search."

Note that **Member routes** typically map to actions that perform operations on a single member or instance of a resource.
**Collection routes** map to actions that involve the entire collection of a resource.

## 4. Adding Non-RESTful Routes

Sometimes, you may need routes that don't follow RESTful conventions. For example, let's create a custom route for marking a book as a favorite:

```ruby
# config/routes.rb
resources :books do
  post 'favorite', on: :member
end
```
Below are other different approaches to implement a non-RESTful route for this scenario:

### **Using the match Method**
This approach allows you to specify the HTTP method (e.g., POST) and the controller action to be invoked. The :as option allows you to create a named route helper method, such as favorite_book_path, for generating URLs to this route.

```ruby
match '/books/:id/favorite', to: 'books#favorite', via: :post, as: :favorite_book
```
### **Using post Method with on: Option**
By specifying the on: option within the post method in your routes file. This approach explicitly states that the "favorite" route is a member route, and it maps to the favorite action in the BooksController

```ruby
resources :books do
  post 'favorite', on: :member, to: 'books#favorite'
end
```
### **Using Custom Named Routes**
With this approach, you have flexibility in naming the route and can use the generated named route helper (e.g., mark_favorite_path or custom_favorite_route_path) to generate URLs for this non-RESTful action.

```ruby
post '/books/:id/favorite', to: 'books#favorite', as: :mark_favorite

# OR, using a custom helper method name
post '/books/:id/favorite', to: 'books#favorite', as: :custom_favorite_route

```


Now, you can mark a book as a favorite by making a POST request to "/books/1/favorite."


## 5. Redirects
In Rails, you can define redirects easily in your routes file. Suppose you want to redirect "/old_path" to "/new_path":

```ruby
# config/routes.rb
get '/old_path', to: redirect('/new_path')
```
This code will redirect any request to "/old_path" to "/new_path."

## 6. Wildcard Routes
Wildcard routes are a powerful tool for handling dynamic URLs. Suppose you want to capture all requests to "/books/*" and route them to a specific controller action:

```ruby
get '/books/*path', to: 'books#show'
```
With this wildcard route, you can capture various book-related paths and route them to the "BooksController#show" action.

## 7. Some Anti-Patterns, or Please Dont
### Overuse of Custom Routes
Anti-Pattern: Defining excessive custom routes for every controller action
```ruby
resources :books do
  get 'custom_action_1', on: :member
  post 'custom_action_2', on: :collection
  put 'custom_action_3', on: :member
  # ... more custom actions ...
end
```
Instead, it's recommended to use RESTful routes whenever possible and resort to custom routes only when necessary.

### Verbosity in Named Routes:
Anti-Pattern: Using overly verbose names for named routes.

```ruby
# BAD
get '/books/:id/edit_book', to: 'books#edit', as: :edit_book_action

# GOOD
get '/books/:id/edit', to: 'books#edit', as: :edit_book
```

### Unused or Dead Routes
Anti-Pattern: Defining routes that are no longer used in the application.

```ruby
get '/unused_route', to: 'unused#action'

```
Such routes should be removed to maintain a clean and maintainable route configuration

### Complex Route Constraints
Anti-Pattern: Overcomplicating routes with complex constraints.

```ruby
get '/books/:id', to: 'books#show', constraints: { id: /\d{3}/ }
```
Keep route constraints simple and understandable, and use them sparingly.

### Lack of RESTful Structure with RESTful actions
Anti-Pattern: Creating routes that don't adhere to RESTful principles when these are for RESTful action in the first place.

```ruby
# BAD because show is a RESTful action
get '/books/show_book/:id', to: 'books#show'
# Better
resources :books, only: [:show]
```
### Inconsistent Route Naming
Anti-Pattern: Using inconsistent naming conventions for routes.

```ruby
# Wrong
get '/books/show_book/:id', to: 'books#show', as: :display_single_book
# Best
resources :books, only: [:show]
```

Consistency in naming conventions makes the codebase more understandable

By avoiding these anti-patterns and following Rails' conventions for routing definitions, you can maintain a clean and maintainable routing configuration in your Ruby on Rails application, making it easier for developers to understand and work with the code.

## Conclusion:
Mastering routing in Ruby on Rails is essential for building robust web applications. In this concise guide, we've covered single route resources, nested routes, member and collection routes, non-RESTful routes, redirects, and wildcard routes, and anti-patterns, each of these illustrated with code examples. By understanding these routing concepts, you'll be better equipped to handle complex routing scenarios in your Rails applications.