Chapter 1

From Zero to Deploy

Welcome to the Ruby on Rails Tutorial (https://www.railstutorial.org/)! 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 (https://rubyonrails.org/)—continues to be an outstanding choice for learning web development (Box 1.1).

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 (https://www.learnenough.com/), especially Learn Enough Command Line to Be Dangerous (https://www.learnenough.com/command-line) and Learn Enough Ruby to Be Dangerous (https://www.learnenough.com/ruby).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.

1. Adding the rest of the Learn Enough sequence (https://www.learnenough.com/all-access) 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.

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.

Images

Figure 1.1: The beginning hello app.

Images

Figure 1.2: An intermediate toy app.

Images

Figure 1.3: The final sample app.

2. Baby photo courtesy of Pshenina_m/Shutterstock

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 (https://www.learnenough.com/dev-environmenttutorial#sec-native_os_setup) 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.

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.

Images

Figure 1.4: The anatomy of the cloud IDE.

Here are the steps for getting started with the cloud development environment:3

3. Due to the constantly evolving nature of sites like AWS, details may vary; use your technical sophistication (Box 1.2) to resolve any discrepancies.

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

    4. https://aws.amazon.com/

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

    5. https://www.railstutorial.org/cloud9-signup

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

    Images

    Figure 1.5: Creating an environment on AWS Cloud9.

    Images

    Figure 1.6: Naming a new work environment at AWS Cloud9.

    Images

    Figure 1.7: Selecting Ubuntu Server.

    Images

    Figure 1.8: The final step before provisioning the IDE.

    Images

    Figure 1.9: The default cloud IDE.

    6. If you’ve previously done this tutorial, you may want to use a fresh environment, with a name like “rails-tutorial-7”.

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

Images

Figure 1.10: Setting Cloud9 to use two spaces for indentation.

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

7. This uses the echo and >> (append) commands covered in Section 1.3 (https://www.learnenough.com/command-line-tutorial/basics#sec-our_first_command) and Section 2.1 (https://www.learnenough.com/command-line-tutorial/manipulating_files) 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.

Listing 1.1: Configuring the .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.)

Listing 1.2: Installing Rails with a specific version number.

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

Listing 1.3: Installing Bundler with a specific version number.

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

Listing 1.4: Adding extra size to the cloud IDE.

$ 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

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

Listing 1.5: Making an 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.

Table 1.1: Some common Unix commands.

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

Listing 1.6: Running 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.

Images

Figure 1.11: The directory structure for a newly created Rails app.

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.

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

Table 1.2: A summary of the default Rails directory structure.

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

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

Figure 1.12: The default Gemfile open in a text editor.

9. This is a typical example of technical sophistication (Box 1.2).

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

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

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.

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

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

12. As noted in Table 3.1, you can even leave off install, as the bundle command by itself is an alias for bundle install.

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

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.

Images

Figure 1.13: Allowing Cloud9 to connect to the Rails server.

Listing 1.10: Allowing connections to the local web server.
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

Images

Figure 1.14: Opening a new terminal tab.

Images

Figure 1.15: Running the Rails server in a separate tab.

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

14. 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”.

Listing 1.11: Running the Rails server.

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

Images

Figure 1.16: Sharing the local server running on the cloud workspace.

Images

Figure 1.17: Opening the running app in a full browser window or tab.

Images

Figure 1.18: The default Rails page served by rails server.

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:

  1. According to the default Rails page, what is the version of Ruby on your system? Confirm by running ruby -v at the command line.

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

Images

Figure 1.19: A schematic representation of the model-view-controller (MVC) architecture.

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

Listing 1.12: Adding a 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.

Listing 1.13: The default routing file (formatted to fit).
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

Listing 1.14: Setting the root route.
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!

Images

Figure 1.20: Viewing “hello, world!” in the browser.

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

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. Change the content of the hello action in Listing 1.12 to read “hola, mundo!” instead of “hello, world!”.

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

    Images

    Figure 1.21: Changing the root route to return “¡Hola, mundo!”.

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

  3. By following the example of the hello action in Listing 1.12, add a second action called goodbye that renders the text “goodbye, world!”. Edit the routes file from Listing 1.14 so that the root route goes to goodbye instead of to hello (Figure 1.22).

    Images

    Figure 1.22: Changing the root route to return “goodbye, world!”.

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 (https://www.learnenough.com/git).

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.

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 (https://www.learnenough.com/git-tutorial/getting_started#sec-installation_and_setup) 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 [email protected]

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” (https://news.learnenough.com/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.

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

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.

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

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

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

$ 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

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

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 <[email protected]>
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 (https://www.learnenough.com/command-line-tutorial/inspecting_files#secless_is_more).)

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 (https://www.learnenough.com/command-line-tutorial/directories#secremoving_directories) 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.

21. Bitbucket (https://bitbucket.org/) and GitLab (https://about.gitlab.com/) are also excellent choices. Like GitHub, GitLab is written in Rails.

Getting started with GitHub is straightforward: just sign up for a GitHub account (https://github.com/join) 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 (https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git)”.)

Images

Figure 1.23: Signing up for GitHub.

Once you’ve signed up or signed in, click on the + sign dropdown menu and select “New repository” (Figure 1.24).

Images

Figure 1.24: Selecting the “New repository” option.

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

Images

Figure 1.25: Creating a private repository at GitHub.

22. GitHub allows unlimited public and private repositories.

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.

Images

Figure 1.26: Code for adding an existing repository.

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.

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

Listing 1.20: A template for the first push to GitHub.

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

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

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

After executing the first git push as shown in Listing 1.20, you should reload the current page (using, e.g., ImagesR 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).

Images

Figure 1.27: The browser reload-page button.

Images

Figure 1.28: A GitHub repository page.

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.

Images

Figure 1.29: GitHub’s rendering of the default Rails README.

26. See Learn Enough Text Editor to Be Dangerous (https://www.learnenough.com/text-editor) and Learn Enough Git to Be Dangerous for more information about Markdown.

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

27. For a convenient way to visualize Git repositories, take a look at Atlassian’s SourceTree app (https://www.sourcetreeapp.com/).

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.

28. See, for example, the section on Collaborating (https://www.learnenough.com/git-tutorial/collaborating) in Learn Enough Git to Be Dangerous.

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.

Images

Figure 1.30: Editing the README file.

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!
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, 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 (https://www.learnenough.com/gittutorial/getting_started#aside-commit_messages) 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

Figure 1.31: The improved README file at GitHub.

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

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

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.

30. Pronounced “Engine X”.

My favorite Rails deployment option 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 free tier is more than sufficient.

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:31

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

 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.

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

32. toolbelt.heroku.com

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:

Images

Figure 1.32: The API key at Heroku.

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

Listing 1.25: Creating and configuring a new application at Heroku.

$ heroku create
Creating app... done, Images 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).33 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.

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

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.34 (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 (https://www.learnenough.com/custom-domains).)

Images

Figure 1.33: The first Rails Tutorial application running on Heroku.

34. Your results may differ if you completed the exercises in Section 1.2.4.

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, arrange for your production app to display “hola, mundo!”.

  2. As in Section 1.2.4, 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.

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.35 (By the way, as a preview of Ruby’s compact awesomeness, here’s the code I used to generate the subdomains above:

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

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

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

36. 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!

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 (https://devcenter.heroku.com/) 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.)

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’m learning Ruby on Rails with the @railstutorial! https://www.railstutorial.org/

I also recommend signing up for the Rails Tutorial email list,37 which will ensure that you receive priority updates (and exclusive coupon codes) regarding the Ruby on Rails Tutorial.

37. railstutorial.org/email

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

38. https://www.railstutorial.org/help

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
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.14.118