Friday, May 26, 2017

The Sign Up Page in My Own's Style

* I went back to myapp2 (https://github.com/jimmy2046/myapp2)

* I made and switched to a new branch named sign-up. It was just the same shell command.
$ git checkout -b sign-up

* I added a debug file in the home.html.erb file.
myapp2/app/views/static_pages/home.html.erb
<%= debug(params) if Rails.env.development? %>

* I added the resources in the routes.rb file.
myapp2/config/routes.rb
Rails.application.routes.draw do
  get 'users/new'

  root 'static_pages#home'
   
# home_path
  get '/home',     to: 'static_pages#home'

# help_path   
  get '/help',    to: 'static_pages#help'

# about_path   
  get '/about',    to: 'static_pages#about'   

# contact_path
  get '/contact',  to: 'static_pages#contact'   

# signup_path   
  get  '/signup',  to: 'users#new'

  resources :users   
   
end

The first part of this blog to to make a page to show user profile.  

* I created a temporary html.erb file to show user's profile.
myapp2/app/views/users/show.html.erb
<%= @user.first_name %>, <%= @user.last_name %>, <%= @user.email %>

* I added a new method show in the Users controller.
myapp2/app/controllers/users_controller.rb
  def show
    @user = User.find(params[:id])
  end

* I create a sample user profile in Rails Console for testing.
$ rails console
User.create(first_name: "Jimmy", last_name: "Chong",
email: "jimmyc@example.com",
password: "foobar", password_confirmation: "foobar")

* I typed http://localhost:3000/users/1 in my Firefox browser to make sure the a user could be queried in the Sqlite database.

* I re-wrote show.html.erb to make a trendy style.
myapp2/app/views/users/show.html.erb
<% provide(:title, @user.first_name+" "+@user.last_name) %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

<!--
Classic Template
http://www.templatemo.com/tm-488-classic
-->
    <!-- load stylesheets -->
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400">  <!-- Google web font "Open Sans" -->
    <link rel="stylesheet" href="/assets/css/bootstrap.min.css">                                      <!-- Bootstrap style -->
    <link rel="stylesheet" href="/assets/css/templatemo-style.css">                                   <!-- Templatemo style -->

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
        <!--[if lt IE 9]>
          <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
          <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
          <![endif]-->
</head>

    <body>
      
        <div class="tm-header">
            <div class="container-fluid">
                <div class="tm-header-inner">
                    <%= link_to("Sample App", root_path, :class => "navbar-brand tm-site-name") %>
            <!--        <a href="#" class="navbar-brand tm-site-name">Sample App</a>  -->
                   
                    <!-- navbar -->
                    <nav class="navbar tm-main-nav">

                        <button class="navbar-toggler hidden-md-up" type="button" data-toggle="collapse" data-target="#tmNavbar">
                            &#9776;
                        </button>
                       
                        <div class="collapse navbar-toggleable-sm" id="tmNavbar">
                            <ul class="nav navbar-nav">
                                <li class="nav-item"><%= link_to "Home", root_path, class: "nav-link" %></li>
                                <li class="nav-item"><%= link_to "Help", help_path, class: "nav-link" %></li>
                                <li class="nav-item"><%= link_to "Log in", '#', class: "nav-link" %></li>
                            </ul>
                        </div>
                    </nav> 
                </div>                                 
            </div>           
        </div>

        <div class="tm-about-img-container">
           
        </div>       

        <section class="tm-section">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 text-xs-center">

                        <p class="tm-subtitle">
                        <%= @user.first_name %>, <%= @user.last_name %>, <%= @user.email %></p>
                       
                    </div>
                    <%= link_to image_tag("rails.png", alt: "Rails logo"),
            'http://rubyonrails.org/' %>
                    <%= debug(params) if Rails.env.development? %>
                       
                    </div>
                </div>
        </section>
       
        <footer class="tm-footer">
            <div class="container-fluid">
                <div class="row">
                   
                    <div class="col-xs-12 col-sm-6 col-md-6 col-lg-3 col-xl-3">
                        <div class="tm-footer-content-box tm-footer-links-container">
                       
                            <h3 class="tm-gold-text tm-title tm-footer-content-box-title">Ruby on Rails Tutorial</h3>
                            <nav>
                                <ul class="nav">
                                    <li>
                                        <%= link_to("About", about_path, :class => "tm-footer-link") %>
                                    </li>
                                    <li>
                                        <%= link_to("Contact", contact_path, :class => "tm-footer-link") %>
                                    </li>
                                    <li>
                                        <a href="http://news.railstutorial.org/" class="tm-footer-link">News</a>
                                    </li>
                                </ul>
                            </nav>
                        </div>                     
                    </div>

                    <!-- Add the extra clearfix for only the required viewport
                        http://stackoverflow.com/questions/24590222/bootstrap-3-grid-with-different-height-in-each-item-is-it-solvable-using-only
                    -->
                    <div class="clearfix hidden-lg-up"></div>
                </div>

                <div class="row">
                    <div class="col-xs-12 tm-copyright-col">
                        <p class="tm-copyright-text">Jimmy Chong 2017</p>
                    </div>
                </div>
            </div>
        </footer>

        <!-- load JS files -->
        <script src="js/jquery-1.11.3.min.js"></script>             <!-- jQuery (https://jquery.com/download/) -->
        <script src="https://www.atlasestateagents.co.uk/javascript/tether.min.js"></script> <!-- Tether for Bootstrap, http://stackoverflow.com/questions/34567939/how-to-fix-the-error-error-bootstrap-tooltips-require-tether-http-github-h -->
        <script src="js/bootstrap.min.js"></script>                 <!-- Bootstrap (http://v4-alpha.getbootstrap.com/) -->
      
</body>
</html>

* I copied the Gravatar helper method to Users helper file. I changed the original users.name variable to user.first_name.
myapp2/app/helpers/users_helper.rb
module UsersHelper
   
  # Returns the Gravatar for the given user.
  def gravatar_for(user)
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
    image_tag(gravatar_url, alt: user.first_name, class: "gravatar")
  end
   
end

* In the same time, I coded the show.html.erb file to display the Gravatar of the user.
myapp2/app/views/users/show.html.erb
<% provide(:title, @user.first_name+" "+@user.last_name) %>
...
<h1>
  <%= gravatar_for @user %>
  <%= @user.first_name %> <%= @user.last_name %>
</h1>
...

* This was the preliminary version of user show page with templatemo CSS theme. (Firefox: http://localhost:3000/users/1)

The second part is to make the user sign up form.

* I copied the Users controller file from chapter 7 of the book, which included methods: show, new, create, and user_params.
myapp2/app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end   
   
  def new
    @user = User.new     
  end

  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Welcome to the Sample App!"       
      redirect_to @user
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:first_name, :last_name, :email, :password,
                                   :password_confirmation)
    end   

end

* Similarly, I copied the users/new.html.erb from the book to myapp2 project with the exception that I had to separate the name field into 2 independent first_name and last_name fields.
myapp2/app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user, url: signup_path) do |f| %>
     
      <%= f.label :first_name %>
      <%= f.text_field :first_name, class: 'form-control' %>
     
      <%= f.label :last_name %>
      <%= f.text_field :last_name, class: 'form-control' %>
     
      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

* I created a shared view to display sign up error messages.
$ mkdir app/views/shared

* I copied the partial html.erb file from the book to display error message in HTML format.
myapp2/app/views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

* I added <%= render 'shared/error_messages' %> to new.html.erb Users view.
myapp2/app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user, url: signup_path) do |f| %>
      <%= render 'shared/error_messages' %>
     
      <%= f.label :first_name %>
      <%= f.text_field :first_name, class: 'form-control' %>
     
      <%= f.label :last_name %>
      <%= f.text_field :last_name, class: 'form-control' %>
     
      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

* I did not understand why, but I added a line post '/signup',  to: 'users#create' in config/routes.rb file to make the sign up form work.
myapp2/config/routes.rb
post '/signup',  to: 'users#create'

* I didn't realized that, in the Users controller file, the permitted params.require
is (:first_name, :last_name, :email, :password, :password_confirmation)
NOT (:name, :email, :password, :password_confirmation)
I made the correction for the above code as well.

* Next, I generated the integration test for the Users sign up module. And then, I copied the Ruby codes from the book.
$ rails generate integration_test users_signup

* I added a gem 'rails-controller-testing' in the Gemfile for the testing of Rails controller.
gem 'rails-controller-testing'

* After I had added a line gem 'rails-controller-testing' to my Gemfile, I ran bundle install
$ bundle install

* In refer to the book's exercise, I added a signup route in the routes.rb file for responding to POST requests.
myapp2/config/routes.rb
  post '/signup',  to: 'users#create'

*  I modified the form_for() line in the new.html.erb view file in the user controller.
myapp2/app/views/users/new.html.erb
    <%= form_for(@user, url: signup_path) do |f| %>

* For successful user sign up, I copied the create method from the book to the Users controller file.
myapp2/app/controllers/users_controller.rb
  def create
    @user = User.new(user_params)
      if @user.save
      flash[:success] = "Welcome to the Sample App!"         
      redirect_to @user
    else
      render 'new'
    end
  end

* Then, I added the flash message (prompt) in the application layout file.
myapp2/app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end   
   
  def new
    @user = User.new     
  end

  def create
    @user = User.new(user_params)
      if @user.save
      flash[:success] = "Welcome to the Sample App!"         
      redirect_to @user
    else
      render 'new'
    end
  end   

  private

    def user_params
      params.require(:user).permit(:first_name, :last_name, :email, :password,
                                   :password_confirmation)
    end
   
end

* I reset the database for the first sign up.
$ rails db:migrate:reset

* The sign up page in my own style.

* The sign up success page (user profile page) in my own style.


* I added one more test case for testing a legitimate sign up scenario.
myapp2/test/integration/users_signup_test.rb
  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { first_name:  "Example",
                                         last_name: "User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    follow_redirect!
    assert_template 'users/show'
  end

* At the end of today's blog, I added all untracked files, committed all changes, went back to master branch, and merged sign-up branch.
$ git add -A
$ git commit -m "Finish user signup"
$ git checkout master
$ git merge sign-up

* I enabled SSL.
myapp2/config/environments/production.rb
  config.force_ssl = true

* I enabled preload_app! in puma.rb
myapp2/config/puma.rb
preload_app!

* Finally, I ran a test and pushed it onto Github.
$ rails test
$ git add -A
$ git commit -m "Use SSL and the Puma webserver in production"
$ git push

* The Github address for the myapp2 project is
https://github.com/jimmy2046/myapp2

* Here are final version of some important programming files for today's blog:

myapp2/app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user, url: signup_path) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :first_name %>
      <%= f.text_field :first_name, class: 'form-control' %>

      <%= f.label :last_name %>
      <%= f.text_field :last_name, class: 'form-control' %>     
     
      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

myapp2/app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end   
   
  def new
    @user = User.new     
  end

  def create
    @user = User.new(user_params)
      if @user.save
      flash[:success] = "Welcome to the Sample App!"         
      redirect_to @user
    else
      render 'new'
    end
  end   

  private

    def user_params
      params.require(:user).permit(:first_name, :last_name, :email, :password,
                                   :password_confirmation)
    end
   
end

myapp2/config/routes.rb
Rails.application.routes.draw do
  get 'users/new'

  root 'static_pages#home'
   
# home_path
  get '/home',     to: 'static_pages#home'

# help_path   
  get '/help',    to: 'static_pages#help'

# about_path   
  get '/about',    to: 'static_pages#about'   

# contact_path
  get '/contact',  to: 'static_pages#contact'   

# signup_path   
  get  '/signup',  to: 'users#new'

  post '/signup',  to: 'users#create'   
   
  resources :users   
   
end

myapp2/app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all',
                                              'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>       
    <%= yield %>
    </div>
  </body>   
</html>

myapp2/test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path, params: { user: { first_name:  "",
                                         last_name:   "",
                                         email: "user@invalid",
                                         password:              "foo",
                                         password_confirmation: "bar" } }
    end
    assert_template 'users/new'
  end   

  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { first_name:  "Example",
                                         last_name: "User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    follow_redirect!
    assert_template 'users/show'
  end   
   
end

end

myapp2/app/views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

1 comment:

  1. Very informative blog and useful article thank you for sharing with us , keep posting learn more Ruby on Rails Online Training India

    ReplyDelete

How to kill an abandoned process in Linux/Unix

I remembered it, then I forgot, then I remembered it, and then I forgot again. In case of a Linux/Unit process hang, I have to figure out ...