Blog

Rails Discovery: Magical Routes - Part 1: Major Usages

Placeholder Avatar
Tianwen Chen
May 26, 2014

NOTE: The Rails version for this article is v3.2.17. The content might not be applicable to other Rails version.

NOTE: Before we dig into the Rails routing, if you are not familiar with it, please have a read on [Rails Guides: Rails Routing from the Outside In] (http://guides.rubyonrails.org/routing.html).

In this article, we will talk about three different ways to generate URLs used by Rails among controllers, mailers, views and helpers. There will be examples and some suggestions on when you should use them.

1. Named Route Path Helper

As in most of the Rails app, you might see the following snippet:

erb <%= link_to edit_post_path(@post, source: 'external'), 'Edit' %>

When it is rendered, it will generate the following HTML:

html <a href="/posts/1/edit?source=external">Edit</a>

The edit_post_path is a registered helper method and the edit_post is called a named route. Most Rails devs should be familiar with this as one of the Rails conventions, but especially for beginners, how does it work? We will find out in the next article. Before that, let’s take a look at how to find out all of the named routes. Run the following command in your terminal:

sh orz $ bundle exec rake routes

It will print a list of named routes that are available inside controllers, mailers, views and helpers.

sh Prefix Verb URI Pattern Controller#Action posts GET /posts(.:format) posts#index POST /posts(.:format) posts#create new_post GET /posts/new(.:format) posts#new edit_post GET /posts/:id/edit(.:format) posts#edit post GET /posts/:id(.:format) posts#show PATCH /posts/:id(.:format) posts#update PUT /posts/:id(.:format) posts#update DELETE /posts/:id(.:format) posts#destroy OR open this link in the browser to view a similar output from the local rails server [http://localhost:3000/rails/info/routes] (http://localhost:3000/rails/info/routes) NOTE: This routes link is ONLY visible in the development environment.

Prefix

Prefix column lists the unique named routes. Normally, the named route together with a suffix _path or _url forms a helper method, e.g. new_post_path NOTE: By appending _url, the named route path helper will generate a full URL, e.g. new_post_url => http://localhost:3000/posts/new, whereas appending _path will just generate the site-relative URL, e.g. new_post_path => /posts/new.

Verb

Verb column specifies the HTTP request method for a route. Generally, the acceptable HTTP request methods for Rails are GET, POST, PATCH, PUT and DELETE. (see constant HTTP_METHODS on [ActionDispatch::Request] (http://api.rubyonrails.org/v3.2.17/classes/ActionDispatch/Request.html) for more details )

URI Pattern

URI Pattern column shows the URI pattern that Rails uses to match a route. Symbols will be mapped into the params helper method. For instance, for URI pattern /posts(.:format), the json of URI /posts.json will be stored in params[:format].

Controller#Action

Controller#Action column shows the corresponding controller and action to which Rails will dispatch the HTTP request.

When / Where should I apply this?

If you know exactly what the path or URL is that you are going to generate, you should use the named route path helper methods. By using it, your code will become more readable.

2. URL Helper Method

ActionDispatch::Routing::UrlFor#url_for is the URL helper method. It generates a URL based on the options supplied. It is pretty flexible in that it accepts either a string, an ActiveRecord object, an options hash, or an argument list array. Let’s take a glimpse at some examples and see how powerful it is.

Examples

ruby url_for('/posts') # => /posts url_for(@post) # => http://localhost:3000/posts/1 url_for(controller: :comments, post_id: @post.id) # => http://localhost:3000/posts/1/comments url_for([@post, :comments]) # => http://localhost:3000/posts/1/comments url_for([@post, routing_type: :path]) # => /posts/1 url_for([@post, :comments, slug: 'reinteractive', only_path: true]) # => /posts/1/comments?slug=reinteractive url_for([:admin, :posts, format: :js]) # => http://localhost:3000/admin/posts.js

It accepts the following hash options, you could find this in the [API document #url_for] (http://api.rubyonrails.org/v3.2.17/classes/ActionDispatch/Routing/UrlFor.html#method-i-url_for):

Options for URL Helper Method

  • :only_path - If true, the relative url is returned. Defaults to false.
  • :protocol - The protocol to connect to. Defaults to ‘http’.
  • :host - Specifies the host the link should be targeted at. If :only_path is false, this option must be provided either explicitly, or via default_url_options.
  • :subdomain - Specifies the subdomain of the link, using the :tld_length to split the subdomain from the host. If false, removes all subdomains from the host part of the link.
  • :domain - Specifies the domain of the link, using the :tld_length to split the domain from the host.
  • :tld_length - Number of labels the TLD id composed of, only used if :subdomain or :domain are supplied. Defaults to ActionDispatch::Http::URL.tld_length, which in turn defaults to 1.
  • :port - Optionally specify the port to connect to.
  • :anchor - An anchor name to be appended to the path, as in “/archive#2009”
  • :trailing_slash - If true, adds a trailing slash, as in “/archive/2009/”
  • Any other key (:controller, :action, etc.) given to url_for is forwarded to the Routes module. Additionally, you might come across this option in the source code:
  • :routing_type - Specifies which type of URL it should be, relative or full. Value can be :url or :path. Defaults to :url.

When / Where should I apply this?

The url_for method is useful when you require flexibility. Consider the following situations: - Passing variable ActiveRecord objects to the helper, e.g. url_for(@post_or_comment) - Specifying variable options (:controller, :action, :anchor, etc.), e.g. url_for(controller: @posts_or_comments_controller) - Specifying the URL string, e.g. url_for('/posts') You will find this helper very useful.

3. Polymorphic URL Helper Methods

ActionDispatch::Routing::PolymorphicRoutes#polymorphic_path and ActionDispatch::Routing::PolymorphicRoutes#polymorphic_url are the polymorphic URL helpers which can handle either an ActiveRecord object, an options hash, or an Array. Under the hood, polymorphic_url is called by url_for, and both polymorphic_path and polymorphic_url can be prefixed with new_ and edit_. In other words, the polymorphic URL helpers wrap the url_for method to provide better object support, although they can’t handle plain strings.

Examples

ruby polymorphic_path(@post) # => /posts/1 polymorphic_url([@post, @comment]) # => http://localhost:3000/posts/1/comments/1 edit_polymorphic_path(@post) # => /posts/1/edit new_polymorphic_path(:post) # => /posts/new NOTE: [The options for URL helper method] (#options-for-url-helper-method) are also applicable for polymorphic URL helper methods. Polymorphic URL helper methods are very powerful and they’ve been used in a lot of places throughout rails, e.g. ActionView::Helpers::UrlHelper#link_to, ActionView::Helpers::FormHelper#form_for, ActionController::Redirecting#redirect_to (see [ActionDispatch::Routing::PolymorphicRoutes] (http://api.rubyonrails.org/v3.2.17/classes/ActionDispatch/Routing/PolymorphicRoutes.html))

When / Where should I apply this?

Similar to url_for, the situations where polymorphic urls are very useful are when you have a dynamic argument. Imagine you had a homepage where you wanted an edit link for the latest post or the latest comment by the current user in a sidebar. You could call the method like this:

edit_polymorphic_url(@post_or_comment). In this case it would also be trivial to check the class of the object, but that approach becomes unwieldy when you have more than just a couple of classes to consider.

When building an engine or a gem that abstracts functionality without necessarily knowing which classes it is working with, helpers like this will help immensely to keep your code clean.

We will continue to explain how the named routes works in next article Part 2