Chapter 1: From zero to deploy | Ruby on Rails Tutorial | Learn Enough to Be Dangerous
You have to make a choice. Choose...wisely.

Get occasional notifications about things like product discounts, blog posts, and new or updated tutorials. Unsubscribe at any time.

Gift Delivery Options
Quick Checkout
or Pay by Credit Card
Error processing your payment
  • You didn't choose whether or not to be added to the mailing list
Confirm
$0.00

Payments and credit card details are securely managed and protected by Learn Enough's payment processor, Stripe. More information on their site:

CART
Total
$0.00

Your Cart is Empty

$30
$300
$300
$XY
$XY
1234
Get Single Tutorial
MORE INFO

Ruby on Rails Tutorial is available as an ebook, an offline video series, and as a structured, self-paced online course. The course includes full online access to the book content, streaming videos, progress tracking, exercises, and community exercise answers.

All Access Subscription
MORE INFO

The Learn Enough All Access Subscription includes the entire Learn Enough introductory sequence and the full Ruby on Rails Tutorial. More than 2500 pages of book content and 53 hours of video that teach you to code from total beginner up to professional-grade web development.

Sign up for the course and get access to the full tutorial and streaming screencasts!

Ruby on Rails Tutorial Learn Web Development with Rails Michael Hartl

Newly updated for Rails 7, the Ruby on Rails Tutorial book and screencast series teach you how to develop and deploy real, industrial-strength web applications with Ruby on Rails, the open-source web framework that powers top websites such as GitHub, Hulu, Shopify, and Airbnb. The Ruby on Rails Tutorial book is available for purchase as an ebook (PDF, EPUB, and MOBI formats). The companion screencast series includes 14 individual lessons, one for each chapter of the Ruby on Rails Tutorial book.

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).

Box 1.1. The many advantages of Rails

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!

