简体   繁体   中英

NoMethodError: undefined method `remember' for #<User:0x0000000a0c5698>

I warmly welcome. I recently started learning RoR. Currently processed course Michael Hartl. I got to the lesson 9 - cookies. Unfortunately, I received an error that I can not fix. Please help.

Error:

ERROR["test_login_with_valid_information_followed_by_logout", UserLoginTest, 1.443278428982012]
 test_login_with_valid_information_followed_by_logout#UserLoginTest (1.44s)
NoMethodError:         NoMethodError: undefined method `remember' for #<User:0x0000000a0c5698>
        Did you mean?  remember_token
            app/helpers/sessions_helper.rb:8:in `remember'
            app/controllers/sessions_controller.rb:10:in `create'
            test/integration/user_login_test.rb:23:in `block in <class:UserLoginTest>'

My files:

User.rb

class User < ApplicationRecord
  attr_accessor :remember_token

  before_save { email.downcase! }
  validates :name,  presence: true, length: {maximum: 50}
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[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}

  class << self 

    # Returns the hash digest of the given string.
    def digest(string)
      cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                              BCrypt::Engine.cost
      BCrypt::Password.create(string, cost: cost)
    end

    # Returns a random token.
    def new_token
      SecureRandom.urlsafe_base64
    end

    def remember
      remember_token = new_token
      update_attribute(:remember_digest, digest(remember_token))
    end

    # Returns true if the given token matches the digest.
    def authenticated?(remember_token)
      BCrypt::Password.new(remember_digest).is_password?(remember_token)
    end
  end
end

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
      remember user
      redirect_to user
    else
      flash.now[:danger] = "Invalid email/password combination"
      render 'new'
    end
  end

  def destroy
    log_out
    redirect_to root_path
  end
end

Sessions_helper.rb

    module SessionsHelper
      def log_in(user)
        session[:user_id] = user.id
      end

      # Remembers a user in a persistent session.
      def remember(user)
        user.remember
        cookies.permanent.signed[:user_id] = user.id
        cookies.permanent[:remember_token] = user.remember_token
      end

      def current_user
        if (user_id = session[:user_id])
          current_user ||= User.find_by(id: user_id)
        elsif ( user_id = cookies.signed[:user_id] )
          user = User.find_by(id: user_id )
          if user && user.authenticated?(cookies[:remember_token])
            log_in user
            @current_user = user
          end
        end
      end

      def logged_in?
        !current_user.nil?
      end

      def log_out
        session.delete(:user_id)
        @current_user = nil
      end
    end

**user_login_test.rb**

require 'test_helper'

class UserLoginTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:marcin)
  end

  test "user invalid login" do
    get login_path
    assert_template 'sessions/new'
    post login_path, params: { session: { email: "", password: "" } }
    assert_template 'sessions/new'
    assert_not flash.empty?
    assert flash.any?
    get root_path
    assert_not flash.any?
  end

  test "login with valid information followed by logout" do
    get login_path
    assert_template 'sessions/new'
    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_path
    follow_redirect!
    skip
    assert_select "a[href=?]", login_path
    assert_select "a[href=?]", logout_path, count: 0
    assert_select "a[href=?]", user_path(@user), count: 0
  end

end

The problem is that you have wrapped the remember method within class << self , making it a class method of User instead of an instance method.

Remove the class << self and just put self in front of the methods you want to be class methods. Or you can move remember so it's outside the class << self .

....
has_secure_password
validates :password,  presence: true, length: {minimum: 6}

def remember
  remember_token = new_token
  update_attribute(:remember_digest, digest(remember_token))
end
...

This should solve your problem, Just make the method remember an instance method by moving it out of the class << self declaration.

# frozen_string_literal: true
class User < ApplicationRecord
  attr_accessor :remember_token

  before_save { email.downcase! }
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[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 }

  def remember
    remember_token = new_token
    update_attribute(:remember_digest, digest(remember_token))
  end

  class << self
  # Returns the hash digest of the given string.
  def digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end

  # Returns a random token.
  def new_token
    SecureRandom.urlsafe_base64
  end

  # Returns true if the given token matches the digest.
  def authenticated?(remember_token)
    BCrypt::Password.new(remember_digest).is_password?(remember_token)
  end
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM