Dynamic Segment Keys
You may have seen route paths such as posts/:id
. If you're new to routing, you might be wondering why there are symbols inside the URL. These symbol-like terms are called segment keys, which make your URLs dynamic. In this lesson, we'll learn about the dynamic segment keys.
Instead of hard-coding a URL which matches exactly one URL, segment keys allow you to match more than one URL. You can access the values of those keys in the params
hash, e.g. params[:id]
.
Consider this route:
get "profile", to: "pages#profile"
As you can guess, when the Rails application receives a request on the /profile
page, it will direct that request to the profile
method on the PagesController
class.
At the same time, Rails provides the controller and action name in a hash called params
in the controller.
# controllers/pages_controller.rb
class PagesController < ApplicationController
def profile
puts params # {"controller"=>"pages", "action"=>"profile"}
end
end
The main use of the params
hash is to access the user-submitted information, such as query string parameters in the URL or the form data when the user submits the form.
So far, we've seen static URLs, such as /posts
, /about
, and /profile
. However, often your application URLs may contain dynamic content, such as /posts/20
, /users/2
, or /articles/learn-ruby
, where the numbers 20, 2, and the text learn-ruby
can vary depending on what resource the user is trying to access.
If your routes have this kind of dynamic content, i.e. parts of the URL that can vary, the Rails routing system lets you define them in the route, and make them available in the params
hash in the controller.
A segment key is just a variable prefixed with a colon, just like a symbol, inside a URL pattern. It lets you capture the value inside the URL.
You can make a route dynamic by providing segment keys in the URL pattern. Consider the following route with two dynamic segments, :section_name
and :slug
.
get 'section/:section_name/article/:slug', to: 'articles#show'
Now consider what will happen when you visit the following URL:
writesoftwarewell.com/section/rails/article/activerecord.
Rails will extract the values of dynamic segments :section_name
and :slug
, that is, rails
and activerecord
respectively; and make them available in the params
hash:
# controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
puts params
end
end
# Output
params = {
section_name: "rails",
slug: "activerecord
# ...
}
Now in your controllers, you can read these dynamic segments to generate and dispatch appropriate response, i.e. an article on ActiveRecord under the Rails section.
To generate the URL, you have to provide the values for the segment keys. For example, given this route:
get "songs/:genre", to: "songs#index", as: "songs"
Calling songs_path(genre: "pop")
in your application will generate the path "/songs/pop"
.
Here's a segment key named :slug
which lets you refer posts by their logical, dasherized name, i.e. ruby-on-rails
.
get '/posts/:slug', to: 'posts#show'
No, not this slug!
In the context of web applications, a Slug is the unique identifying part of a web address.
When you make a GET request to the URL matching the above route, e.g. /posts/understanding-router
, Rails sets params[:slug]
to understanding-router
. You can access the slug value inside the controller.
class PostsController < ApplicationController
def show
@post = Post.find_by(slug: params[:slug])
end
end
Note: If you use segment keys in a route, you have to provide their values when generating the URLs with the Rails helpers.
<%= link_to 'Understanding Rails', controller: 'posts', action: 'show', slug: 'understanding-rails' %>
Optional Segments
You can make your dynamic segments optional by wrapping the segment key in parenthesis. For example,
get 'songs(/:genre)', to: 'songs#index'
This route will match both /songs/happy
and /songs
and will be routed to the index
action on the SongsController
class.
If you want to provide default values for the dynamic segments, you can do so by providing the :defaults
option.
get "songs(/:genre)", to: "songs#index", defaults: { genre: "rock" }
When the user visits songs/pop
, the params[:genre]
will be pop
. However, if they visit only /songs
, the params[:genre]
will be rock
.
Pretty cool, right? Don't you just love Rails?
If you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. You can also subscribe to my blog to receive future posts via email.