images/figures/hello_world_hello_app
Figure 1.1: The beginning hello app.
images/figures/micropost_length_error
Figure 1.2: An intermediate toy app.
images/figures/home_page_with_feed
Figure 1.3: The final sample app.
Adding the rest of the Learn Enough sequence would certainly provide excellent preparation for this tutorial, but if you’re in a hurry you can probably get by with just Command Line and Ruby. Learn Enough Ruby to Be Dangerous in particular has a chapter on building a simple web application using Sinatra, a Ruby-based micro-framework that serves as excellent preparation for Rails. If you get stuck in the present tutorial, I suggest giving Learn Enough Ruby to Be Dangerous and its prerequisites a try, then loop back here to see how it goes the second time.
Baby photo retrieved from https://www.flickr.com/photos/glasgows/338937124/ on 2014-08-25. Copyright © 2008 M&R Glasgow and used unaltered under the terms of the Creative Commons Attribution 2.0 Generic license.
Due to the constantly evolving nature of sites like AWS, details may vary; use your technical sophistication (Box 1.2) to resolve any discrepancies.
https://aws.amazon.com/
https://www.railstutorial.org/cloud9-signup
If you’ve previously done this tutorial, you may want to use a fresh environment, with a name like “rails-tutorial-7”.
This uses the 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.
This step is designed to unify the treatment of native systems and the cloud IDE by using identical directory structures. If you are confident in your technical sophistication, feel free to omit this step, and use a directory of your choice.
This is a typical example of technical sophistication (Box 1.2).
This notation is especially useful if the project in question uses semantic versioning (also called “semver”), which is a convention for numbering releases designed to minimize the chances of breaking software dependencies.
You can determine the exact version number for each gem by running gem list <gem name> at the command line, but Listing 1.8 saves you the trouble.
As noted in Table 3.1, you can even leave off install, as the bundle command by itself is an alias for bundle install.
Inside a Rails project directory, there’s no need to use a specific version number via 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.
Here “C” refers to the character on the keyboard, not the capital letter, so there’s no need to hold down the Shift key to get a capital “C”.
The base URL for the Rails Tutorial Cloud9 shared URLs has changed from rails-tutorial-c9-mhartl.c9.io to one on Amazon Web Services, but in many cases the screenshots are identical, so the browser address bar will show old-style URLs in some figures (such as Figure 1.20). This is the sort of minor discrepancy you can resolve using your technical sophistication (Box 1.2).
Your editor may display a message like “invalid multibyte character”, but this is not a cause for concern. You can Google the error message if you want to learn how to make it go away.
As of this writing, the article is at https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git, but Googling “caching your github credentials in git” is also a good bet.
In theory, you could use a longer timeout, but on the cloud IDE the timer seems to get reset every day or so, so entering a timeout of more than 86,400 seconds appears to have little effect in this case.
Many developers use the nearly equivalent 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.
Although we’ll never need to edit it in the main tutorial, an example of adding a rule to the .gitignore file appears in Section 3.6.2, which is part of the optional advanced testing setup in Section 3.6.
Bitbucket and GitLab are also excellent choices. Like GitHub, GitLab is written in Rails.
GitHub allows unlimited public and private repositories.
The -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.
The “password” used to be your actual password, but GitHub changed its interface in 2021, requiring an update to this tutorial. This is the kind of little change that happens all the time in tech, and it’s impossible for tutorials like this one to be up-to-date at all times; as is so often the case, technical sophistication (Box 1.2) is the only general answer for dealing with issues of this kind.
As of this writing, the article is at https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token, but Googling “github creating personal access token” is also a good bet.
For a convenient way to visualize Git repositories, take a look at Atlassian’s SourceTree app.
See, for example, the section on Collaborating in Learn Enough Git to Be Dangerous.
Though it shouldn’t matter for the example applications in the Rails Tutorial, if you’re worried about accidentally making your app public too soon there are several options; see Section 1.4.2 for one.
Pronounced “Engine X”.
This used to say “free tier”; see the next paragraph for details.
SQLite is widely used as an embedded database—for instance, it’s ubiquitous in mobile phones—and Rails uses it locally by default because it’s so easy to set up, but it isn’t designed for database-backed web applications. See Section 3.1 for more information.
toolbelt.heroku.com
If you’re working on your local machine instead of the cloud IDE, you can use heroku open to open the site automatically in a web browser.
Your results may differ if you completed the exercises in Section 1.2.4.1.
This solution, known as “security through obscurity”, is fine for hobby projects, but for sites that require greater initial security I recommend using Rails HTTP basic authentication. This is a much more advanced technique, though, and requires significantly more technical sophistication (Box 1.2) to implement. (Thanks to Alfie Pates for raising this issue.)
As is often the case, this code can be made even more compact using a built-in part of Ruby, in this case something called 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!
railstutorial.org/email
https://www.railstutorial.org/help
">

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

images/figures/cloud9_gemfile
Figure 1.12: The default Gemfile open in a text editor.
Listing 1.7: The default 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).

Listing 1.8: A 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).

Listing 1.9: Installing the gems from Listing 1.8.
$ 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.)

Box 1.4. Training wheels

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.

" data-sub-section-id="3755">

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.

Listing 1.15: Upgrading Git on the cloud IDE.
$ 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.

Listing 1.16: Configuring the name and email fields for Git.
$ 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.

Listing 1.17: Defining the default branch name.
$ 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.

Listing 1.18: Setting up 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.

Listing 1.19: Configuring Git to remember passwords for a set length of time.
$ 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.)

" data-sub-section-id="3759">

1.3.4 Branch, edit, commit, merge

If you’ve followed the steps in Section 1.3.3, you might notice that GitHub automatically rendered the repository’s README file, as shown in Figure 1.29. This file, called README.md, was generated automatically by the command in Listing 1.6. As indicated by the filename extension .md, it is written in Markdown,26 a human-readable markup language designed to be easy to convert to HTML—which is exactly what GitHub has done.

This automatic rendering of the README is convenient, but of course it would be better if we tailored the contents of the file to the project at hand. In this section, we’ll customize the README by adding some Rails Tutorial–specific content. In the process, we’ll see a first example of the branch, edit, commit, merge workflow that I recommend using with Git.27

