Harakiri
Harakiri

Reputation: 730

Rails: Generate a Hash

I'm trying to find a way to get this done:

-> Every time when a "Sale" gets saved to the DB rails has to generate a hash (not longer than 250 characters) and save the value to the column "token".

I've searched a lot but nothing met my requirements. Do you have any ideas?

Thanks in advance!

Upvotes: 1

Views: 4364

Answers (4)

Mapad
Mapad

Reputation: 8527

Building on top of richfisher answer:

class Sale < ActiveRecord::Base
  before_save :generate_token
  validates :token, uniqueness: true
  def generate_token(length=50)
    self.token = SecureRandom.urlsafe_base64(length, false)
  end
end

uniqueness can be ensured through a validation.

Edit The validation above is useless and doesn't solve the problem when the generated token is not unique. A loop is needed (like what joshua.paling proposed), or better use has_secure_token (proposed by Mike Desjardins) which implements something similar. On top you’re encouraged to add a unique index in the database to avoid race conditions:

class Sale < ActiveRecord::Base
  before_create :generate_token
  def generate_token(length=50)
    loop do
      token = self.token = SecureRandom.urlsafe_base64(length, false)
      break token unless self.class.exists?(token: token)
    end
  end
end

Edited "validates"

Upvotes: 1

Mike Desjardins
Mike Desjardins

Reputation: 460

Adding to everyone else's answer... Rails 5 has a has_secure_token method that you can simply add to your ActiveRecord class, e.g.,

class User < ActiveRecord::Base
  has_secure_token
end

This method basically does the dirty work that everyone else is suggesting you do. If you're not on Rails 5 yet, there's a Rails 4 backport available:

https://github.com/robertomiranda/has_secure_token

Upvotes: 2

richfisher
richfisher

Reputation: 951

class Sale < ActiveRecord::Base
  before_save :generate_token
  def generate_token(length=250)
    self.token = [*('A'..'Z'),*('a'..'z'),*('0'..'9')].sample(length).join
  end
end

you can control the length and character.

Upvotes: 1

joshua.paling
joshua.paling

Reputation: 13952

My go-to for this kinda thing is SecureRandom.hex. It takes a parameter indicating length - eg. SecureRandom.hex(250) in your case. It's hex - so uses digits, then the letters a-f.

And I imagine you also want it to be unique amongst all records in your database? If so, you'll want something like this in your model:

def generate_unique_token
  loop do
    token = SecureRandom.hex
    break token unless self.class.exists?(token: token)
  end
end

Upvotes: 3

Related Questions