Understanding Rails Routing
In this article we will go over a few pointers on Rails Routing.
Published on:October 15, 2015
Introduction
Ruby on Rails offers a fundamentally different way of dealing with URLS compared to most other languages. Rather than rely on the web server to control URL routing, Rails handles routing via the config/routes.rb
file. This file controls every single aspect of your URLs. In this article we will cover several routing basics. Let's get started.
RESTful Routes
Before we get to routing, we must understand REST. Rails uses REST extensively for URL routing so we must have a good understanding of REST in order to understand Rails routing. REST stands for Representational State Transfer. RESTful applications typically treat the web like a resource. With REST, there are several HTTP methods that are used to represent the types of actions performed by the user and/or application.
HTTP Method | What it's used for | Examples |
---|---|---|
GET | Retrieving a resource. | Any time you navigate directly to a page or use google to navigating the page, you use the GET http method. |
POST | Creating a resource | Older web applications used POST for everything. Many browsers only understand POST and GET. More on this below. |
PUT | Used to completely update a resource. | Updating your user profile on some website would typically use patch with web frameworks that support it. |
PATCH | Used to partially update a resource. | An example of this would be where you are just updating the password for your user profile. |
You'll notice in the table above that I mentioned that most browsers only understand POST and GET. This isn't a problem for Rails. Rails actually has the HTTP method buried inside a hidden form element. When the page interacts with the server, the request is intercepted and reconstructed.
The Rails Routes File
Typically in a Rails routes file you specify an HTTP method followed by the page or action. For example:
get "users", to: "users#index"
# http GET method, In this instance, gets a list of users
get "users/:id", to: "users#show"
# In this instance, gets a specific user via the provided id. For example: /users/3882
post "users", to: "users#create"
# http POST method, In this instance, used for creating a user.
put "users/:id"
# http PUT method, used for updating a resource, In this instance, updates the user. Older versions of Rails used this for all updates.
patch "users/:id"
# http PATCH method. in this instance, used to partially update the user's information.
delete "users/:id"
# http DELETE method. In this instance, used to delete a user.
All of the methods listed above use HTTP methods to tell Rails what methods to call on the controller. They are so important that Rails includes 2 special keywords to automatically create them. The first, resources
tells Rails you wish to specify the above actions for a given controller):
resources :users
# generates:
# get "/users" -- index on your controller
# get "/users/:id" -- show on your controller
# get "/users/new" -- new method on your controller
# post "/users" -- create on your controller
# get "/users/:id/edit" -- edit method on your controller
# put "/users/:id" -- update on your controller
# patch "/users/:id" -- update on your controller
# delete "/users/:id" -- destroy on your controller
The second keyword, resource
(no collection), is used if you are interacting with a single resource. In this case, certain routes are omitted.
resource :privacy_policy
# generates:
# get "/privacy_policy" -- show on your controller
# get "/privacy_policy/new" -- new method on your controller
# post "/privacy_policy" -- create on your controller
# get "/privacy_policy/edit" -- edit method on your controller
# put "/privacy_policy" -- update on your controller
# patch "/privacy_policy" -- update on your controller
# delete "/privacy_policy" -- destroy on your controller
In the above block of code you will notice slight differences compared to resources. A singular resource is typically used when there is only one instance of a given resource. For instance, on a given website, there may only be one privacy policy, not many.
You can quickly and easily skip certain actions for both resource
and resources
. To do this you use either the except
keyword or only
keyword. As their names imply, the except keyword tells rails to create all actions except the ones you specify, while the only keyword tells Rails to only create the given routes.
resources :users, except: [:show]
# generates actions for everything except for get "/users/:id"
resource :privacy_policy, only: [:show]
# only generate the get "/privacy_policy route for the Privacy Policy.
Custom Actions
In certain cases, you may wish to have custom actions. These actions may either be on an individual resource (/users/:id) or a collection of resources (/users).
resources "posts" do
put :rate # generate put "/posts/:id/rate_up"
end
resources "products" do
collection do
get "most_popular" # generate get "/products/most_popular"
end
end
Custom actions typically come in handy to either make the URL more meaningful or to imply more functionality.
Nested Resources
Resources can also be nested:
resources :users do
resources :posts
end
This would create what is called a nested resource. This lets you do things like http://yourdomain.com/users/4/posts/
, which would list all of the posts for that specific user. Your controller would then receive a user_id parameter as an argument.
Aliasing and Redirects
You can also create aliases for URLs. This means you can have a user friendly url such as http://yourdomain.com/login
call a specific controller and action, the new action on your sessions controller for example.
get 'login', to: 'sessions#new'
get 'logout', to: "sessions#destroy"
You can also redirect legacy URLs to new locations using a similar mechanism. For instance:
get 'webcatalog/', to: redirect("/products")
# redirect http://mydomain.com/webcatalog/ to http://mydomain.com/products
get "/webcatalog/product_info.php", to: redirect {|params, req|
begin
id = req.params[:products_id]
product = Product.find(id)
"/products/#{product.slug}"
rescue
"/products"
end
}
# Slightly more advanced example that uses a block to redirect from an OSCommerce page to a Rails style URL.
Constraints
You can also specify constraints. For instance, let's say you want to constrain certain actions to a particular subdomain. You can do so with the following code.
constraints subdomain: 'api' do
resources :products do
end
Constraints can be even more advanced, for example, the following constraint checks to see if the user is using an iPhone:
constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
resources :products
end
For really advanced constraints, you can create a class. For example, the following code does the exact same thing as before:
class Iphone
def self.matches?(request)
request.env["HTTP_USER_AGENT"] =~ /iPhone/
end
end
constraints(Iphone) do
resources :products
end
Namespaces
You can have custom namespaces as well. Everything in a namespace will be prefixed by the namespace. For instance:
namespace :admin do
get 'posts' => "posts#index" # http://yourapp.com/admin/posts
end
Conclusion
This isn't meant to be an exhaustive guide to routing in Ruby on Rails, but rather it's designed to help you get your feet wet without having to dig through tons of documentation. Let me know if you find this guide useful. Thanks for reading!