Chantal
Chantal Software developer focused on personal development not only as a coder but as a human being.

How to setup clearance gem like a boss!

How to setup clearance gem like a boss!

How to setup clearance gem like a boss!

What is clearance?

According to the documentation, Clearance is a rails engine for authentication with email & password.

Clearance is intended to be small, simple, and well-tested. Just like Rails, It has opinionated defaults but is intended to be easy to override. It has a ton of features and a huge plus is the integration with Active Job.

Why clearance?

So…? We have Devise already. Why do we need another gem?

Well, Devise is super powerful and extensible, but sometimes it feels like it does way more than what I need. It might be worth the time if your app needs:

  • Multiple user roles
  • Authentication at the rack level and not rails level
  • forgot password, user activation, invalid password lockouts, remember-me tokens, etc.

The source code at times is more complex and you end-up fighting it instead of coding the other/important layers of the app.

Now don’t get me wrong Clearance is no joke, it has great features but it’s kinda waiting for you, you’ll add a command and the extra features will be added whereas with Devise as soon as you setup the gem you sendup with a ton of code that sometimes you are not sure why is there.

If you find yourself fighting with Devise, or you just want a breath of fresh air. Follow along.

Let’s get started

You can add it to your Gemfile with:


$ gem 'clearance'

Run the bundle command:


$ bundle install

You will need a generator:

Run: $ rails g to see the list of generators available to us and find the Clearance section.

Clearance:
  clearance:install
  clearance:routes
  clearance:views

Now that we know the clearance generators, we will run:


  $ rails g clearance:install
*******************************************************************************
# expected output
Next steps:

1. Configure the mailer to create full URLs in emails:

    # config/environments/{development,test}.rb
    config.action_mailer.default_url_options = { host: 'localhost:3000' }

    In production it should be your apps domain name.

2. Display user session and flashes. For example, in your application layout:

    <% if signed_in? %>
      Signed in as: <%= current_user.email %>
      <%= button_to 'Sign out', sign_out_path, method: :delete %>
    <% else %>
      <%= link_to 'Sign in', sign_in_path %>
    <% end %>

    <div id="flash">
      <% flash.each do |key, value| %>
        <div class="flash <%= key %>"><%= value %></div>
      <% end %>
    </div>

3. Migrate:

    rails db:migrate

*******************************************************************************

Following the instructions paste the line below into develoment.rb and test.rb

config.action_mailer.default_url_options = { host: 'localhost:3000' }

Next we add the block of code provided to application.html.erb inside the layouts directory just under the body tag.

<!DOCTYPE html>
<html>
  <head>
    <title>Shoutr</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
  <% if signed_in? %>
    Signed in as: <%= current_user.email %>
    <%= button_to 'Sign out', sign_out_path, method: :delete %>
  <% else %>
    <%= link_to 'Sign in', sign_in_path %>
  <% end %>

  <div id="flash">
    <% flash.each do |key, value| %>
      <div class="flash <%= key %>"><%= value %></div>
    <% end %>
  </div>

  <%= yield %>
  </body>
</html>

Last step but not least run the rake command

$ rake db:migrate

Now you can run rails s and if you go to localhost:3000 you’ll see rails welcome

Now go to localhost:3000/info and you’ll see all the available routes

rails routes page

…but if you go to routes.rb you’ll see nothing

# routes.rb

Rails.application.routes.draw do
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

And this is because as we said before clearance is an engine so all the routes live inside clearance. Isn’t that cool?

Go ahead and visit some of the routes like sign_in or signup on the browser.

Coming from Devise, the first time I saw the empty routes.rb file this came as shock, but as a good dev you check the docs and there it was plain and simple how to deal with routes, which will see later on.

signup email password

From the signup page go ahead and create a user by filling out the form, after you click the signup button you’ll be redirected to root.

You can do a sanity check and run rails c from terminal and run User.last you will see the User that you just created.

Customization

At this point, we can see that it works but how about some customization. A lot of apps have an username field, so let’s do that as well.

Let’s run rails g clearance:views and you’ll see a message in the console saying that the generator wants to change the application.html.erb. In my case I’m gonna say no and just type $ n

Now we have all of the clearance views to our disposal:

        skip  app/views/layouts/application.html.erb
      create  app/views/passwords/create.html.erb
      create  app/views/passwords/edit.html.erb
      create  app/views/passwords/new.html.erb
      create  app/views/sessions/_form.html.erb
      create  app/views/sessions/new.html.erb
      create  app/views/users/_form.html.erb
      create  app/views/users/new.html.erb
      create  config/locales/clearance.en.yml

Let’s visit app/views/_form.html.erb

<div class="text-field">
  <%= form.label :email %>
  <%= form.email_field :email, type: 'email' %>
</div>

<div class="password-field">
  <%= form.label :password %>
  <%= form.password_field :password %>
</div>

and change the form a bit by adding an username field

<div class="text-field">
  <%= form.label :username %>
  <%= form.text_field :username %>
</div>

If you visit the signup page now it will give an error because we don’t have an username field so let’s do a migration to add that.

$ rails g migration AddUsernameToUsers username:string:index

Your migration should look like this:


class AddUsernameToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :username, :string
    add_index :users, :username
  end
end

Now because username should be unique we can add that here at the db level.


class AddUsernameToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :username, :string
    add_index :users, :username, unique: true
  end
end

Now we can run:

rake db:migrate

Since we added db level validations we should also do it at the model level

class User < ApplicationRecord
  include Clearance::User

  validates :username, presence: true, uniqueness: true
end

Now lets visit /sign_up

signup with username

It loads but if we try to create a user it will fail and this is because username is not part of the permitted attributes yet.

Let’s run:


$ rails g clearance:routes

Now we can see all of the routes on our routes.rb file

Rails.application.routes.draw do
  resources :passwords, controller: "clearance/passwords", only: [:create, :new]
  resource :session, controller: "clearance/sessions", only: [:create]

  resources :users, controller: "clearance/users", only: [:create] do
    resource :password,
      controller: "clearance/passwords",
      only: [:create, :edit, :update]
  end

  get "/sign_in" => "clearance/sessions#new", as: "sign_in"
  delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
  get "/sign_up" => "clearance/users#new", as: "sign_up"
end

We are going to be adding our own users controller and for this we need to use our routes.

Edit the file routes.rb like this

[...]
  resources :users, only: [:create] do
    resource :password,
      controller: "clearance/passwords",
      only: [:create, :edit, :update]
  end

# basically just remove controller: "clearance/users" from the block.

Now let’s create a users_controller.rb in our Controllers directory but instead of inheriting from ApplicationController it will inherit from Clearance::UsersController like so:

class UsersController < Clearance::UsersController

end

At this point we can add our own user_params method

class UsersController < Clearance::UsersController

  private

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

Technically, we don’t need email and password BUT I have FOMO for strong params I like to see everything that is coming through, through my params hash.

Now let’s go to the /signup and try it out, create a new user with email, username and password.

Boom! It works! It works!

NOTE: if you haven’t restarted your sever you’ll get an Invalid route name, already in use: Sign in. Don’t fret just restart the server and you are good to go.

Tests?

I didn’t test on this tutorial, but that’s ok! When I’m learning something new I try to focus on the task at hand. I learn the tool first and then I learn how to test that tool. In that way I don’t get overwhelmed as much. We’ll learn how to test on the next post. However here’s a preview:


$ rails g clearance:specs

🥳 Happy Coding!

  • some todo here

  • happy to try

  • [ ]

comments powered by Disqus