Dotenv : Are the environment variables not loading in the `production` environment of your Rails application?

You have configured your .env for all common environment variables across environments but they are not loading in production environment.

Here is a catch in the intallation doc.

could not fetch specs from https://rubygems.org/

Here you can see that the document mentions to install the gem only in development and test environments and you just copied and pasted as is in your Gemfile !

The fix is simple. Just remove groups: [:development, :test] or put the dotenv-rails gem outside of all environment like ohter gems required in all environment.

CanCanCan : Use differnt column to `load_and_authorize_resource` in inherited controllers

Recently I came across a situation where there was a need to use differnt column for load_and_authorize_resource in an inherited controller.

By default CanCanCan, use id to find resources in load_and_authorize_resource

If you want to use different column, you can use :find_by option:

load_and_authorize_resource :find_by => :slug

But this will apply on all the controller inheriting it.

# app/controllers/application_controller.rb

class ApplicationController < ActionController::Base

  load_and_authorize_resource

end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController

  # for this controller, resource will be load using `id` 

end

If you try to put load_and_authorize_resource :find_by => :slug in posts_controller.rb hoping it will override the behaviour then you are wrong. It doesn’t work.

To make it working, you need to use :prepend option.

# app/controllers/posts_controller.rb
class PostsController < ApplicationController

  load_and_authorize_resource :find_by => :slug, prepend: true

end

Hope this helps!

How to install JetBrains Mono on Ubuntu and enable in Sublime?

JetBrains recently released JetBrains Mono fonts for free.

It is very friendly to developers as the font has,

  • Increased height and letter spacing for a better reading experience(study says we spend 80%+ time on reading code)

without ligatures

  • Code-specific ligatures merges multiple symbols helps eyes to process less

Without Ligature without ligatures

With Ligaturewithout ligatures

  • And it’s free & open source

How install in Ubuntu?

Download and Install

Option 1

Use browser to download zip from Jetbrains Mono page and unzip it in ~/.local/share/fonts/ to use it for specific user or /usr/share/fonts for system wide use.

Option 2

Use command line

$ wget https://download.jetbrains.com/fonts/JetBrainsMono-2.001.zip
$ unzip JetBrainsMono-2.001.zip

To use it for specific user:

$ mv JetBrainsMono-*.ttf ~/.local/share/fonts/

To use it sysem-wide:

$ sudo mv JetBrainsMono-*.ttf /usr/share/fonts/

How to enable in Sublime Text?

Download latest version from here

In sublime,

  1. Open Preferences > Settings
  2. Add "font_face": "JetBrains Mono" on the User’s pref
  3. Save (Ctrl+S)

without ligatures

Thant’s it!

How to fix `DEPRECATION WARNING: Dangerous query method` on `pluck`?

Suddenly I noticed a peculiar deprecation warning on .pluck in log everywhere.

DEPRECATION WARNING: Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): [:client_id, :duns, :legal_business_name]. Non-attribute arguments will be disallowed in Rails 6.1. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql()

Here is a simplified snippet which was generating the warning:

ERRORED_RECORDS_FIELDS = %i(client_id duns legal_business_name)
...
...
Client.pluck(ERRORED_RECORDS_FIELDS)

However, the result returns just fine. Reading a bit around the warning I relized that I have been passing array if fields which at first point looks ok but it’s not.

So instead of passing array, splat the array so that all elements are received as separate column value in the args of the pluck method.

So following fixes the issue.

Client.pluck(*ERRORED_RECORDS_FIELDS)

Thanks!

How to allow users to sign in using mobile number with Devise and Rails?

Most people in the world who use smartphones don’t own/use email. However they know how to operate the mobile and apps.

What if you need to build an app for such users who want to access your app and it requires some kind of authentication mechanism?

If you are building a web application using rails, the natural choice for authentication would be Devise. Conventionally Device’s promotes email to authenticate users. Replacing email with mobile as authentication key needs some tweaks in configurations.

Prerequisite

Ensure you have configured root in your application inside config/routes.rb. You can skip this section and jump to Install Devise step

If you just created a new rails app, let us create a simple HomeController with index action for demo purpose. Run following from command line

rails g controller Home index

Then change config/routes.rb and configure root

root 'home#index'

Install Devise

You can skip this step if you have already set up the Device.

  1. Modify Gemfile Add gem 'devise' to your Gemfile and run bundle install from the command line

  2. Run Devise Generator Next, run rails generate devise:install from the command line

Follow instructions appear in the console to complete configuration.

Create Model

Create a model for authentication passing mobile as additional field

rails generate devise User mobile

Modify Generated Migration

Devise is going to use mobile to find matching user in the app so we should add index to mobile to make searching faster. This command creates User model, creates a migration for it and changes routes.rb to add Devise specific routes.

Open the generated migration and make following changes

  1. Mark mobile field as not null
t.string :mobile, null: false

Add following line below the the index for email

add_index :users, :mobile,               unique: true

Run Migration

Run rails db:migrate from the command line to create users table.

Enable Authentication

Assuming you want to protect all views/controllers, add following to enable authentication for all controllers in ApplicationController

before_action :authenticate_user!

Now start rails server and hit http://localhost:3000 to verify the setup. You should get a login page.

Override Devise’s Default Views

Devise provides default views for Sign In, Sign Up, Reset Password etc. All those views are configured to use email which we need to replace with mobile.

  1. Copy views from Devise to your app
    Run rails generate devise:views from command line

  2. Replace email with mobile in Sign In and Sign Up devise views
    Open app/views/devise/sessions/new.html.erb and app/views/devise/registrations/new.html.erb to replace email with mobile
    Also, replace email_filed with telephone_field. The updated configuration will look like in both views

<div class="field">
 <%= f.label :mobile %><br />
 <%= f.telephone_field :mobile, autofocus: true, autocomplete: "mobile" %>
</div>

Whitelist mobile param via Strong parameters

Devise controllers santize parameters to ensure only require parameters are sent and rest are ignored. We added mobile field which is not in the whitelist provided by Deviase. We need to add it to ensure it is allowed to use by Devise. Here is how it looks like:

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :authenticate_user!

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:mobile])
    devise_parameter_sanitizer.permit(:sign_in, keys: [:mobile])
  end
end

Check Whitelisting Custom Fields for more details.

Override Devise’s Default Model Configuration

Add following to your User model to avoid email as authentication key

validates :email, uniqueness: true
validates :mobile, uniqueness: true

# Search user by mobile(not email)
def self.find_first_by_auth_conditions(warden_conditions)
  conditions = warden_conditions.dup
  where(mobile: conditions[:mobile]).first
end

# Stop using email as authentication key
def email_required?
  false
end

def email_changed?
  false
end

def will_save_change_to_email?
  false
end

Refer How To: Allow users to sign in using their username or email address For more details

Change Initializer to support mobile as authentication key

Open config/initializers/devise.rb and ensure following configuration:

config.authentication_keys = [:mobile]
config.case_insensitive_keys = [:mobile]
config.strip_whitespace_keys = [:mobile]

And….That’s it. Now restart the rails server and we are done!

You can find the working application here on Github and take a look at commits.

P.S. You can use any custom field instead of mobile.