Chapter 1 From zero to deploy
Welcome to the Ruby on Rails Tutorial! The purpose of this tutorial is to teach you how to develop custom web applications. The resulting skillset will put you in a great position to get a job as a web developer, start a career as a freelancer, or found a company of your own. If you already know how to develop web applications, this tutorial will quickly get you up to speed with Ruby on Rails.
The focus throughout the Ruby on Rails Tutorial is on general skills that are useful no matter which specific technology you end up using. Once you understand how web apps work, learning another framework can be done with much less effort. That being said, the framework of choice in this tutorial—namely, Ruby on Rails—continues to be an outstanding choice for learning web development (Box 1.1).
Ruby on Rails (or just “Rails” for short) is a free and open-source web development framework written in the Ruby programming language. Upon its debut, Ruby on Rails rapidly became one of the most popular tools for building dynamic web applications. Rails is used by companies as varied as Airbnb, SoundCloud, Disney, Hulu, GitHub, and Shopify, as well as by innumerable freelancers, independent development shops, and startups.
Although there are many choices in web development, Rails stands apart for its elegance, power, and integrated approach to web applications. Using Rails, even novice developers can build a full-stack web application without ever leaving the framework—a huge boon for people learning web development for the first time. Rails also gives you flexibility going forward—for example, serving as a great back end if you want to build a single-page application or mobile app sometime down the line.
One big advantage is that Rails is not prone to the “new hotness” problem that plagues some development communities (notably JavaScript/Node.js), in which a dizzyingly complex set of technologies seems to change every six months. As Rails creator David Heinemeier Hansson once noted:
Back then the complexity merchant of choice was J2EE, but the complaints are uncannily similar to those leveled against JavaScript today… The core premise of Rails remains in many ways as controversial today as it was when it premiered. That by formalizing conventions, eliminating valueless choices, and offering a full-stack framework that provides great defaults for anyone who wants to create a complete application, we can make dramatic strides of productivity.
Due in part to this philosophy, Rails has remained so stable at its core that much of this tutorial has been the same since the third edition, launched in 2014. The things you learn here won’t go out of date soon.
And yet, Rails continues to innovate. For example, the Rails 6 release included major new features for email routing, text formatting, parallel testing, and multiple-database support, allowing it to be “scalable by default”. And the landmark Rails 7 release integrated technologies like Hotwire and Turbo to “deliver all the tools needed to produce fantastic user experiences that leave little to nothing on the table in contrast to single-page applications—at a fraction of the complexity.”
Rails does all this while maintaining rock-solid dependability—indeed, the wildly popular developer platform GitHub, the hugely successful online store-builder Shopify, and the collaboration tool (and very first Rails app) Basecamp all run their sites on the pre-release versions of Rails. This means that new versions of Rails are immediately tested by some of the largest, most successful web apps in existence.
Not bad for a little side project cooked up by a freelance Danish web developer way back in 2004. What was an edgy choice then is an easy choice now: with its proven track record, productive feature set, and helpful community, Rails is a fantastic framework for building modern web applications.
There are no formal prerequisites for this book, which contains integrated tutorials for the Ruby programming language, test-driven development (TDD), the Unix command line, version control with Git, HTML, a fair amount of CSS, some JavaScript (including Hotwire and Turbo), and even a little SQL. That’s a lot of material to absorb, though, and if you’re new to software development I recommend starting with the tutorials at Learn Enough, especially Learn Enough Command Line to Be Dangerous and Learn Enough Ruby to Be Dangerous.1 On the other hand, a surprising number of complete beginners have gotten through this tutorial, so don’t let me stop you if you’re excited to build web apps.
The principal teaching method of this tutorial is building real working software through a series of example applications of increasing sophistication, starting with a minimal hello app (Figure 1.1, Section 1.2), a slightly more capable toy app (Figure 1.2, Chapter 2), and a real sample app (Figure 1.3,2 Chapter 3 through Chapter 14). As implied by their generic names, these applications focus on general principles, which are applicable to practically any kind of web application. In particular, the full sample application includes all the major features needed by professional-grade web apps, including user signup, login, and account management. The final version of the sample app, developed in Chapter 14, also bears more than a passing resemblance to Twitter—a website that, coincidentally, was also originally written in Rails.
Let’s get started!
echo
and >>
(append) commands covered in Section 1.3 and Section 2.1 of Learn Enough Command Line to Be Dangerous. Note that if the file being appended to doesn’t exist, >>
is smart enough to create it.gem list <gem name>
at the command line, but Listing 1.8 saves you the trouble.install
, as the bundle
command by itself is an alias for bundle install
.rails _7.0.4_
because the right version of Rails gets used automatically. As a result, in Listing 1.11 we can just write rails server
by itself, and similarly for other commands.git add .
, where .
(“dot”) represents the current directory. In the rare cases where the two differ, what you usually want is git add -A
, and this is what’s used in the official Git documentation, so that’s what we go with here..gitignore
file appears in Section 3.6.2, which is part of the optional advanced testing setup in Section 3.6.-u
option to git push
sets GitHub as the upstream repository, which means you’ll be able to download any changes automatically by running git pull
, although we’ll never need to do so in this tutorial. See Learn Enough Git to Be Dangerous for more information.heroku open
to open the site automatically in a web browser.sample
: ('a'..'z').to_a.sample(8).join
. Thanks to alert reader Stefan Pochmann for pointing this out—I didn’t even know about sample
until he told me!1.2.1 Bundler
After creating a new Rails application, the next step is to use Bundler to install and include the gems needed by the app. A Gemfile
was automatically created by the rails
command in Listing 1.6, but in this section we’ll make some changes to the default application gems. This involves opening the Gemfile
with a text editor. (With the cloud IDE, this means clicking the arrow in the file navigator to open the sample app directory and double-clicking the Gemfile
icon.) Although the exact version numbers and details may differ slightly, the results should look something like Figure 1.12 and Listing 1.7. (The code in this file is Ruby, but don’t worry at this point about the syntax; Chapter 4 will cover Ruby in more depth.)
If the files and directories don’t appear as shown in Figure 1.12, click on the file navigator’s gear icon and select “Refresh File Tree”. (As a general rule, you should refresh the file tree any time files or directories don’t appear as expected.)9
Gemfile
in the hello_app
directory.
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "3.1.2"
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 7.0.4"
# The original asset pipeline for Rails
# [https://github.com/rails/sprockets-rails]
gem "sprockets-rails"
# Use sqlite3 as the database for Active Record
gem "sqlite3", "~> 1.4"
# Use the Puma web server [https://github.com/puma/puma]
gem "puma", "~> 5.0"
# Use JavaScript with ESM import maps
# [https://github.com/rails/importmap-rails]
gem "importmap-rails"
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails"
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem "stimulus-rails"
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
gem "jbuilder"
# Use Redis adapter to run Action Cable in production
# gem "redis", "~> 4.0"
# Use Kredis to get higher-level data types in Redis
# [https://github.com/rails/kredis]
# gem "kredis"
# Use Active Model has_secure_password
# [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
# gem "bcrypt", "~> 3.1.7"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", require: false
# Use Sass to process CSS
# gem "sassc-rails"
# Use Active Storage variants
# [guides.rubyonrails.org/active_storage_overview.html#transforming-images]
# gem "image_processing", "~> 1.2"
group :development, :test do
# See https://guides.rubyonrails.org/
# debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[ mri mingw x64_mingw ]
end
group :development do
# Use console on exceptions pages [https://github.com/rails/web-console]
gem "web-console"
# Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
# gem "rack-mini-profiler"
# Speed up commands on slow machines / big apps
# [https://github.com/rails/spring]
# gem "spring"
end
group :test do
# Use system testing
# [https://guides.rubyonrails.org/testing.html#system-testing]
gem "capybara"
gem "selenium-webdriver"
gem "webdrivers"
end
Many of these lines are commented out with the hash symbol #
(Section 4.2); they are there to show you some commonly needed gems and to give examples of the Bundler syntax. For now, we won’t need any gems other than the defaults.
Unless you specify a version number to the gem
command, Bundler will automatically install the latest requested version of the gem. This is the case, for example, in the code
gem "sprockets-rails"
There are also two common ways to specify a gem version range, which allows us to exert some control over the version used by Rails. The first looks like this:
gem "capybara", ">= 3.26"
This would install the latest version of the capybara
gem (which is used in testing) as long as it’s greater than or equal to version 3.26
—even if it’s, say, version 7.2
.
The second method looks like this:
gem "sqlite3", "~> 1.4"
This installs the gem sqlite3
as long as it’s version 1.4
or newer (a “minor update”) but not 2
or newer (a “major update”). In other words, the >=
notation always installs the latest gem as long as it meets the minimum version requirement, whereas the ~> 1.4
notation will install 1.5
(if available) but not 2.0
.10
The main idea behind using ~>
is that it should generally be safe to use the latest minor update of a gem, but experience shows that even minor point releases can break Rails applications. As a result, we’ll err on the side of caution by including exact version numbers for all gems. You are welcome to use the most up-to-date version of any gem, including using the ~>
construction in the Gemfile
(which I generally recommend for more advanced users), but be warned that this may cause the tutorial to act unpredictably.
Converting the Gemfile
in Listing 1.7 to use exact gem versions results in the code shown in Listing 1.8.11 Note that we’ve also taken this opportunity to arrange for the sqlite3
gem to be included only in a development or test environment (Section 7.1.1), which prevents potential conflicts with the database used by Heroku (Section 1.4). We’ve also commented out the final line because it is specific to Microsoft Windows and results in a potentially confusing warning message on non-Windows systems; you should uncomment that line if you are running Rails on a native Windows system.
Important note: For all the Gemfiles in this book, you should use the version numbers listed at gemfiles-7th-ed.railstutorial.org instead of the ones listed below (although they should be identical if you are reading this at learnenough.com or railstutorial.org).
Gemfile
with an explicit version for each Ruby gem.
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "3.1.2"
gem "rails", "7.0.4"
gem "sassc-rails", "2.1.2"
gem "sprockets-rails", "3.4.2"
gem "importmap-rails", "1.1.0"
gem "turbo-rails", "1.1.1"
gem "stimulus-rails", "1.0.4"
gem "jbuilder", "2.11.5"
gem "puma", "5.6.4"
gem "bootsnap", "1.12.0", require: false
group :development, :test do
gem "sqlite3", "1.4.2"
gem "debug", "1.5.0", platforms: %i[ mri mingw x64_mingw ]
end
group :development do
gem "web-console", "4.2.0"
end
group :test do
gem "capybara", "3.37.1"
gem "selenium-webdriver", "4.2.0"
gem "webdrivers", "5.0.0"
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem.
# Uncomment the following line if you're running Rails
# on a native Windows system:
# gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
With those preliminaries out of the way, we’re finally ready to use Bundler to install the gems needed for the hello app. Once you’ve placed the contents of Listing 1.8 into the application’s Gemfile
and saved the file in your editor, install the gems using bundle install
as shown in Listing 1.9.12 As with the rails
command in Listing 1.6, the command in Listing 1.9 uses an explicit version number to ensure that bundle
is being run with the correct version of the bundler
gem (i.e., the one installed in Listing 1.3). This is recommended for maximum compatibility with this tutorial, but it’s not generally what you would do in real life (Box 1.4).
$ cd hello_app/
$ bundle _2.3.14_ install
Fetching source index for https://rubygems.org/
.
.
.
The bundle install
command might take a few moments, but when it’s done our application will be ready to run. (Be sure to uncomment the final line in Listing 1.8 if you’re running Rails on a native Windows system.)
Why do commands like Listing 1.6 and Listing 1.9 use rails _7.0.4_ new
and bundle _2.3.14_ install
instead of just using rails new
and bundle install
?
The answer is maximum future compatibility: a new version of Rails or Bundler might very well break the steps shown in this tutorial, and we want to prevent that if we can. It’s impossible to anticipate every possible source of breakage—one of many reasons why technical sophistication (Box 1.2) remains indispensable—but using exact version numbers minimizes the risk.
In effect, such commands work like the training wheels commonly used when learning to ride a bicycle. And just as you eventually remove your bike’s training wheels, in real life you’ll eventually type the bare commands:
$ rails new hello_world
and
$ bundle install
or even just
$ bundle
(It turns out bundle
is a shortcut for bundle install
.)
As noted, these bare commands can cause incompatibilities. For example, leaving off the --skip-bundle
option in Listing 1.6 causes Rails to run bundle install
automatically—using the most recent version of Bundler on the system, which might or might not correspond to the version installed in Listing 1.3. This isn’t necessarily a big deal, because you can recover from any incompatibilities by running a command like
$ bundle update
or
$ rm -f Gemfile.lock $ bundle
But when you’re just starting out, you might not know these commands, and figuring out how to recover from such errors can be frustrating or confusing.
So, how do you know when to take off the training wheels? After finishing this tutorial (or, if you’re feeling adventurous, during this tutorial), try leaving off the exact version numbers, or maybe use the ~>
syntax for specifying gem versions shown in Listing 1.7. If things still work, great; if they go wrong, use your technical sophistication to solve the issue. And if you still can’t get things to work—just use the exact versions as shown here, and save taking off the training wheels for another day.
1.3.1 Git setup
The cloud IDE recommended in Section 1.1.1 includes Git by default, so the only configuration that might be necessary is ensuring that the version number is later than 2.28.0. You can find the Git version number as follows:
$ git --version # should be 2.28.0 or later
git version 2.17.1
If the version number is less than 2.28.0 (as 2.17.1 is here), then you will need to upgrade it. On the cloud IDE, you can do this by running the command shown in Listing 1.15.
$ source <(curl -sL https://cdn.learnenough.com/upgrade_git)
If you need to upgrade Git but you’re not using the cloud IDE, you should refer to Learn Enough Git to Be Dangerous, which includes instructions for installing Git on your system.
First-time system setup
Before using Git, you should perform a few one-time setup steps. These are system setups, meaning you have to do them only once per computer.
The first (and required) step is to configure your name and email address, as shown in Listing 1.16.
$ git config --global user.name "Your Name"
$ git config --global user.email your.email@example.com
Note that the name and email address you use in your Git configuration will be available in any repositories you make public.
The second step is to configure Git’s default branch name as in Listing 1.17.
$ git config --global init.defaultBranch main
Listing 1.17 sets the default Git branch name to main
. (Because videos are relatively hard to update, the screencasts that accompany this book use master
, which was the default branch name for the first 15+ years of Git’s existence, but the text has been updated to use main
, which is the current preferred default. See the Learn Enough blog post “Default Git Branch Name with Learn Enough and the Rails Tutorial” for more information.) We’ll learn more about Git branches starting in Section 1.3.4.
Next, we’ll take an optional but convenient step and set up an alias, or synonym, for the commonly used checkout
command, as shown in Listing 1.18.
git co
as a checkout alias.
$ git config --global alias.co checkout
In this tutorial, I’ll always use the full git checkout
command for maximum compatibility, but in practice I almost always use git co
for short.
The final step is to prevent Git from asking for your authentication credentials every time you want to use commands like push
or pull
(Section 1.3.4). The options for doing this are system-dependent; see the article “Caching your GitHub credentials in Git” for information on how to set this up on your system17 if you’re using anything other than Linux (including the cloud IDE). If you are using Linux (including the cloud IDE), you can simply set a cache timeout as shown in Listing 1.19.
$ git config --global credential.helper "cache --timeout=86400"
Listing 1.19 configures Git to remember any passwords you use for 86,400 seconds (one day).18 If you’re highly security-conscious, you can use a shorter timeout, such as the default 900 seconds, or 15 minutes.
First-time repository setup
Now we come to some steps that are necessary each time you create a new repository (sometimes called a repo for short). The first step is to navigate to the root directory of the hello app and initialize a new repository:
$ cd ~/environment/hello_app # Just in case you weren't already there
$ git init
Reinitialized existing Git repository in
/home/ubuntu/environment/hello_app/.git/
Note that Git outputs a message that the repository has been reinitialized. This is because, as of Rails 6 and continuing in Rails 7, running rails new
(Listing 1.6) automatically initializes a Git repository (a strong indication of how ubiquitous Git’s use is in tech). Thus, the git init
step isn’t technically necessary in our case, but this won’t hold for general Git repositories, so always running git init
is a good habit to cultivate.
The next step is to add all the project files to the repository using git add -A
:19
$ git add -A
This command adds all the files in the current directory apart from those that match the patterns in a special file called .gitignore
. The rails new
command automatically generates a .gitignore
file appropriate to a Rails project, but you can add additional patterns as well.20
The added files are initially placed in a staging area, which contains pending changes to our project. We can see which files are in the staging area using the status
command:
$ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .browserslistrc
new file: .gitignore
new file: .ruby-version
new file: Gemfile
new file: Gemfile.lock
.
.
.
To tell Git we want to keep the changes, we use the commit
command:
$ git commit -m "Initialize repository"
[main (root-commit) df0a62f] Initialize repository
.
.
.
The -m
flag lets us add a message for the commit; if we omit -m
, Git will open the system’s default editor and have us enter the message there. (All the examples in this tutorial will use the -m
flag.)
It is important to note that Git commits are local, recorded only on the machine on which the commits occur. We’ll see how to push the changes up to a remote repository (using git push
) in Section 1.3.4.
By the way, we can see a list of the commit messages using the log
command:
$ git log
commit b981e5714e4d4a4f518aeca90270843c178b714e (HEAD -> main)
Author: Michael Hartl <michael@michaelhartl.com>
Date: Wed Mar 9 17:57:06 2022 +0000
Initialize repository
Depending on the length of the repository’s log history, you may have to type q
to quit. (As explained in Learn Enough Git to Be Dangerous, git log
uses the less
interface covered in Learn Enough Command Line to Be Dangerous.)