Reputation: 730
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
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
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
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
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