images/figures/default_readme
Figure 1.29: GitHub’s rendering of the default Rails README.

Branch

Git is incredibly good at making branches, which are effectively copies of a repository where we can make (possibly experimental) changes without modifying the parent files. In most cases, the parent repository is the main branch, and we can create a new topic branch by using checkout with the -b flag:

$ git checkout -b modify-README
Switched to a new branch 'modify-README'
$ git branch
  main
* modify-README

Here the second command, git branch, just lists all the local branches, and the asterisk * identifies which branch we’re currently on. Note that git checkout -b modify-README both creates a new branch and switches to it, as indicated by the asterisk in front of the modify-README branch.

The full value of branching only becomes clear when working on a project with multiple developers,28 but branches are helpful even for a single-developer tutorial such as this one. In particular, because the main branch is insulated from any changes we make to the topic branch, even if we really mess things up we can always abandon the changes by checking out the main branch and deleting the topic branch. We’ll see how to do this at the end of the section.

By the way, for a change as small as this one I wouldn’t normally bother with a new branch (opting instead to work directly on the main branch), but in the present context it’s a prime opportunity to start practicing good habits.

Edit

After creating the topic branch, we’ll edit the README to add custom content, as shown in Listing 1.21 and Figure 1.30.

Listing 1.21: The new README file. README.md
# Ruby on Rails Tutorial

## "hello, world!"

This is the first application for the
[*Ruby on Rails Tutorial*](https://www.railstutorial.org/)
by [Michael Hartl](https://www.michaelhartl.com/). Hello, world!
images/figures/readme
Figure 1.30: Editing the README file.

Commit

With the changes made, we can take a look at the status of our branch:

$ git status
On branch modify-README
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

At this point, we could use git add -A as in Section 1.3.1.2, but git commit provides the -a flag as a shortcut for the (very common) case of committing all modifications to existing files:

$ git commit -a -m "Improve the README file"
[modify-README 34bb6a5] Improve the README file
 1 file changed, 5 insertions(+), 22 deletions(-)

Be careful about using the -a flag improperly; if you have added any new files to the project since the last commit, you still have to tell Git about them using git add -A first.

Note that we write the commit message in the present tense (and, technically speaking, the imperative mood). Git models commits as a series of patches, and in this context it makes sense to describe what each commit does, rather than what it did. Moreover, this usage matches up with the commit messages generated by Git commands themselves. See Committing to Git from Learn Enough Git to Be Dangerous for more information.

Merge

Now that we’ve finished making our changes, we’re ready to merge the results back into our main branch:

$ git checkout main
Switched to branch 'main'
$ git merge modify-README
Updating b981e57..015008c
Fast-forward
 README.md | 27 +++++----------------------
 1 file changed, 5 insertions(+), 22 deletions(-)

Note that the Git output frequently includes things like 34f06b7, which are related to Git’s internal representation of repositories. Your exact results will differ in these details, but otherwise should essentially match the output shown above.

After you’ve merged in the changes, you can tidy up your branches by deleting the topic branch using git branch -d if you’re done with it:

$ git branch -d modify-README
Deleted branch modify-README (was 015008c).

This step is optional, and in fact it’s quite common to leave the topic branch intact. This way you can switch back and forth between the topic and main branches, merging in changes every time you reach a natural stopping point.

As mentioned above, it’s also possible to abandon your topic branch changes, in this case with git branch -D:

# For illustration only; don't do this unless you mess up a branch
$ git checkout -b topic-branch
$ <really mess up the branch>
$ git add -A
$ git commit -a -m "Make major mistake"
$ git checkout main
$ git branch -D topic-branch

Unlike the -d flag, the -D flag will delete the branch even though we haven’t merged in the changes.

Push

Now that we’ve updated the README, we can push the changes up to GitHub to see the result. Since we have already done one push (Section 1.3.3), on most systems we can omit origin main, and simply run git push:

$ git push

As with the default README, GitHub nicely converts the Markdown in our updated README to HTML (Figure 1.31).

images/figures/new_readme
Figure 1.31: The improved README file at GitHub.
" data-sub-section-id="3762">

1.4.1 Heroku setup and deployment

Heroku uses the PostgreSQL database (pronounced “post-gres-cue-ell”, and often called “Postgres” for short), which means that we need to add the pg gem in the production environment to allow Rails to talk to Postgres:

group :production do
  gem "pg", "1.3.5"
end

Also be sure to incorporate the changes made in Listing 1.8 preventing the sqlite3 gem from being included in a production environment, since the SQLite database isn’t supported at Heroku:32

group :development, :test do
  gem "sqlite3", "1.4.2"
  gem "debug",   "1.5.0", platforms: %i[ mri mingw x64_mingw ]
end

The resulting Gemfile appears as in Listing 1.22.

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).

Listing 1.22: A Gemfile with added and rearranged gems.
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby "3.1.2"

gem "rails",           "7.0.4"
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

group :production do
  gem "pg", "1.3.5"
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]

