Ch 8: Basic Login Flashcards

(28 cards)

1
Q

Create sessions controller:

A

<code>rails generate controller Sessions new</code>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Clarify sessions routes

A

get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy'

  • routes add a page for a new session, create an action for a new session, and delete a session
  • deletes the unneeded routes generated by rails generate controller
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Edit session test with new route

A
`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`
  • assert_response makes sure the page loaded correctly (could also verify a redirect)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

code the new view for sessions (use layout to render the container)

A
  • Use render layout to put the forms in the card.

<%= 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 %>`

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

give form_for slightly more information because there’s no session model or @user variable

A
  • form_for(@user) allows Rails to infer that the action of the form should be to POST to the URL /users
  • in the case of sessions we need to indicate the name of the resource and the corresponding URL: form_for(:session, url: login_path)

form_for(:session, url: login_path)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

refactor the content from the layout (if your form is inside a bootstrap card etc.)

A
  • Create a form layout container

`

    <%= render 'shared/error_messages' %>        
      <%= title %>

        <%= yield %>          

`` ` ` `
  • Create an if statement in your error_messages partial so it will only render if it’s on the signup page

<% if current_page?(signup_path) %> byebug <% if @user.errors.any? %> <% @user.errors.full_messages.each do |msg| %> &times; <%= msg %> <% end %> <% end %> <% end %>

  • Edit User new and Sessions new to take in the form container layout

<% 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 %>`

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Create the create action

A
  • The User.find pulls the user out of the database using the submitted email address.
  • email addresses are saved as all lower-case, so here we use the downcase method to ensure a match when the submitted address is valid.
  • authenticate method provided by has_secure_password which returns false for an invalid authentication
  • user && user.auth is true only if a user with the given email both exists in the database and has the given password, exactly as required.

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

add flash.now warning to create action

A

renders error messages as a flash object since they don’t belong to Active record because sessions doesn’t have a model

  • since contents of a flash persist for one request and re-rendering a template with render doesn’t count as a request we need to include .now instead of just flash[:danger]

flash.now[:danger] = 'Invalid email/password combination'

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

add destroy action

A

def destroyend

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Create test file for flash message

A

rails generate integration_test users_login

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Write test for error messages

A
  • assert_template Asserts that the request was rendered with the appropriate template file or partials.
  • post to login_path with incorrect credentials
  • make sure the flash isn’t empty
  • the last step is to go to the root_path and make sure the flash doesn’t carry over into another request

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`

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

add SessionsHelper to application controller

A
  • This is so the methods you make there can be used across all controllers

app/controllers/application_controller.rb

` class ApplicationController < ActionController::Base protect_from_forgery with: :exception include SessionsHelper end`

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

create log_in method in sessions_helper

A
  • this goes in the sessions_helper because we want to be able to access it several times.
  • We use the sessions rails instance method (which can be treated like a hash) which places a temporary cookie on the user’s browser containing an encrypted version of the user’s id,
  • We can retrieve the id on subsequent pages using session[:user_id].

# Logs in the given user. def log_in(user) session[:user_id] = user.id end

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

add log_in method and redirect underneath if statement in create controller

A
  • rails assumes going to a model object show page if you just redirect it to the object itself (e.g. redirect_to user)

if user && user.authenticate(params[:session][:password]) log_in user redirect_to user else

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

add current_user method to session helper

A

# Returns the current logged-in user (if any). def current_user @current_user ||= User.find_by(id: session[:user_id]) end

  • if @current_user isn’t defined it just returns the user stored in sessions

add logged_in? method to sessions helper

def logged_in? !current_user.nil? end

  • logged_in? method will be used in the views to determine what is showing based on the logged in status
  • needs the bang added to make sure the current_user is not nil
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Create if statement for a view for logged in members

A

<% if logged_in? %> # Links for logged-in users <% else %> # Links for non-logged-in-users <% end %>

17
Q

add log_out method to the header code that is visible when logged in

A

<%= link_to "Log out", logout_path, method: :delete %>

18
Q

add log_in method to the else statement when there is no user logged in

A

<%= link_to "Log in", login_path %>

19
Q

Create User.digest method to user class (user.rb)

A
  • We won’t necessarily have access to a user object when calculating the digest (as will be the case in the fixtures file), so we’ll attach the digest method to the User class itself, which makes it a class method.

# 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

20
Q

Add user fixture

A
  • Uses the new User.digest method to test the password
  • fixtures support embedded Ruby, which allows us to use <%= User.digest(‘password’) %>
  • adopt the convention that all fixture users have the same password (’password’).

test/fixtures/users.yml michael: name: Michael Example email: michael@example.com password_digest: <%= User.digest('password') %>

21
Q

add setup method to test file to access the signed in user

A

test/integration/users_login_test.rb

` def setup @user = users(:michael) end`

22
Q

add test to users_login_test.rb

A
  • use assert_redirected_to @user to check the right redirect target
  • use follow_redirect! to actually visit the target page.
  • use assert_select “a[href=?]”, login_path, count: 0 to verify that the login link disappears

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

23
Q

add a call to log_in in the Users controller create action

A
  • This logs the user in after they sign up instead of making them put in a new email and password again

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

24
Q

create the is_logged_in method in test_helper because we can’t use the logged_in? method that already exists

A
  • helper methods aren’t available in tests so we can’t use the current_user, but the session method is available, so we’ll use that instead.
  • we use is_logged_in? instead of logged_in? so that the test helper and Sessions helper methods have different names, which prevents them from being mistaken for each other.

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`

25
add a assertion to users\_signup\_test is\_logged\_in?
`test/integration/users_signup_test.rb require 'test_helper'` ` class UsersSignupTest < ActionDispatch::IntegrationTest . . . test "valid signup information" do get signup_path 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 is_logged_in? end end`
26
put the log\_out method in the Sessions helper module
* Use the delete method * Logging out involves undoing the effects of the log\_in method from, which involves deleting the user id from the session. * also set the current user to nil ## Footnote `app/helpers/sessions_helper.rb module SessionsHelper` ` # Logs in the given user. def log_in(user) session[:user_id] = user.id end . . . # Logs out the current user. def log_out session.delete(:user_id) @current_user = nil end end`
27
Add steps to login with valid information on user\_login\_test
* After logging in, we use delete to issue a DELETE request to the logout path and verify that the user is logged out and redirected to the root URL. * Also, check that the login link reappears and that the logout and profile links disappear. * Now that we have is\_logged\_in? available in tests, we’ve also thrown in a bonus assert is\_logged\_in? immediately after posting valid information to the sessions path. ## Footnote `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`
28
Create a class method for making a remember token
`app/models/user.rb# Returns a random token. def User.new_token SecureRandom.urlsafe_base64 end ` * The urlsafe\_base64 method from the SecureRandom module in the Ruby standard library returns a random string of length 22 (for a total of 64 possibilities, thus “base64”) * we’ll be able to use the same token generator to make account activation and password reset links in the future * the new token method doesn’t need a user object, so we’ll make it a class method.