Home > Articles

Sign Up

This chapter is from the book

7.4 Successful Signups

Having handled invalid form submissions, now it’s time to complete the signup form by actually saving a new user (if valid) to the database. First, we try to save the user; if the save succeeds, the user’s information gets written to the database automatically, and we then redirect the browser to show the user’s profile (together with a friendly greeting), as mocked up in Figure 7.21. If it fails, we simply fall back on the behavior developed in Section 7.3.

Figure 7.21

Figure 7.21: A mockup of successful signup.

7.4.1 The Finished Signup Form

To complete a working signup form, we need to fill in the commented-out section in Listing 7.19 with the appropriate behavior. Currently, the form simply freezes on valid submission, as indicated by the subtle color change in the submission button (Figure 7.22), although this behavior may be system-dependent. This is because the default behavior for a Rails action is to render the corresponding view, and there isn’t a view template corresponding to the create action (Figure 7.23).

Figure 7.22

Figure 7.22: The frozen page on valid signup submission.

Figure 7.23

Figure 7.23: The create template error in the server log.

Although it’s possible to render a template for the create action, the usual practice is to redirect to a different page instead when the creation is successful. In particular, we’ll follow the common convention of redirecting to the newly created user’s profile, although the root path would also work. The application code, which introduces the redirect_to method, appears in Listing 7.26.

Listing 7.26: The user create action with a save and a redirect.

app/controllers/users_controller.rb

class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render 'new', status: :unprocessable_entity
    end
  end
  private
    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

Note that we’ve written

redirect_to @user

where we could have used the equivalent

redirect_to user_url(@user)

This is because Rails automatically infers from redirect_to @user that we want to redirect to user_url(@user). (The name @user is incidental; it could be @foo and the redirect would still work as long as the object has class User.)

Exercises

  1. Using the Rails console, verify that a user is in fact created when submitting valid information.

  2. Confirm by updating Listing 7.26 and submitting a valid user that redirect_to user_url(@user) has the same effect as redirect_to @user.

7.4.2 The Flash

With the code in Listing 7.26, our signup form is actually working, but before submitting a valid registration in a browser we’re going to add a bit of polish common in web applications: a message that appears on the subsequent page (in this case, welcoming our new user to the application) and then disappears upon visiting a second page or on page reload.

The Rails way to display a temporary message is to use a special method called the flash, which we can treat like a hash. Rails adopts the convention of a :success key for a message indicating a successful result (Listing 7.27).

Listing 7.27: Adding a flash message to user signup.

app/controllers/users_controller.rb

class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new', status: :unprocessable_entity
    end
  end
  private
    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

By assigning a message to the flash, we are now in a position to display the message on the first page after the redirect. Our method is to iterate through the flash and insert all relevant messages into the site layout. You may recall the console example in Section 4.3.3, where we saw how to iterate through a hash using the strategically named flash variable (Listing 7.28).

Listing 7.28: Iterating through a flash hash in the console.

$ rails console
>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", danger: "It failed."}
>> flash.each do |key, value|
?>   puts "#{key}"
?>   puts "#{value}"
>> end
success
It worked!
danger
It failed.

By following this pattern, we can arrange to display the contents of the flash site-wide using code like this:

<% flash.each do |message_type, message| %>
  <div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>

(This code is a particularly ugly and difficult-to-read combination of HTML and ERb; making it prettier is left as an exercise (Section 7.4.4).) Here the embedded Ruby

alert-<%= message_type %>

makes a CSS class corresponding to the type of message, so that for a :success message the class is

alert-success

(The key :success is a symbol, but embedded Ruby automatically converts it to the string "success" before inserting it into the template.) Using a different class for each key allows us to apply different styles to different kinds of messages. For example, in Section 8.1.4 we’ll use flash[:danger] to indicate a failed login attempt.13 (In fact, we’ve already used alert-danger once, to style the error-message div in Listing 7.21.) Bootstrap CSS supports styling for four such flash classes for increasingly urgent message types (success, info, warning, and danger), and we’ll find occasion to use all of them in the course of developing the sample application (info in Section 11.2, warning in Section 11.3, and danger for the first time in Section 8.1.4).

Because the message is also inserted into the template, the full HTML result for

flash[:success] = "Welcome to the Sample App!"

appears as follows:

<div class="alert alert-success">Welcome to the Sample App!</div>

Putting the embedded Ruby discussed above into the site layout leads to the code in Listing 7.29.

Listing 7.29: Adding the contents of the flash variable to the site layout.

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  .
  .
  .
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
    .
    .
    .
  </body>
</html>

Exercises

  1. In the console, confirm that you can use interpolation (Section 4.2.1) to interpolate a raw symbol. For example, what is the return value of "#{:success}"?

  2. How does the previous exercise relate to the flash iteration shown in Listing 7.28?

