dimButTries
dimButTries

Reputation: 878

Accessing a model method from a controller

I have got a login form using the bcrypt gem. Its working as expected in the rails console. However, I get these errors on the site. Any help would be most appreciated.

NoMethodError in ManagersController#loginattempt undefined method `authenticate' for true:TrueClass

I've read a few SO posts - I used, made revisions they do not resolve the error:

For example - it is because the authenticate class doesn't have 'self' prefixed, this suggestion actually breaks the working functionality in the terminal @some_user.authenticate('***********') => true

    def authenticate(password)
        self.password_hash == BCrypt::Engine.hash_secret(password, password_salt)
    end
or

    def self.authenticate(password)
        self.password_hash == BCrypt::Engine.hash_secret(password, password_salt)
    end

or
    def self.authenticate(password)
        password_hash == BCrypt::Engine.hash_secret(password, password_salt)
    end

Model

class Manager < ApplicationRecord
    attr_accessor :password

    validates :username, presence: true, uniqueness: true
    validates :password, presence: true, on: :create


    before_validation(on: :create) do
        encrypt_password
    end

    def authenticate(password)
        password_hash == BCrypt::Engine.hash_secret(password, password_salt)
    end

    private 
        def encrypt_password
            self.password_salt = BCrypt::Engine.generate_salt
            self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
        end
end

Controller - which produces errors.

class ManagersController < ApplicationController
    ...
    def loginattempt
        @manager = Manager.where(manager_params).exists?
        if @manager && @manager.authenticate(manager_params)
            redirect_to managers_path, notice: 'You are logged in.'
        else  
            redirect_to manager_login_path, notice: 'Username or Password incorrect.'
        end   
    end

    private
        def manager_params
            params.require(:manager).permit(:username, :password)
        end
end

For posterity I believe this works now as expected - thank you to the SO community for helping me in my hour of need. Working Controller

    def loginattempt
        @manager = Manager.find_by_username(manager_params[:username])
        if @manager.present? && @manager.authenticate(manager_params[:password])
            redirect_to manager_logged_in_path(@manager), notice: 'Logged In.'
            session[:logged_in] = @manager.id
        else  
            redirect_to manager_login_path, notice: 'Username or Password incorrect.'
        end
    end

Upvotes: 0

Views: 37

Answers (1)

Masafumi Okura
Masafumi Okura

Reputation: 724

A few things to note:

  1. Manager.where(manager_params) is redundant because manager_params is something like {manager: {username: 'foo', password: 'password'}} but where methods takes simple hash like {username: 'foo', password: 'password'}.
  2. You can and should use find_by instead of where and exists? because exists? returns Boolean and this is not what you want. find_by returns nil if the record doesn't exist.
  3. Manager#authenticate method (authenticate instance method on Manager class) takes only password so you can use @manager.authenticate(params[:manager][:password]) instead of @manager.authenticate(manager_params)

I'd like to mention that if username is unique we can find a user by username only, without password.

Upvotes: 1

Related Questions