DEV Community

Saral Karki
Saral Karki

Posted on

Rails! Here I come (Day#5)

I was supposed to come up with this post yesterday, on February 1st, but, even though I worked on the code yesterday, I could not muster the effort to write the post on a Friday evening. I needed some downtime. So here I am on a Saturday afternoon all recharged, and ready to post.

First things first, on day four I experienced real struggle building the authentication for user register and login. I was trying to understand and read the code as I was programming the features from a tutorial. However, there were moments where I had no idea what the tutorial was talking about. I tried to follow the tutorial but to no avail. It was then I thought, I was not understanding the tutorial due to my lack of understanding of Ruby. If I wanted to understand the code altogether, I would need to up my Ruby game. So I signed up for a Ruby course to really start learning Ruby. After listening to one of the podcasts on codenewbie, I found TheOdinProject. This is what I am deciding to stick to as I learn to code in Ruby.

Armed with this insight and having a taken up action, I decided to forge on with the build of the first blog project I was working on. In particular, I wanted to finish the user registration and login page.

Here is what I accomplished and how I did it.

  1. Using gem 'bcrypt'

    Here's what I learnt before starting off building the page. Trying to do everything from the scratch would be like trying to reinvent the wheel, and especially as a beginner who did not have a sound foundation of Ruby, it would be even tougher to begin from scratch. Hence, I used the gem 'bcrypt'. This gem was available in my gemfile, and all I needed to do was uncomment it. Then after running bundle install and restarting my server, I was ready to go. I then modified my user.rb file from my model folder.

user.rb

class User < ApplicationRecord

  has_secure_password
  validates :email, uniqueness: true, presence: true
end

Whilst creating my user database, I had merely run rails g model User email password_digest. As you can see, there is no password and password_confirmation set up right now. Using bcrypt, we are able to add a password and password_confirmation to the user model via the has_secure_password method. Furthermore, the method also checks for validations. I could have handled the validation on my own by writing validation: false, but for now, I did not want to go with that route. I have an understanding that it can be done, but why reinvent the wheel right?

  1. Setting up the routes

routes.rb

Rails.application.routes.draw do
  # Routes for users
  root 'dashboard#show' 
  get 'users/new' => 'users#new', as: 'register_page'
  get 'sessions/new' => 'sessions#new', as:'login'
  get 'sessions/destroy' => 'sessions#destroy', as: "logout"

  resources :sessions, only: [:create, :new, :destroy]
  resources :users, only: [:new]

  #Routes for blog posts


  post 'posts' => 'posts#create'
  get 'posts/new' => 'posts#new', as: 'new_post'
  get 'posts/failure' => 'posts#failure', as: 'failure'
  get 'posts/:id' => 'posts#show' , as: 'post'

end

As seen I have routes for blog posts and users. I chose to create resources for sessions and users. However, rather than creating all the default resources that Rails provide, I chose the resources(actions for controllers) I required. It was suggested that I only get the resources I required rather than everything. The resources I required were for sessions and users. Sessions would allow me to store the user session data, in other words, if a user was signed in or not. Also, it would have the user login page. Users would allow me to create a new user and the create action allowed me to store the user data in the users' database I had created.

  1. Setting up the controller

Next up was setting up the controller for the routes. First up, was the application controller.

application_controller.rb

class ApplicationController < ActionController::Base
    before_action :require_valid_user!

    def current_user
        if !session[:user_id].blank?
        @user ||= User.find(session[:user_id])
        end
    end

    def require_valid_user!
        if current_user.nil?
            flash[: error] = 'You must be logged in to access that page!'
            redirect_to login_path
        end
    end
end

I literally followed the tutorial here but did try to understand what was going on. Here's my reading on. The first line defines before_action. Which is this case is require_valid user. require_valid_user then has been defined. It checks if the current_user is nil or not. If nil, it flashes an error and redirects user to the login_path. The current_user has also be defined. It basically says that if the session user_id is blank, then search the user database and find the session_id. Meaning if the session has a user_id then it gets stored in the instance @user, if not @user returns nil. And if nil, as defined in require_valid_user the user is redirected to login path. With this setup, we move on to the users controller. Here I needed the registration process to take place. A user would register via a registration form, and the data would be saved in the user database.

users_controller.rb

users_controller.rb
class UsersController < ApplicationController

  skip_before_action :require_valid_user!
  before_action :reset_session

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)

    if @user.save
      flash[: success] =  'You have successfully created an account.  Please sign in to continue.'
      redirect_to login_path
    else
      render :new
    end
  end

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