To prepare the system for deployment to production, we first run a special bundle config command to prevent the local installation of any production gems (which in this case consists of the pg gem), as shown in Listing 1.23. We’ve also included a line to add the Linux deployment platform, which is required on some systems.

Listing 1.23: Bundling without production gems.
$ bundle _2.3.14_ config set --local without 'production'
$ bundle _2.3.14_ install
$ bundle _2.3.14_ lock --add-platform x86_64-linux

Because the only gem added in Listing 1.22 is restricted to a production environment, right now the command in Listing 1.23 doesn’t actually install any additional local gems, but it’s needed to update Gemfile.lock with the pg gem. We can then commit the changes as follows:

$ git commit -a -m "Update Gemfile for Heroku"

Next we have to create and configure a new Heroku account. The first step is to sign up for Heroku. As part of this, you should set up Multi-Factor Authentication on your account.

The next step is to check to see if your system already has the Heroku command-line client installed:

$ heroku --version    # will work only if heroku is installed
heroku: command not found

This will display the current version number if the heroku command-line interface (CLI) is available, but on most systems it will be necessary to install the Heroku CLI by hand.33 In particular, if you’re working on the cloud IDE, you can install Heroku using the command shown in Listing 1.24. Note: You may need to run the resize command in Listing 1.4 first to add extra space to your cloud IDE environment. (Other remedies include running the commands rake log:clear and rake tmp:clear to clear the log and temp files on your system.)

Listing 1.24: The command to install Heroku on the cloud IDE.
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)

After running the command in Listing 1.24, you should now be able to verify the installation by displaying the current version number (details may vary):

$ heroku --version
heroku/7.59.2 linux-x64 node-v12.21.0

Once you’ve verified that the Heroku command-line interface is installed, the next step is to use the heroku command to log in to your account. If you’re using a native development environment, simply type heroku at the command line, which will automatically spawn a browser and let you log in with your Heroku email and password:

$ heroku login        # on a native system but not on the cloud IDE
$ # Spawns a browser window. Log in with your email and Heroku password.

If you’re using the cloud IDE, you need to pass the --interactive option, which prevents the heroku command from trying to spawn a browser (which wouldn’t work in the cloud). You also won’t be able to log in using your regular Heroku password; instead, you’ll have to create an API Key using the interface on your Heroku Account page (Figure 1.32). Once you’ve followed that step (and saved the result somewhere safe), you can log in using your email and the Account Key as your password:

$ heroku login --interactive    # on the cloud IDE
Email: <your email>
Password: <your API Key, NOT your Heroku password>
images/figures/heroku_api_key
Figure 1.32: The API key at Heroku.

After you’ve logged in, you can use the heroku create command to create a place on the Heroku servers for the sample app to live (Listing 1.25).

