* First, I create a branch called basic-login.
$ git checkout -b basic-login
* I moved the debug message <%= debug(params) if Rails.env.development? %> from home.html.erb to application layout file application.html.erb
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 %>
<%= debug(params) if Rails.env.development? %>
</div>
</body>
</html>
* I created a new file for the header: _header.html.erb on layout directory and I copied the content of <div> class "tm-header" to the header partial file.
myapp2/app/views/layouts/_header.html.erb
<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">
☰
</button>
<div class="collapse navbar-toggleable-sm" id="tmNavbar">
<ul class="nav navbar-nav">
<li class="nav-item active"><%= link_to "Home", root_path, class: "nav-link" %></li>
<li class="nav-item active"><%= link_to "Help", help_path, class: "nav-link" %></li>
<li class="nav-item active"><%= link_to "Log in", '#', class: "nav-link" %></li>
</ul>
</div>
</nav>
</div>
</div>
</div>
* I added the <%= render 'layouts/header' %> tag to the application layout file. It was placed after the <body> tag and before the <div class="container"> tag.
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>
<%= render 'layouts/header' %>
<div class="container">
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
<%= yield %>
<%= debug(params) if Rails.env.development? %>
</div>
</body>
</html>
* Similarly, I did it for the footer too. I created a footer partial file called: _footer.html.erb. And then, I copied the codes between the <footer class="tm-footer"> tag and </footer> to the _footer.html.erb partial file.
myapp2/app/views/layouts/_footer.html.erb
<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>
* Afterward, I added the <%= render 'layouts/footer' %> line the the application layout file. It was placed between the <%= yield %> and the <%= debug() %> tag.
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>
<%= 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>
* I went back to section 8.1.1 of the book: Sessions controller. I made a Sessions controller with method new.
$ rails generate controller Sessions new
* I copied the codes for the Sessions controller (Login controller).
myapp2/config/routes.rb
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
* I updated the path for Sessions controller test file.
myapp2/test/controllers/sessions_controller_test.rb
test "should get new" do
get login_path
assert_response :success
end
* I copied the codes for login page.
myapp2/app/views/sessions/new.html.erb
<% provide(:title, "Log in") %>
<h1>Log in</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:session, url: login_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.submit "Log in", class: "btn btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>
</div>
* I copied the codes of Sessions controller.
myapp2/app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out
redirect_to root_url
end
end
* I generated an integration test for user login.
$ rails generate integration_test users_login
* I copied the codes for login test.
myapp2/test/integration/users_login_test.rb
test "login with invalid information" do
get login_path
assert_template 'sessions/new'
post login_path, params: { session: { email: "", password: "" } }
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
* Then I ran the test for the login part only.
$ rails test test/integration/users_login_test.rb
Running via Spring preloader in process 5373
Run options: --seed 21529
# Running:
.
Finished in 0.602614s, 1.6594 runs/s, 6.6378 assertions/s.
1 runs, 4 assertions, 0 failures, 0 errors, 0 skips
* I kept going to section 8.2 Logging in, I added Sessions helper module into the Application controller.
myapp2/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
* I copied the session helper codes.
myapp2/app/helpers/sessions_helper.rb
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Returns the current logged-in user (if any).
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Logs out the current user.
def log_out
session.delete(:user_id)
@current_user = nil
end
end
* This part, Listing 8.19: Changing the layout links for logged-in users, was slightly difficult for creating an header with drop down menu using the customized CSS theme by templatemo. I needed to rewrite the changing the layout links for logged-in use for the templatemo theme.
* I re-wrote the header to fit the drop down menu and the CSS theme. This code does not change the style and color of the Account drop down menu. The pull down menu is not pretty. However, this HTML code makes the Ruby code works.
myapp2/app/views/layouts/_header.html.erb
<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>
<% if logged_in? %>
<li class="nav-item"><%= link_to "Users", '#', class: "nav-link" %></li>
<li class="nav-item dropdown">
<a href="#" class="nav-item dropdown-toggle" data-toggle="dropdown"> Account <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", '#' %></li>
<li class="nav-item divider"></li>
<li>
<%= link_to "Log out", logout_path, method: :delete %>
</li>
</ul>
</li>
<% else %>
<li class="nav-item"><%= link_to "Log in", login_path, class: "nav-link" %></li>
<% end %>
</ul>
</div>
* I adjusted the height of the header banner to make it fits with the drop down menu.
myapp2/app/assets/stylesheets/templatemo-style.css
.tm-header-inner {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
height: 175px;
}
* I copied the file bootstrap.min.js to myapp2/app/assets/javascripts.
* But I did NOT change the application.js file.
* I added the resulting digest method in user.rb model file.
myapp2/app/models/users.rb
class User < ApplicationRecord
before_save { self.email = email.downcase }
validates :first_name, presence: true, length: { maximum: 50 }
validates :last_name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
end
* I created a user fixtur in user.yml.
myapp2/test/fixtures/users.yml
jimmy:
first_name: Jimmy
last_name: Chong
email: jimmyc5@example.com
password_digest: <%= User.digest('password') %>
* I copied the Integration Test codes to define new user "jimmy" and to test "login with valid information".
myapp2/test/integration/users_login_test.rb
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
def setup
@user = users(:jimmy)
end
test "login with invalid information" do
get login_path
assert_template 'sessions/new'
post login_path, params: { session: { email: "", password: "" } }
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
test "login with valid information" do
get login_path
post login_path, params: { session: { email: @user.email,
password: 'password' } }
assert_redirected_to @user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
end
end
* And then, I ran the integration test.
$ rails test test/integration/users_login_test.rb
Running via Spring preloader in process 5842
Run options: --seed 52302
# Running:
..
Finished in 0.711340s, 2.8116 runs/s, 14.0580 assertions/s.
2 runs, 10 assertions, 0 failures, 0 errors, 0 skips
* I added the line "log_in @user" in the Create method of Users Controller to let a new user log in automatically once he has signed up.
myapp2/app/controllers/users_controller.rb
def create
@user = User.new(user_params)
if @user.save
log_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
* I added a Test Helper method: is_logged_in? to check if a user is logged in.
myapp2/test/test_helper.rb
def is_logged_in?
!session[:user_id].nil?
end
* I added a line assert is_logged_in? in the Integration Test: users_signup_test.rb to test When a user has successfully sign up, the user will re-directed to log in automatically.
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'
assert is_logged_in?
end
* I added the "login with valid information followed by logout" method in the users_login_test.rb Integration Test.
myapp2/test/integration/users_login_test.rb
test "login with valid information followed by logout" do
get login_path
post login_path, params: { session: { email: @user.email,
password: 'password' } }
assert is_logged_in?
assert_redirected_to @user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(@user), count: 0
end
* Finally, I have completed Chapter 8 in my own CSS style. I ran a test, added all untrackted file, commited the changes, merged to Master branch in Git.
$ rails test
$ git add -A
$ git commit -m "Implement basic login"
$ git checkout master
$ git merge basic-login
* Then, I pushed the project to Github as well. My URL address for myapp2 project in Github is: https://github.com/jimmy2046/myapp2
$ rails test
$ git push
* The screen shot after a user has logged in successfully.
It is nice blog Thank you provide important information and I am searching for the same information to save my time Ruby on Rails Online Training Hyderabad
ReplyDelete