In the users controller, the action new and create have been defined. Remember, we had created new and create using resources in the routes.rb page. Here first we first ask the controller to skip the before_action. Which means users need not be logged in to make a registration. Makes sense, right?

Now, we define an action new. Here we store in an instance @user the values that we get from the form in the registration page. All the validation is done via the bcrypt gem. Next up, we create an action that checks if the user is saved in the database, and if saved the user gets a message and is redirected to a login page, from where the user can log in. Here, the storing process is similar to how we had stored data in for our blog posts.

Once the registration is done, we focus our attention on logging in and logging out via sessions controllers.

sessions_controller.rb

class SessionsController < ApplicationController
  skip_before_action :require_valid_user!, except: [:destroy]

  def new
  end

  def create
    reset_session
    @user = User.find_by(email: session_params[:email])

    if @user && @user.authenticate(session_params[:password])
      session[:user_id] = @user.id
      flash[:success] = 'Welcome back!'
      redirect_to root_path
    else
      flash[:error] = 'Invalid email/password combination'
      redirect_to login_path
    end
  end

  def destroy
    reset_session
  end

  def session_params
    params.require(:session).permit(:email, :password)
  end


end

The session controller handles our login and logout action. The create action is what handles the login. It finds and the user according to the email entered in the login page and it checks a for all the required condition, such as the existence of the user in the database and the password match. The destroy action resets the session and the user is able to log out. Personally, there's still few things for me to unwrap here, especially the session_params and what is going on here. But for now, I do not want to get too stuck on on this, I will be learning more as I go along in my Ruby lessions.

  1. Setting up the view

The views for both registration and login are similar to that of the new post page. They both consists of forms and those forms are submitted to the database. One of the update that I was happy about making was the dynamic navbar, that changed if the user was logged in or if there was no user, and I achieved this by
making changes to the application.html.erb.

<ul class="nav navbar-nav navbar-right">        
         <%  if !session[:user_id]%>
            <li><%= link_to 'Login', login_path , class: 'glyphicon glyphicon-log-in'%></a></li> 
            <li><%= link_to 'Register', register_page_path , class: 'glyphicon glyphicon-user'%></li>             
          <% else %>         
          <li><%= link_to 'Logout', logout_path , class: 'glyphicon glyphicon-log-out' %> <li>

        <% end %>

Finally, the registration and login process was complete, and a pat on the back for me for getting this far. There are still additional features I would like to add, and will continue to add in the days to come. Also, I would have to thank this tutorial that helped me immensely.

Next up, I plan to deploy the app on heroku. I have no idea right now how to do so, but will figure things out as I go along. Something that has already made my stomach twitch a bit is the fact I have used sqlite3 as my database, and apparently, heroku does not support it. So I have to find a way to use Postgresql, and given my limited database knowledge, I am a bit overwhelmed, but we'll figure it out. Patience.

Top comments (2)

Collapse
 
romanegreen profile image
Romane Green • Edited

You'll just have to migrate your sqlite database data when you move to production,via heroku / rails commands and configuraton.Consider the idea that you will have a totally new database in production depending on the method you choose.

Collapse
 
saral profile image
Saral Karki

Thank you. I was able to do so and made deployed my first my to heroku.

pacific-temple-10604.herokuapp.com/

:D