Listing 1.25: Creating and configuring a new application at Heroku.
$ heroku create
Creating app... done, ⬢ thawing-refuge-35095
https://thawing-refuge-35095.herokuapp.com/ |
https://git.heroku.com/thawing-refuge-35095.git

The heroku command creates a new subdomain just for our application, available for immediate viewing. There’s nothing there yet, though, so let’s get busy deploying.

Heroku deployment, step 1

The first step is to use Git to push the main branch up to Heroku:

$ git push heroku main

You may see some warning messages at this point, which you should ignore for now; we’ll discuss them further in Section 7.5.

If anything goes wrong with the deployment, I suggest inspecting the Heroku logs to see if you can figure out the problem (Box 1.2):

$ heroku logs

We’ll discuss this useful command further starting in Section 2.3.5.

Heroku deployment, step 2

There is no step 2! We’re already done. To see your newly deployed application, visit the address that you saw when you ran heroku create (i.e., Listing 1.25).34 In case you ever need to get the web URL again, you can run the heroku apps:info command, as shown in Listing 1.26.

Listing 1.26: Getting info about the Heroku app, including the web URL.
$ heroku apps:info
=== mysterious-atoll-47182
.
.
.
Web URL:        https://mysterious-atoll-47182.herokuapp.com/

The result of visiting the URL from Listing 1.26 appears in Figure 1.33. The page is identical to Figure 1.20, but now it’s running in a production environment on the live Web.35 (To learn how to host a Heroku site using a custom domain instead of a herokuapp.com subdomain, see the free tutorial Learn Enough Custom Domains to Be Dangerous.)

images/figures/heroku_app_hello_world
Figure 1.33: The first Rails Tutorial application running on Heroku.

Exercises

To see other people’s answers and to record your own, subscribe to the Rails Tutorial course or to the Learn Enough All Access Subscription.

  1. By making the same change as in Section 1.2.4.1, arrange for your production app to display “hola, mundo!”.
  2. As in Section 1.2.4.1, arrange for the root route to display the result of the goodbye action. When deploying, confirm that you can omit main in the Git push, as in git push heroku.
" data-sub-section-id="3763">

1.4.2 Heroku commands

There are many Heroku commands (including the one in Listing 1.26 at the end of Section 1.4.1), and we’ll barely scratch the surface in this book. Let’s take a minute to show just one of them by renaming the application as follows:

$ heroku rename rails-tutorial-hello

Don’t use this name yourself; it’s already taken by me! In fact, you probably shouldn’t bother with this step right now; using the default address supplied by Heroku is fine. But if you do want to rename your application, you can arrange for it to be reasonably secure by using a random or obscure subdomain, such as the following:

hwpcbmze.herokuapp.com
seyjhflo.herokuapp.com
jhyicevg.herokuapp.com

With a hard-to-guess subdomain like this, someone would be likely to visit your site only if you gave them the address.36 (By the way, as a preview of Ruby’s compact awesomeness, here’s the code I used to generate the subdomains above:

('a'..'z').to_a.shuffle[0..7].join

We’ll return to this bit of code in Chapter 4.)37

In addition to supporting subdomains, Heroku also supports custom domains. (In fact, the Ruby on Rails Tutorial site lives at Heroku; if you’re reading this book at railstutorial.org or learnenough.com, you’re looking at a Heroku-hosted site right now!) See the Heroku documentation for more information about custom domains and other Heroku topics.

Exercises

To see other people’s answers and to record your own, subscribe to the Rails Tutorial course or to the Learn Enough All Access Subscription.

  1. Run heroku help to see a list of Heroku commands. (If the output of heroku help doesn’t fit in your terminal window, either scroll up or use heroku help | less to pipe to the less command.) What is the command to display logs for an app?
  2. Use the command identified in the previous exercise to inspect the activity on your application. What was the most recent event? (This command is often useful when debugging production apps.)
" data-sub-section-id="3764">
CONTINUE READING

Join the Mailing List

Get occasional notifications about things like product discounts, blog posts, and new or updated tutorials. Unsubscribe at any time.