How To Use HashID's to Change autoincreamented id in Rails

in #rails8 years ago

How to use Hash ID’s in your URL in Ruby on Rails 5

When I am building a Rails app that I expect to be public facing in some sort of capacity, I don’t want to be displaying auto-incrementing, integer based IDs in my URLs. Not only does it visually look better, in my opinion. It is a security enhancement by removing the predictability of record discovery via the URL.

Though, as ingrained as auto-incremented integer primary keys are in ActiveRecord, I wouldn’t want to rock the boat too much. Some suggest using UUIDs but I find them to be too long for URLs in my taste. So, I set out on how to use Hashes as IDs in my URL and would like to share that with you.
This tutorial is going to assume you already have existing models, however, you will simply just need to add a hash_id:string column to your new models. Additionally, you will need to install the friendly_id gem.

Gemfile

gem 'friendly_id', '~> 5.1.0'

…and of course
bundle install
Perfect, we have the gem needed to do the hashed ID lookups on our models. Next, we need to include the functionality into our models. I chose to create a model concern that will be able to be included in any model we want the Hashed ID functionality on…

app/models/concerns/friendlyable.rb

module Friendlyable
extend ActiveSupport::Concern
included do
extend ::FriendlyId
before_create :set_hash_id
friendly_id :hash_id
end
def set_hash_id
hash_id = nil
loop do
hash_id = SecureRandom.urlsafe_base64(9).gsub(/-|_/,('a'..'z').to_a[rand(26)])
break unless self.class.name.constantize.where(:hash_id => hash_id).exists?
end
self.hash_id = hash_id
end
end

Let’s go over that file. The first two lines are standard model concern boilerplate.

included do
extend ::FriendlyId
before_create :set_hash_id
friendly_id :hash_id
end
The include block is basically allowing you to insert lines into your model file as if you were to put them there manually. Thus, we’re adding FriendlyId functionality, adding a before_create callback, and lastly telling FriendlyId we’re using the hash_id column has the FriendlyId lookup column.
def set_hash_id
hash_id = nil
loop do
hash_id = SecureRandom.urlsafe_base64(9).gsub(/-|_/,('a'..'z').to_a[rand(26)])
break unless self.class.name.constantize.where(:hash_id => hash_id).exists?
end
self.hash_id = hash_id
end
In the second half of the file, we’re defining the method called from the before_create hook. Essentially, we’re creating a loop to create a URL safe hash, and set the hash_id attribute if that hash_id does not collide with any existing records. If it’s successful, we break the loop and set the attribute for ActiveRecord to save.
Now that we have the model concern ready to go, it’s really easy to include it in a model:
class User < ApplicationRecord
include Friendlyable
...
end
…and that’s it! Let’s add the migration to finish this off.
class AddHashIdToUsers < ActiveRecord::Migration[5.0]
def up
add_column :users, :hash_id, :string, index: true
User.all.each{|m| m.set_hash_id; m.save}
end
def down
remove_column :users, :hash_id, :string
end
end

The migration will add the column, make it indexed, and then update any existing records to have a hash_id.
The last piece here will be looking up records via FriendlyId, which is a simple update to any finds in your app:
User.friendly.find(params[:id])
…which will use the primary ‘id’ key or your ‘hash_id’ to look up records. There you have it! From this point forward you can be using URLs like http://localhost:3000/users/90upoijsz in your Rails application.

Sort:  

Congratulations @kizzonia! You have completed some achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes

Click on any badge to view your own Board of Honnor on SteemitBoard.
For more information about SteemitBoard, click here

If you no longer want to receive notifications, reply to this comment with the word STOP

By upvoting this notification, you can help all Steemit users. Learn how here!

Coin Marketplace

STEEM 0.27
TRX 0.26
JST 0.039
BTC 94467.52
ETH 3348.45
USDT 1.00
SBD 3.35