0

I am new to rails and am following Hartl's rails tutorial. I'm trying to set up the user login form but am facing this error upon submission of valid credentials:-

undefined method `[]' for nil:NilClass

    Extracted source (around line #7):

   6   def create
   7     user = User.find_by(email: params[:session][:email].downcase)
   8     if user && user.authenticate(params[:session][:password])
   9       log_in user
  10       redirect_to user

It is coming from this file which is trying to create a session:-

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

I tried to debug a little and found that params[:session] returns nil. I confirmed it using byebug. I changed [:session] to [:sessions] and the login form submitted successfully, which was weird because in the tutorial it is just [:session]. But the other weird thing is that when I changed this, my rails tests failed showing the following 2 errors:-

ERROR["test_login_with_invalid_information", UsersLoginTest, 0.8645880669937469]
 test_login_with_invalid_information#UsersLoginTest (0.86s)
NoMethodError:         NoMethodError: undefined method `[]' for nil:NilClass
            app/controllers/sessions_controller.rb:7:in `create'
            test/integration/users_login_test.rb:13:in `block in <class:UsersLoginTest>'

ERROR["test_login_with_valid_information", UsersLoginTest, 0.887510354994447]
 test_login_with_valid_information#UsersLoginTest (0.89s)
NoMethodError:         NoMethodError: undefined method `[]' for nil:NilClass
            app/controllers/sessions_controller.rb:7:in `create'
            test/integration/users_login_test.rb:22:in `block in <class:UsersLoginTest>'

It's from this test:-

require 'test_helper'

class UsersLoginTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  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

This is really odd.

In summary, I am stuck between getting the form to submit successfully on localhost by changing [:session] to [:sessions] and having my rails test fail or leave it at [:session] and have my tests pass and the actual form fail to submit.

I don't know how to process this error. Would love to have some light thrown on it.

Update:-

I'm using Rails 5.0.2 (but following the tutorial written using 5.0.1). (update - I just tried downgrading to 5.0.1 and it doesn't make a difference).

The users controller with the create action:-

class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
  @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Welcome to your profile page!"
      redirect_to @user
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                    :password_confirmation)
    end

end

I used (byebug) near the code that's giving me trouble and got this when I entered "params" in the terminal:-

 <ActionController::Parameters {"utf8"=>"✓", 
"authenticity_token"=>"CZUudm++VEWSxWUUxpDD5xI+oTezbzVE9lcX5Up5P5UAwl2zG+p
Oic5e6Sh08Vu2iyvQ0TAlHJSEuA+j/+C7jA==", "sessions"=>
<ActionController::Parameters {"email"=>"dude@email.com", 
"password"=>"password"} permitted: false>, "commit"=>"Log in", 
"controller"=>"sessions", "action"=>"create"} permitted: false>
Ragav Y
  • 1,662
  • 1
  • 18
  • 32
  • 1
    "I am stuck between" - there is a third way. You know, making your tests mimic the __actual__ data being sent (`:sessions`) – Sergio Tulentsev May 08 '17 at 14:42
  • Also, show what `params` looks like in the create action – Sergio Tulentsev May 08 '17 at 14:43
  • Add the Rails version you're using. – Sebastián Palma May 08 '17 at 14:47
  • I'm using Rails 5.0.2. My problem is not just the conflict between the test and the results on browser, I want to know what is going on so that I can get a handle on what's causing this issue. I'm following the tutorial as described and the code is pretty much copy paste (except that he's using 5.0.1 and I'm using 5.0.2, and I don't know if it could be an issue). Also I just updated the details with the code containing the create action. – Ragav Y May 09 '17 at 04:25

2 Answers2

0

You are not sending the params correctly

post login_path, params: { session: { email: "", password: "" } }

to

post login_path, { session: { email: "", password: "" } }
Deepak Mahakale
  • 22,834
  • 10
  • 68
  • 88
0

Figured it out!

After messing around a bit I found that I had used ":sessions" in the form_for tag in the view file here:-

<% provide(:title, "Log in") %>
<h1>Log in</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(:sessions, url: login_path) do |f| %>

I changed that to :session and that fixed the problem.

Ragav Y
  • 1,662
  • 1
  • 18
  • 32