Create sessions controller:
<code>rails generate controller Sessions new</code>
Clarify sessions routes
get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy'
Edit session test with new route
`test/controllers/sessions_controller_test.rb ` `require 'test_helper'class SessionsControllerTest < ActionDispatch::IntegrationTest test "should get new" do get login_path assert_response :success endend`
code the new view for sessions (use layout to render the container)
<%= render :layout => 'shared/card_container', :locals => {:title => 'Log in'} do %> <%= 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 %> <% end %>`
give form_for slightly more information because there’s no session model or @user variable
form_for(:session, url: login_path)
refactor the content from the layout (if your form is inside a bootstrap card etc.)
`
<%= render 'shared/error_messages' %>
<%= title %>
<%= yield %>
`` ` ` `<% if current_page?(signup_path) %> byebug <% if @user.errors.any? %>
<% @user.errors.full_messages.each do |msg| %>
× <%= msg %>
<% end %>
<% end %> <% end %>
<% provide(:title, 'Sign Up') %>
` <%= render :layout => ‘shared/card_container’, :locals => {:title => ‘Sign Up’} do %> <%= form_for(@user, url: signup_path) do |f| %> <%= f.label :name %> <%= f.text_field :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 %> <% end %>`
Create the create action
def create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) # Log the user in and redirect to the user's show page. else # display flash warning render 'new' end end
add flash.now warning to create action
renders error messages as a flash object since they don’t belong to Active record because sessions doesn’t have a model
flash.now[:danger] = 'Invalid email/password combination'
add destroy action
def destroyend
Create test file for flash message
rails generate integration_test users_login
Write test for error messages
require 'test_helper'
` class UsersLoginTest < ActionDispatch::IntegrationTest`
` test “Error message for wrong login” do get login_path assert_template ‘sessions/new’ post login_path, params: {session: {email: “user@invalid”, password: “foo”}} assert_template ‘sessions/new’ assert_not flash.empty? get root_path assert flash.empty? end end`
add SessionsHelper to application controller
app/controllers/application_controller.rb
` class ApplicationController < ActionController::Base protect_from_forgery with: :exception include SessionsHelper end`
create log_in method in sessions_helper
# Logs in the given user. def log_in(user) session[:user_id] = user.id end
add log_in method and redirect underneath if statement in create controller
if user && user.authenticate(params[:session][:password]) log_in user redirect_to user else
add current_user method to session helper
# Returns the current logged-in user (if any). def current_user @current_user ||= User.find_by(id: session[:user_id]) end
add logged_in? method to sessions helper
def logged_in? !current_user.nil? end
Create if statement for a view for logged in members
<% if logged_in? %> # Links for logged-in users <% else %> # Links for non-logged-in-users <% end %>
add log_out method to the header code that is visible when logged in
<%= link_to "Log out", logout_path, method: :delete %>
add log_in method to the else statement when there is no user logged in
<%= link_to "Log in", login_path %>
Create User.digest method to user class (user.rb)
# 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
Add user fixture
test/fixtures/users.yml michael: name: Michael Example email: michael@example.com password_digest: <%= User.digest('password') %>
add setup method to test file to access the signed in user
test/integration/users_login_test.rb
` def setup @user = users(:michael) end`
add test to users_login_test.rb
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
add a call to log_in in the Users controller create action
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
create the is_logged_in method in test_helper because we can’t use the logged_in? method that already exists
test/test_helper.rb
` class ActiveSupport::TestCase fixtures :all`
` # Returns true if a test user is logged in. def is_logged_in? !session[:user_id].nil? end end`