7.4.3 The First Signup

We can see the result of all this work by signing up the first user for the sample app. Even though previous submissions didn’t work properly (as shown in Figure 7.22), the user.save line in the Users controller still works, so users might still have been created. To clear them out, we’ll reset the database as follows:

$ rails db:migrate:reset

On some systems, you might have to restart the web server (using Ctrl-C) for the changes to take effect (Box 1.2).

We’ll create the first user with the name “Rails Tutorial” and email address “example@railstutorial.org”, as shown in Figure 7.24. The resulting page (Figure 7.25) shows a friendly flash message upon successful signup, including nice green styling for the success class, which comes included with the Bootstrap CSS framework from Section 5.1.2. Then, upon reloading the user show page, the flash message disappears as promised (Figure 7.26).

Figure 7.24

Figure 7.24: Filling in the information for the first signup.

Figure 7.25

Figure 7.25: The results of a successful user signup, with flash message.

Figure 7.26

Figure 7.26: The flash-less profile page after a browser reload.

Exercises

  1. Using the Rails console, find by the email address to double-check that the new user was actually created. The result should look something like Listing 7.30.

  2. Create a new user with your primary email address. Verify that the Gravatar correctly appears.

Listing 7.30: Finding the newly created user in the database.

$ rails console
>> User.find_by(email: "example@railstutorial.org")
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.
org", created_at: "2016-05-31 17:17:33", updated_at: "2016-05-31 17:17:33",
password_digest: "$2a$10$8MaeHdnOhZvMk3GmFdmpPOeG6a7u7/k2Z9TMjOanC9G...">

7.4.4 A Test for Valid Submission

Before moving on, we’ll write a test for valid submission to verify our application’s behavior and catch regressions. As with the test for invalid submission in Section 7.3.4, our main purpose is to verify the contents of the database. In this case, we want to submit valid information and then confirm that a user was created. In analogy with Listing 7.23, which used

assert_no_difference 'User.count' do
  post users_path, ...
end

here we’ll use the corresponding assert_difference method:

assert_difference 'User.count', 1 do
  post users_path, ...
end

As with assert_no_difference, the first argument is the string 'User.count', which arranges for a comparison between User.count before and after the contents of the assert_difference block. The second (optional) argument specifies the size of the difference (in this case, 1).

Incorporating assert_difference into the file from Listing 7.23 yields the test shown in Listing 7.31. Note that we’ve used the follow_redirect! method after posting to the users path. This simply arranges to follow the redirect after submission, resulting in a rendering of the 'users/show' template. (It’s probably a good idea to write a test for the flash as well, which is left as an exercise (Section 7.4.4).)

Listing 7.31: A test for a valid signup. GREEN

test/integration/users_signup_test.rb

require "test_helper"
class UsersSignupTest < ActionDispatch::IntegrationTest
  .
  .
  .
  test "valid signup information" do
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    follow_redirect!
    assert_template 'users/show'
  end
end

Note that Listing 7.31 also verifies that the user show template renders following successful signup. For this test to work, it’s necessary for the Users routes (Listing 7.3), the Users show action (Listing 7.5), and the show.html.erb view (Listing 7.8) to work correctly. As a result, the one line

assert_template 'users/show'

is a sensitive test for almost everything related to a user’s profile page. This sort of end-to-end coverage of important application features illustrates one reason why integration tests are so useful.

Exercises

  1. Write a test for the flash implemented in Section 7.4.2. How detailed you want to make your tests is up to you; a suggested ultra-minimalist template appears in Listing 7.32, which you should complete by replacing FILL_IN with the appropriate code. (Even testing for the right key, much less the text, is likely to be brittle, so I prefer to test only that the flash isn’t empty.)

  2. As noted above, the flash HTML in Listing 7.29 is ugly. Verify by running the test suite that the cleaner code in Listing 7.33, which uses the Rails content_tag helper, also works.

  3. Verify that the test fails if you comment out the redirect line in Listing 7.26.

  4. Suppose we changed @user.save to false in Listing 7.26. How does this change verify that the assert_difference block is testing the right thing?

Listing 7.32: A template for a test of the flash.

test/integration/users_signup_test.rb

require "test_helper"
class UsersSignupTest < ActionDispatch::IntegrationTest
  .
  .
  .
  test "valid signup information" do
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    follow_redirect!
    assert_template 'users/show'
    assert_not flash.FILL_IN
  end
end

Listing 7.33: The flash ERb in the site layout using content_tag.

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
      .
      .
      .
      <% flash.each do |message_type, message| %>
        <%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
      <% end %>
      .
      .
      .
</html>

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020