Ruby on Rails Tutorial
Learn Web Development with Rails
Michael Hartl
Contents
- Foreword
- Acknowledgments
- About the author
- Copyright and license
- Chapter 1 From zero to deploy
- Chapter 2 A toy app
- Chapter 3 Mostly static pages
- Chapter 4 Rails-flavored Ruby
- Chapter 5 Filling in the layout
- Chapter 6 Modeling users
- Chapter 7 Sign up
- Chapter 8 Basic login
- Chapter 9 Advanced login
- Chapter 10 Updating, showing, and deleting users
- Chapter 11 Account activation
- Chapter 12 Password reset
- Chapter 13 User microposts
- Chapter 14 Following users
Foreword
My former company (CD Baby) was one of the first to loudly switch to Ruby on Rails, and then even more loudly switch back to PHP (Google me to read about the drama). This book by Michael Hartl came so highly recommended that I had to try it, and the Ruby on Rails Tutorial is what I used to switch back to Rails again.
Though I’ve worked my way through many Rails books, this is the one that finally made me “get” it. Everything is done very much “the Rails way”—a way that felt very unnatural to me before, but now after doing this book finally feels natural. This is also the only Rails book that does test-driven development the entire time, an approach highly recommended by the experts but which has never been so clearly demonstrated before. Finally, by including Git, GitHub, and Heroku in the demo examples, the author really gives you a feel for what it’s like to do a real-world project. The tutorial’s code examples are not in isolation.
The linear narrative is such a great format. Personally, I powered through the Rails Tutorial in three long days doing all the examples and challenges at the end of each chapter. [This is not typical! Most readers take much longer to finish the tutorial. —Michael] Do it from start to finish, without jumping around, and you’ll get the ultimate benefit.
Enjoy!
Derek Sivers (sivers.org) Founder, CD Baby
Acknowledgments
The Ruby on Rails Tutorial owes a lot to my previous Rails book, RailsSpace, and hence to my coauthor Aurelius Prochazka. I’d like to thank Aure both for the work he did on that book and for his support of this one. I’d also like to thank Debra Williams Cauley, my editor on both RailsSpace and the Ruby on Rails Tutorial.
I’d like to acknowledge a long list of Rubyists who have taught and inspired me over the years: David Heinemeier Hansson, Yehuda Katz, Carl Lerche, Jeremy Kemper, Xavier Noria, Ryan Bates, Geoffrey Grosenbach, Peter Cooper, Matt Aimonetti, Mark Bates, Gregg Pollack, Wayne E. Seguin, Amy Hoy, Dave Chelimsky, Pat Maddox, Tom Preston-Werner, Chris Wanstrath, Chad Fowler, Josh Susser, Obie Fernandez, Ian McFarland, Steph Bristol, Pratik Naik, Sarah Mei, Sarah Allen, Wolfram Arnold, Alex Chaffee, Giles Bowkett, Evan Dorn, Long Nguyen, James Lindenbaum, Adam Wiggins, Tikhon Bernstam, Ron Evans, Wyatt Greene, Miles Forrest, Sandi Metz, Ryan Davis, Aaron Patterson, Aja Hammerly, Richard “Schneems” Schneeman, the good people at Pivotal Labs, the Heroku gang, the thoughtbot folks, and the GitHub crew.
I’d like to thank technical reviewer Andrew Thai for his careful reading of the original manuscript and for his helpful suggestions. I’d also like to thank my cofounders at Learn Enough, Nick Merwin and Lee Donahoe, for all their help in preparing this tutorial.
Finally, many, many readers—far too many to list—have contributed a huge number of bug reports and suggestions during the writing of this book, and I gratefully acknowledge their help in making it as good as it can be.
About the author
Michael Hartl is the creator of the Ruby on Rails Tutorial, one of the leading introductions to web development, and is cofounder and principal author at Learn Enough. Previously, he was a physics instructor at the California Institute of Technology (Caltech), where he received a Lifetime Achievement Award for Excellence in Teaching. He is a graduate of Harvard College, has a Ph.D. in Physics from Caltech, and is an alumnus of the Y Combinator entrepreneur program.
Copyright and license
Ruby on Rails Tutorial: Learn Web Development with Rails. Copyright © 2016–2022 Michael Hartl. Last updated 2023/08/10 09:53:28 PT.
All source code in the Ruby on Rails Tutorial is available jointly under the MIT License and the Beerware License. Note that this means that either license is valid; if you wish to use the software under the terms of the MIT License, there is no need to buy me a beer as well.
The MIT License
Copyright (c) 2020 Michael Hartl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
THE BEERWARE LICENSE (Revision 42)
Michael Hartl wrote this code. As long as you retain this notice you can do
whatever you want with this stuff. If we meet some day, and you think this
stuff is worth it, you can buy me a beer in return.
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!
1.1 Up and running
One advantage of using this tutorial is that you can get up and running fast. In particular, the Rails Tutorial has a long-running partnership with AWS Cloud9, a development environment that runs in your browser. The result is a complete system for developing all the software in this tutorial.
This is important because, even for experienced developers, installing Ruby, Rails, and all the associated supporting software can be quite challenging. Compounding the problem is the multiplicity of environments: different operating systems, version numbers, preferences in text editors, etc.
This is why the recommended solution, especially for newer users, is to sidestep most installation and configuration issues by using a cloud integrated development environment, or cloud IDE (Section 1.1.1). The cloud IDE used in this tutorial runs inside an ordinary web browser, and hence works the same across different platforms. It also maintains the current state of your work, so you can take a break from the tutorial and come back to the system just as you left it.
A second possibility is to set up your native system (Windows, macOS, or Linux) for Rails development. It is definitely recommended that you do this eventually, but it can represent significant overhead, and is likely to require a healthy amount of technical sophistication (Box 1.2). Instructions for setting up your native system can be found in the “Native OS setup” section of Learn Enough Dev Environment to Be Dangerous. (The current reference app standardizes on Ruby 3.1.2.) If you go this route, be sure to complete the configuration and Rails installation steps in Section 1.1.2 as well.
The Ruby on Rails Tutorial is part of the Learn Enough family of tutorials, which develop the theme of technical sophistication: the combination of hard and soft skills that make it seem like you can magically solve any technical problem (as illustrated in “Tech Support Cheat Sheet” from xkcd).
Knowing how to code is an important component of technical sophistication, but there’s more to it than that—you also have to know how to click around menu items to learn the capabilities of a particular application, how to clarify a confusing error message by googling it, or when to give up and just reboot the darn thing.
Because web applications have so many moving parts, they offer ample opportunities to develop your technical sophistication. In the context of Rails web development, some specific examples of technical sophistication include making sure you’re using the right Ruby gem versions, running bundle install
or bundle update
, and restarting the local web server if something doesn’t work. (Don’t worry if all this sounds like gibberish; we’ll cover everything mentioned here in the course of completing this tutorial.)
As you proceed through this tutorial, in all likelihood you will occasionally be tripped up by things not immediately working as expected. Although some particularly tricky steps are explicitly highlighted in the text, it is impossible to anticipate all the things that can go wrong. I recommend you embrace these inevitable stumbling blocks as opportunities to work on improving your technical sophistication. Or, as we say in geek speak: It’s not a bug, it’s a feature!
1.1.1 Development environment
Considering various idiosyncratic customizations, there are probably as many development environments as there are Rails programmers. To avoid this complexity, the Ruby on Rails Tutorial standardizes on the excellent cloud development environment Cloud9, part of Amazon Web Services (AWS). The resulting workspace environment comes preconfigured with most of the software needed for Ruby on Rails web development, including Ruby, RubyGems, and Git. (Indeed, the only big piece of software we’ll install separately is Rails itself, and this is intentional (Section 1.1.2).)
The cloud IDE includes the three essential components needed to develop web applications: a command-line terminal, a filesystem navigator, and a text editor (Figure 1.4). Among other features, the cloud IDE’s text editor supports the Find in Files global search that I consider essential to navigating any large Ruby or Rails project. Finally, even if you decide not to use the cloud IDE exclusively in real life (and I certainly recommend learning other tools as well), it provides an excellent introduction to the general capabilities of command-line terminals, text editors, and other development tools.
Here are the steps for getting started with the cloud development environment:3
- Because Cloud9 is part of Amazon Web Services (AWS), if you already have an AWS account you can just sign in.4 To create a new Cloud9 workspace environment, go to the AWS console and type “Cloud9” in the search box.
- If you don’t already have an AWS account, you should sign up for a free account at AWS Cloud9.5 In order to prevent abuse, AWS requires a valid credit card for signup, but the workspace is 100% free (for a year as of this writing), and your card will not be charged. You might have to wait up to 24 hours for the account to be activated, but in my case it was ready in about ten minutes.
- Once you’ve successfully gotten to the Cloud9 administrative page (Figure 1.5), click on “Create environment” and fill in the information as shown in Figure 1.6, including the name “rails-tutorial”.6 Then fill in the description as shown in Figure 1.6. On the next page, choose Ubuntu Server (not Amazon Linux) (Figure 1.7), and then click “Next step”. Keep clicking the confirmation buttons to accept the default settings (Figure 1.8) until AWS starts provisioning the IDE (Figure 1.9). You may run into a warning message about being a “root” user, which you can safely ignore at this early stage. (We’ll discuss the preferred but more complicated practice, called an Identity and Access Management (IAM) user, in Section 13.4.4.)
Because using two spaces for indentation is a near-universal convention in Ruby, I also recommend changing the editor to use two spaces instead of the default four. As shown in Figure 1.10, you can do this by clicking the gear icon in the upper right and then clicking the minus sign in the “Soft Tabs” setting until it reaches 2. I also recommend activating the “On Save, Strip Whitespace” option to avoid annoying whitespace discrepancies that can cause conflicts when collaborating with other users. (Note that these changes take effect immediately; you don’t need to click a “Save” button.)
Finally, this tutorial standardizes on Ruby 3.1.2, which you can install using Ruby Version Manager (rvm
):
$ rvm get stable
$ rvm install 3.1.2
$ rvm --default use 3.1.2
(As noted in Section 1.6, the dollar sign represents the Unix prompt and should not be typed literally.) Once the installation is finished, you can verify the Ruby version as follows (exact version numbers may differ):
$ ruby -v
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
1.1.2 Installing Rails
The development environment from Section 1.1.1 includes all the software we need to get started except for Rails itself. This is by design, as installing the exact version of Rails used in this tutorial is important for getting predictable results.
First, we’ll do a little preparation by adding configuration settings to prevent the time-consuming installation of local Ruby documentation, as shown in Listing 1.1.7 Note that this step needs to be done only once per system. (For more information on the conventions in this book, see Section 1.6.)
.gemrc
file to skip the installation of Ruby documentation.
$ echo "gem: --no-document" >> ~/.gemrc
To install Rails, we’ll use the gem
command provided by the RubyGems package manager, which involves typing the command shown in Listing 1.2 into your command-line terminal. (If developing on your local system, this means using a regular terminal window; if using the cloud IDE, this means using the command-line area shown in Figure 1.4.)
$ gem install rails -v 7.0.4
Here the -v
flag ensures that the specified version of Rails gets installed. You can confirm that the installation succeeded by passing the -v
flag to the rails
command itself:
$ rails -v
Rails 7.0.4
The version number output by this command should match the version installed in Listing 1.2.
Experience shows that using a standard version of the bundler
gem is also a good idea, as shown in Listing 1.3.
$ gem install bundler -v 2.3.14
We’ll learn more about this important gem in Section 1.2.1.
Finally, some users of the cloud IDE have reported having problems running into size limits, so I recommend running the command in Listing 1.4 if you’re using the cloud development environment. (If you can’t get Listing 1.4 to work, try creating a fresh environment while taking care to choose Ubuntu Server as in Figure 1.7. If it still doesn’t work, you should still try proceeding with the tutorial since resizing the environment might not be strictly necessary.)
$ source <(curl -sL https://cdn.learnenough.com/resize)
That’s it! You’ve now got a system fully configured for Ruby on Rails web development.
1.2 The first application
Following a long tradition in computer programming, our goal for the first application is to write a “hello, world” program. In particular, we will create a simple application that displays the string “hello, world!” on a web page, both on our development environment (Section 1.2.4) and on the live Web (Section 1.4).
Virtually all Rails applications start the same way, by running the rails new
command. This handy command creates a skeleton Rails application in a directory of your choice. To get started, those of you who are not using the Cloud9 IDE recommended in Section 1.1.1 should make an environment
directory for your Rails projects if it doesn’t already exist (Listing 1.5) and then change into the directory.8
environment
directory for Rails projects.
# These steps are not needed on the cloud IDE.
$ cd # Change to the home directory.
$ mkdir environment # Make an environment directory.
$ cd environment/ # Change into the environment directory.
Listing 1.5 uses the Unix commands cd
and mkdir
; see Box 1.3 if you are not already familiar with these commands.
For readers coming from Windows or macOS, the Unix command line may be unfamiliar. Luckily, if you are using the recommended cloud environment, you automatically have access to a Unix (Linux) command line running a standard shell (command-line interface) known as Bash.
The basic idea of the command line is simple: by issuing short commands, users can perform a large number of operations, such as creating directories (mkdir
), moving and copying files (mv
and cp
), and navigating the filesystem by changing directories (cd
). Although the command line may seem primitive to users mainly familiar with graphical user interfaces (GUIs), appearances are deceiving: the command line is one of the most powerful tools in the developer’s toolbox. Indeed, you will rarely see the desktop of an experienced developer without several open terminal windows running command-line shells.
The general subject is deep, but for the purposes of this tutorial we will need only a few of the most common Unix command-line commands, as summarized in Table 1.1. For a more thorough introduction to the basics of the command line, see the first Learn Enough tutorial, Learn Enough Command Line to Be Dangerous.
Description | Command | Example |
list contents | ls |
$ ls -l |
make directory | mkdir <dirname> |
$ mkdir environment |
change directory | cd <dirname> |
$ cd environment/ |
cd one directory up |
$ cd ..
|
|
cd to home directory |
$ cd ~ or just $ cd
|
|
cd to path incl. home dir | $ cd ~/environment/ |
|
move file (rename) | mv <source> <target> |
$ mv foo bar |
copy file | cp <source> <target> |
$ cp foo bar |
remove file | rm <file> |
$ rm foo |
remove empty directory | rmdir <directory> |
$ rmdir environment/ |
remove nonempty directory | rm -rf <directory> |
$ rm -rf tmp/ |
concatenate & display file contents | cat <file> |
$ cat ~/.ssh/id_rsa.pub |
The next step on both local systems and the cloud IDE is to create the first application using the command in Listing 1.6. Note that Listing 1.6 explicitly includes the Rails version number as part of the command, which ensures that the same version of Rails we installed in Listing 1.2 is used to create the first application’s file structure. Listing 1.6 also skips the default bundle
command to ensure compatibility with the version installed in Listing 1.3. (We’ll run bundle
by hand in Section 1.2.1.)
rails new
(with a specific version number).
$ cd ~/environment
$ rails _7.0.4_ new hello_app --skip-bundle
create
create README.md
create Rakefile
create .ruby-version
create config.ru
create .gitignore
create Gemfile
run git init from "."
Initialized empty Git repository in /home/ubuntu/environment/hello_app/.git/
create package.json
create app
create app/assets/config/manifest.js
create app/assets/stylesheets/application.css
create app/channels/application_cable/channel.rb
create app/channels/application_cable/connection.rb
create app/controllers/application_controller.rb
create app/helpers/application_helper.rb
.
.
.
Notice how many files and directories the rails
command creates. This standard directory and file structure (Figure 1.11) is one of the many advantages of Rails: it immediately gets you from zero to a functional (if minimal) application. Moreover, since the structure is common to all Rails apps, you can immediately get your bearings when looking at someone else’s code.
A summary of the default Rails files appears in Table 1.2. We’ll learn about most of these files and directories throughout the rest of this book. In particular, starting in Section 5.2.1 we’ll discuss the app/assets
directory, part of the asset pipeline that makes it easy to organize and deploy assets such as Cascading Style Sheets and image files.
File/Directory | Purpose |
app/ |
core application (app) code, including models, views, controllers, and helpers |
app/assets |
application assets such as Cascading Style Sheets (CSS) and images |
bin/ |
binary executable files |
config/ |
application configuration |
db/ |
database files |
doc/ |
documentation for the application |
lib/ |
library modules |
log/ |
application log files |
public/ |
data accessible to the public (e.g., via web browsers), such as error pages |
bin/rails |
a program for generating code, opening console sessions, or starting a local server |
test/ |
application tests |
tmp/ |
temporary files |
README.md |
a brief description of the application |
Gemfile |
gem requirements for this app |
Gemfile.lock |
a list of gems used to ensure that all copies of the app use the same gem versions |
config.ru |
a configuration file for Rack middleware |
.gitignore |
patterns for files that should be ignored by Git |
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.2.2 rails server
Thanks to running rails new
in Section 1.2 and bundle install
in Section 1.2.1, we already have an application we can run—but how? Happily, Rails comes with a command-line program, or script, that runs a local web server to assist us in developing our application: rails server
.
Before running rails server
, it’s necessary on some systems (including the cloud IDE) to allow connections to the local web server. To enable this, you should navigate to the file config/environments/development.rb
and paste in the two extra lines shown in Listing 1.10 and Figure 1.13 and then save the change in your editor.
config/environments/development.rb
Rails.application.configure do
.
.
.
# Allow connections to local server on cloud IDE.
config.hosts.clear
end
The rails server
command appears in Listing 1.11, which I recommend you run in a second terminal tab so that you can still issue commands in the first tab, as shown in Figure 1.14 and Figure 1.15.13 Note from Listing 1.11 that you can shut the server down using Ctrl-C.14
$ cd ~/environment/hello_app/
$ rails server
=> Booting Puma
=> Ctrl-C to shutdown server
To view the result of rails server
on a native OS, paste the URL http://localhost:3000 into the address bar of your browser. On the cloud IDE, go to Preview and click on Preview Running Application (Figure 1.16), and then open the running app in a full browser window or tab (Figure 1.17). In either case, the result should look something like Figure 1.18.
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.
The Ruby on Rails Tutorial contains a large number of exercises. Solving them as you proceed through the tutorial is strongly recommended.
In order to keep the main discussion independent of the exercises, the solutions are not generally incorporated into subsequent code listings. (In the rare circumstance that an exercise solution is used subsequently, it is explicitly solved in the main text.) This means that over time your code may diverge from the code shown in the tutorial due to differences introduced in the exercises. Learning how to resolve such discrepancies is a valuable exercise in technical sophistication (Box 1.2).
Many of the exercises are challenging, but we’ll start out with some easy ones just to get warmed up:
- According to the default Rails page, what is the version of Ruby on your system? Confirm by running
ruby -v
at the command line. - What is the version of Rails? Confirm that it matches the version installed in Listing 1.2.
1.2.3 Model-View-Controller (MVC)
Even at this early stage, it’s helpful to get a high-level overview of how Rails applications work, as illustrated in Figure 1.19. You might have noticed that the standard Rails application structure (Figure 1.11) has an application directory called app/
, which includes subdirectories called models
, views
, and controllers
(among others). This is a hint that Rails follows the model-view-controller (MVC) architectural pattern, which enforces a separation between the data in the application (such as user information) and the code used to display it, which is a common way of structuring a graphical user interface (GUI).
When interacting with a Rails application, a browser sends a request, which is received by a web server and passed on to a Rails controller, which is in charge of what to do next. In some cases, the controller will immediately render a view, which is a template that gets converted to HTML and sent back to the browser. More commonly for dynamic sites, the controller interacts with a model, which is a Ruby object that represents an element of the site (such as a user) and is in charge of communicating with the database. After invoking the model, the controller then renders the view and returns the complete web page to the browser as HTML.
If this discussion seems a bit abstract right now, don’t worry; we’ll cover these ideas in more detail later in this book. In particular, Section 1.2.4 shows a first tentative application of MVC, while Section 2.2.2 includes a more detailed discussion of MVC in the context of the toy app. Finally, the full sample app will use all aspects of MVC: we’ll cover controllers and views starting in Section 3.2, cover models starting in Section 6.1, and see all three working together in Section 7.1.2.
1.2.4 Hello, world!
As a first application of the MVC framework, we’ll make a wafer-thin change to the first app by adding a controller action to render the string “hello, world!” to replace the default Rails page from Figure 1.18. (We’ll learn more about controller actions starting in Section 2.2.2.)
As implied by their name, controller actions are defined inside controllers. We’ll call our action hello
and place it in the Application controller. Indeed, at this point the Application controller is the only controller we have, which you can verify by running
$ ls app/controllers/*_controller.rb
to view the current controllers. (We’ll start creating our own controllers in Chapter 2.) Listing 1.12 shows the resulting definition of hello
, which uses the render
function to return the HTML text “hello, world!”. (Don’t worry about the Ruby syntax right now; it will be covered in more depth in Chapter 4.)
hello
action to the Application controller. app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def hello
render html: "hello, world!"
end
end
Having defined an action that returns the desired string, we need to tell Rails to use that action instead of the default page in Figure 1.18. To do this, we’ll edit the Rails router, which sits in front of the controller in Figure 1.19 and determines where to send requests that come in from the browser. (I’ve omitted the router from Figure 1.19 for simplicity, but we’ll discuss it in more detail starting in Section 2.2.2.) In particular, we want to change the default page, the root route, which determines the page that is served on the root URL. Because it’s the URL for an address like http://www.example.com/ (where nothing comes after the final forward slash), the root URL is often referred to as / (“slash”) for short.
As seen in Listing 1.13, the Rails routes file (config/routes.rb
) includes a comment directing us to the Rails Guide on Routing and includes an example of how to define the root route. The syntax looks like this:
root "controller_name#action_name"
In the present case, the controller name is application
and the action name is hello
, which results in the code shown in Listing 1.14.
config/routes.rb
Rails.application.routes.draw do
# Define your application routes per the DSL in
# https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
# root "articles#index"
end
config/routes.rb
Rails.application.routes.draw do
root "application#hello"
end
With the code from Listing 1.12 and Listing 1.14, the root route returns “hello, world!” as required (Figure 1.20).15 Hello, world!
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.
- Change the content of the
hello
action in Listing 1.12 to read “hola, mundo!” instead of “hello, world!”. - Show that Rails supports non-ASCII characters by including an inverted exclamation point, as in “¡Hola, mundo!” (Figure 1.21).16 To get a ¡ character on a Mac, you can use Option-1; otherwise, you can always copy-and-paste the character into your editor.
- By following the example of the
hello
action in Listing 1.12, add a second action calledgoodbye
that renders the text “goodbye, world!”. Edit the routes file from Listing 1.14 so that the root route goes togoodbye
instead of tohello
(Figure 1.22).
1.3 Version control with Git
Now that we have a working “hello, world” application, we’ll take a moment for a step that, while technically optional, would be viewed by many experienced software developers as practically essential: placing our application source code under version control. Version control systems allow us to track changes to our project’s code, collaborate more easily, and roll back any inadvertent errors (such as accidentally deleting files). Knowing how to use a version control system is a required skill for every professional-grade software developer.
There are many options for version control, but the software development community has largely standardized on Git, a distributed version control system originally developed by Linus Torvalds to host the Linux kernel. Git is a large subject, and we’ll only be scratching the surface in this book; for a more thorough introduction to the basics, see Learn Enough Git to Be Dangerous.
Putting your source code under version control with Git is strongly recommended, not only because it’s nearly a universal practice in the Rails world, but also because it will allow you to back up and share your code more easily (Section 1.3.3) and deploy your application right here in the first chapter (Section 1.4).
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.)
1.3.2 What good does Git do you?
If you’ve never used version control before, it may not be entirely clear at this point what good it does you, so let’s look at just one example. Suppose you’ve made some accidental changes, such as (D’oh!) deleting the critical app/controllers/
directory:
$ ls app/controllers/
application_controller.rb concerns/
$ rm -rf app/controllers/
$ ls app/controllers/
ls: app/controllers/: No such file or directory
Here we’re using the Unix ls
command to list the contents of the app/controllers/
directory and the rm
command to remove it (Table 1.1). As noted in Learn Enough Command Line to Be Dangerous, the -rf
flag means “recursive force”, which recursively removes all files, directories, subdirectories, and so on, without asking for explicit confirmation of each deletion.
Let’s check the status to see what changed:
$ git status
On branch main
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: app/controllers/application_controller.rb
deleted: app/controllers/concerns/.keep
no changes added to commit (use "git add" and/or "git commit -a")
We see here that a file has been deleted, but the changes are only on the “working tree”; they haven’t been committed yet. This means we can still undo the changes using the checkout
command with the -f
flag to force overwriting the current changes:
$ git checkout -f
$ git status
On branch main
nothing to commit, working tree clean
$ ls app/controllers/
application_controller.rb concerns/
The missing files and directories are back. That’s a relief!
1.3.3 GitHub
Now that we’ve put our project under version control with Git, it’s time to push our code up to GitHub, a site optimized for hosting and sharing Git repositories.21 Putting a copy of your Git repository at GitHub serves two purposes: it’s a full backup of your code (including the full history of commits), and it makes any future collaboration much easier.
Getting started with GitHub is straightforward: just sign up for a GitHub account if you don’t already have one (Figure 1.23). It is also recommended that you configure your account for two-factor authentication (2FA). (See especially “Caching your GitHub credentials in Git”.)
Once you’ve signed up or signed in, click on the + sign dropdown menu and select “New repository” (Figure 1.24).
On the new repository page, fill the fields with the repository name (hello_app
) and optional description, and take special care to select the “Private” option, as shown in Figure 1.25. Although Rails apps are in principle safe to expose as public repositories, so many things can go wrong (such as accidentally exposing passwords or private keys) that making all such repositories private is a prudent default.22
After clicking the green “Create repository” button seen in Figure 1.25, you should see a page like Figure 1.26 containing instructions for how to push your local repository up to GitHub. To get the right commands, be sure to select the setup option for HTTPS rather than SSH.
The exact commands in Figure 1.26 will be tailored to your personal account name and default branch name, with a template that looks like Listing 1.20. The first command sets up GitHub as the remote origin, the second ensures that the default branch name is main
(which in our case does nothing since that’s already its name), and the third arranges to push the full repository to GitHub.23 Don’t worry about these details, though; you will almost always copy such commands from GitHub and probably won’t ever have to figure them out on your own.
[website (main)]$ git remote add origin https://github.com/<name>/hello_app.git
[website (main)]$ git branch -M main
[website (main)]$ git push -u origin main
In Listing 1.20, you should replace <name>
with your actual username. For example, the commands for my username, which is mhartl
, look like this:
[website (main)]$ git remote add origin https://github.com/mhartl/hello_app.git
[website (main)]$ git branch -M main
[website (main)]$ git push -u origin main
After running the third command in Listing 1.20, you will be prompted to enter your username and password. The username is simply your GitHub username, but the password is not your GitHub password; instead, the “password” must be a personal access token,24 which you can create by following the instructions in the GitHub article “Creating a personal access token”.25 I suggest selecting “No expiration” for the token expiration, and also be sure to select “repo” as the scope of the token so that it works at the command line. Once you’ve created and saved the personal access token, paste it in at the command line when prompted for a password to complete the git push
in Listing 1.20. (Note that, for security purposes, the command line will likely not give you any feedback after pasting in the token, but as long as you paste it in properly it should allow you to log in.)
After executing the first git push
as shown in Listing 1.20, you should reload the current page (using, e.g., ⌘R or the icon shown in Figure 1.27). The result is a page at GitHub for the hello_app repository, with file browsing, full commit history, and lots of other features (Figure 1.28).
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
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.
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!
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).
1.4 Deploying
Even though this is only the first chapter, we’re already going to deploy our Rails application to production! As with the version control setup in Section 1.3, this step is technically optional, but deploying early and often allows us to catch any deployment problems early in our development cycle. The alternative—deploying only after laborious effort sealed away in a development environment—often leads to terrible integration headaches when launch time comes.29
Deploying Rails applications used to be a pain, but the Rails deployment ecosystem has matured rapidly in the past few years, and now there are several great options. These include shared hosts or virtual private servers running Phusion Passenger (a module for the Apache and Nginx30 web servers), full-service deployment companies such as Engine Yard and Rails Machine, and cloud deployment services such as Engine Yard Cloud and Heroku.
The Rails deployment option used in this tutorial is Heroku, which is a hosted platform built specifically for deploying Rails and other web applications. (As you might guess, Heroku itself is written in Rails.) Heroku makes deploying Rails applications ridiculously easy, as long as your source code is under version control with Git—which is yet another reason to follow the Git setup steps in Section 1.3 if you haven’t already. In addition, for many purposes, including for this tutorial, Heroku’s inexpensive hobbyist tier31 is more than sufficient.
Note: Heroku made a free tier available from its launch in 2009 through late 2022; unfortunately, as of this writing the free tier is not generally available, despite efforts by myself and others to retain it. Learners with a limited budget or without access to a credit card are encouraged to look into services like Render and Fly.io; using one of these services will likely give you some opportunities to apply your technical sophistication (Box 1.2).
The rest of this section is dedicated to deploying our first application to Heroku. Some of the ideas are fairly advanced, so don’t worry about understanding all the details; what’s important is that by the end of the process we’ll have deployed our application to the live web.
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).
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.
$ 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.)
$ 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>
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).
$ 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.
$ 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.)
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.
- By making the same change as in Section 1.2.4.1, arrange for your production app to display “hola, mundo!”.
- 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 omitmain
in the Git push, as ingit push heroku
.
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.
- Run
heroku help
to see a list of Heroku commands. (If the output ofheroku help
doesn’t fit in your terminal window, either scroll up or useheroku help | less
to pipe to theless
command.) What is the command to display logs for an app? - 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.)
1.5 Conclusion
We’ve come a long way in this chapter: development environment setup, installation, version control, and deployment. In the next chapter, we’ll build on the foundation from Chapter 1 to make a database-backed toy app, which will give us our first real taste of what Rails can do.
If you’d like to share your progress at this point, feel free to send a tweet or Facebook status update with something like this:
I also recommend signing up for the Rails Tutorial email list,38 which will ensure that you receive priority updates (and exclusive coupon codes) regarding the Ruby on Rails Tutorial.
1.5.1 What we learned in this chapter
- Ruby on Rails is a web development framework written in the Ruby programming language.
- Installing Rails, generating an application, and editing the resulting files is easy using a preconfigured cloud environment.
- Rails comes with a command-line command called
rails
that can generate new applications (rails new
) and run local servers (rails server
). - We added a controller action and modified the root route to create a “hello, world” application.
- We protected against data loss while enabling collaboration by placing our application source code under version control with Git and pushing the resulting code to a private repository at GitHub.
- We deployed our application to a production environment using Heroku.
1.6 Conventions used in this book
The conventions used in this book are mostly self-explanatory. In this section, we’ll go over some that may not be.
This tutorial makes frequent use of command-line commands. For simplicity, all command-line examples use a Unix-style command-line prompt (a dollar sign), as follows:
$ echo "hello, world"
hello, world
The dollar sign should not be typed literally, so what you enter in the terminal in this case is echo "hello, world"
.
Rails comes with many commands that can be run at the command line. For example, in Section 1.2.2 we ran a local development web server with the rails server
command:
$ rails server
As with the command-line prompt, the Rails Tutorial uses the Unix convention for directory separators (i.e., a forward slash /
). For example, the sample application production.rb
configuration file appears as follows:
config/environments/production.rb
This file path should be understood as being relative to the application’s root directory, which will vary by system. For example, on the cloud IDE (Section 1.1.1) it looks like this:
/home/ubuntu/environment/sample_app/
Thus, the full path to production.rb
is
/home/ubuntu/environment/sample_app/config/environments/production.rb
I will typically omit the application path and write just config/environments/production.rb
for short.
The Rails Tutorial often shows output from various programs. Because of the innumerable small differences between different computer systems, the output you see may not always agree exactly with what is shown in the text, but this is not a cause for concern. In addition, some commands may produce errors depending on your system; rather than attempt the Sisyphean task of documenting all such errors in this tutorial, I will delegate to the “Google the error message” algorithm, which among other things is good practice for real-life software development (Box 1.2). If you run into any problems while following the tutorial, I suggest consulting the resources listed at the Rails Tutorial Help page.39
Because the Rails Tutorial covers testing of Rails applications, it is often helpful to know if a particular piece of code causes the test suite to fail (indicated by the color red) or pass (indicated by the color green). For convenience, code resulting in a failing test is thus indicated with red, while code resulting in a passing test is indicated with green.
Finally, for convenience the Ruby on Rails Tutorial adopts two conventions designed to make the many code samples easier to understand. First, some code listings include one or more highlighted lines, as seen below:
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
Such highlighted lines typically indicate the most important new code in the given sample, and often (though not always) represent the difference between the present code listing and previous listings. Second, for brevity and simplicity many of the book’s code listings include vertical dots, as follows:
class User < ApplicationRecord
.
.
.
has_secure_password
end
These dots represent omitted code and should not be copied literally.
In most cases, typing in code examples by hand is the most effective way to learn, but sometimes copying and pasting is more practical. To make the latter more convenient, all code listings from this book are available online at the following URL:
https://github.com/learnenough/ruby_on_rails_tutorial_code_listings_7th_ed