I've been at this for awhile. I am building a simple lottery website and I am generating random tickets. On my local machine random numbers are generated, however, on the server they are duplicated.
I have tried multiple versions of what I have, but the duplicates is the same.
I need to create a random ticket number per ticket and ensure that it hasn't bee created.
This my like 50th version:
a = Account.find(current_account)
numTics = params[:num_tickets].to_i
t = a.tickets.where(:item_id => item.id).count
total = t + numTics
if total > 5
left = 5 - t
flash[:message] = "The total amount of tickets you can purchase per item is five. You can purchase #{left} more tickets."
redirect_to buy_tickets_path(item.id)
else
i = 1
taken = []
random = Random.new
taken.push(random.rand(100.10000000000000))
code = 0
while i <= numTics do
while(true)
code = random.rand(100.10000000000000)
if !taken.include?(code)
taken.push(code)
if Ticket.exists?(:ticket_number => code) == false
a.tickets.build(
:item_id => item.id,
:ticket_number => code
)
a.save
break
end
code = 0
end
end
i = i + 1
end
session['item_name'] = item.name
price = item.price.to_i * 0.05
total = price * numTics
session['amount_due'] = total.to_i
redirect_to confirmation_path
end
You should be using SecureRandom if possible, not Random. It works the same way but is much more random and doesn't need to be initialized like Random does:
SecureRandom.random_number * 100.1
If you're using Ruby 1.8.7 you can try the ActiveSupport::SecureRandom equivalent.
Also if you're generating lottery tickets, you will want to make sure your generator is cryptographically secure. Generating random numbers alone is probably not sufficient. You will likely want to apply some other function to generate these.
Keep in mind that most actual lotteries do not generate random tickets at the point of purchase, but generate large batches in advance and then issue these to purchasers. This means you are able to preview the tickets and ensure they are sufficiently random.
The problem is not with Ruby's pseudo random number generator but that fact that you are creating generators all the time with Random.new. As explained in this answer, you should not have to call Random.new more than once. Store the result in a global object and you'll be good to go.
Related
For example, I have 30 entries, and I wish to divide them so that it only displays 3 of them on each page (and having many pages of course).
Currently, I have implemented it by passing a parameter in the URL and writing Ruby code in the action.
x = params[:id]
if x
#problems = []
x = params[:id].to_i
t = Problem.all.count
i = 1
problem_numbers = -3 * (x - 1)
while t > 0 do
if Problem.exists?(i)
problem_numbers += 1
t -= 1
if problem_numbers > 0
#problems = #problems + [Problem.find(i)]
end
end
if problem_numbers == 3
break
end
i += 1
end
end
It works quite well, but I think this piece of code might a bit complicated for such a feature; also if there are many entries, visiting pages would be slow because I counted through all the entries.
Is there a more convenient way to do this? Thanks in advance.
Displaying items across multiple pages is called pagination. will_paginate and Kaminari are two very popular gems that provide this functionality in a way that is very easily integrated into Ruby on Rails applications.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
For example: If I'm creating a new user in Rails, the id number will be 1, 2, 3 and so on. In the URL the users id will be like "users/1". My question is: How do I make a random number for every new users? So the id won't be 1, 2, 3 and so on. More like id: 643893, name: Carl, age: 34?
If you really insist upon the digits, you could do something like this
def random_id
Random.new.bytes(8).bytes.join[0,8]
end
5.times { puts random_id }
Output
19854517
11802301
16391219
18414719
35583913
Anyway, I believe you have a bit of an XY Problem and your end goal is to just generate unique ids for your users. The function above is going to be random but without checking with your database, you'll have no idea whether the result is unique or not. Here's some alternative approaches I'd take:
If you wanted, it's not 8 digits, but you could slice of the beginning of a uuid
require "securerandom"
SecureRandom.uuid.split("-").first
# b05582d8
That said, I think it's a much better to use the whole thing
SecureRandom.uuid
# b05582d8-3489-4bba-b94f-565ee458b1f9
Since you asked for user ids, I'm going to assume
There should be no collisions in the user ids.
Because of this, you don't want just purely random ids, otherwise you'll end up with a collision as a result of the birthday problem. Out of only 10^8 random userids, this means the odds are significant that a collision will occur after around 10^4 users, and it is always possible it may happen earlier than this.
What you are looking for is a permutation function. Using some basic number theory, here's one simple, non-secure way to do it:
N = 99999999 # set of ordered numbers to permute = {0,1,2,...,N-1}
P = 1000000007 # prime number larger than N
A = 181932213 # random integer: 2 <= A < P
B = 611232645 # random integer: 0 <= B < P
def get_user_id( i )
j = i.instance_of?(Fixnum) ? i : i.dup # i.dup if i is a Bignum
j -= 1
begin j = ( A * j + B ).modulo P end until j < N
j + 1
end
Internally, you still want to generate incremental ids id. Then get_user_id(id) with return an 8-digit number (or less) which you can use as the user_id, which is guaranteed to not conflict with any other user_id for all non-zero 8-digit numbers id.
To illustrate:
1.upto(N) do |i|
j = get_user_id(i)
puts "#{i}\t#{j}"
end
Output:
1 41168842
2 45894710
3 11122313
4 74043578
5 78434518
6 72415977
...
Note that anyone who knows/suspects you are using this algorithm can fairly easily determine what N, P, A, and B are from getting some consecutively generated values, and obtain the underlying id value which you wanted to keep hidden. If you need something more secure than this, then consider looking into permutation ciphers.
Below is the example, which start with alphabet.
def self.generate_reg_no
#reg_no = PatientReg.last
#reg_no.blank? ? (#reg_no = 'REG00') : (#reg_no = #reg_no.reg_no)
new_id =""
n = #reg_no.length
last_count = #reg_no.slice!(4..n)
new_id << "REG0" << (last_count.to_i+1).to_s
end
You can modify as per you requirement.
I want to generate a token in my controller for a user in the "user_info_token" column. However, I want to check that no user currently has that token. Would this code suffice?
begin
#new_token = SecureRandom.urlsafe_base64
user = User.find_by_user_info_token(#new_token)
end while user != nil
#seller.user_info_token = #new_token
Or is there a much cleaner way to do this?
If your token is long enough and generated by a cryptographically secure [pseudo-]random number generator, then you do not need to verify that the token is unique. You do not need to generate tokens in a loop.
16 raw source bytes is long enough for this effective guarantee. When formatted for URL-safety, the result will be longer.
# Base-64 (url-safe) encoded bytes, 22 characters long
SecureRandom.urlsafe_base64(16)
# Base-36 encoded bytes, naturally url-safe, ~25 characters long
SecureRandom.hex(16).to_i(16).to_s(36)
# Base-16 encoded bytes, naturally url-safe, 32 characters long
SecureRandom.hex(16)
This is because the probability that the 16-byte or 128-bit token is nonunique is so vanishingly small that it is virtually zero. There is only a 50% chance of there being any repetitions after approximately 264 = 18,446,744,073,709,551,616 = 1.845 x 1019 tokens have been generated. If you start generating one billion tokens per second, it will take approximately 264/(109*3600*24*365.25) = 600 years until there is a 50% chance of there having occurred any repetitions at all.
But you're not generating one billion tokens per second. Let's be generous and suppose you were generating one token per second. The time frame until a 50% chance of even one collision becomes 600 billion years. The planet will have been swallowed up by the sun long before then.
The cleanest solution I found:
#seller.user_info_token = loop do
token = SecureRandom.urlsafe_base64
break token unless User.exists?(user_info_token: token)
end
And something very clean but with potential duplicates (very few though):
#seller.user_info_token = SecureRandom.uuid
Random UUID probability of duplicates
Edit: of course, add a unique index to your :user_info_token. It will be much quicker to search for a user with the same token and it will raise an exception if by chance, 2 users are saved at the exact same moment with the exact same token!
I have many models I apply unique tokens to. For this reason I've created a Tokened concern in app/models/concerns/tokened.rb
module Tokened
extend ActiveSupport::Concern
included do
after_initialize do
self.token = generate_token if self.token.blank?
end
end
private
def generate_token
loop do
key = SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
break key unless self.class.find_by(token: key)
end
end
end
In any model I want to have unique tokens, I just do
include Tokened
But yes, your code looks fine too.
Rails 5 comes with this feature, you only need to add to your model the next line:
class User
has_secure_token
end
Since Rails 5 is not releases yet you can use the has_secure_token gem. Also you can see my blog post to see more info about it https://coderwall.com/p/kb97gg/secure-tokens-from-rails-5-to-rails-4-x-and-3-x
Maybe you can do something using the actual time. Then you won't need to check if the token was already used by an user.
new_token = Digest::MD5.hexdigest(Time.now.to_i.to_s + rand(999999999).to_s)
user.user_info_token = new_token
You can try some below tricks to get unique token, its so easy which I used in my project -
CREDIT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
def create_credit_key(count = 25)
credit_key = ""
key = CREDIT_CHARS.length
for i in 1..count
rand = Random.rand((0.0)..(1.0))
credit_key += CREDIT_CHARS[(key*rand).to_i].to_s
end
return credit_key
end
Using digest it is again more easy, here I tried to generate without using any algorithm.
I'm in the middle of writing a World of Warcraft addon and I want the addon to be able to perform certain functions based on a key press or a combination of key presses. Most of the key states are protected WoW API functions now but the following are still able to be used by addons:
IsAltKeyDown()
IsControlKeyDown()
IsShiftKeyDown()
What I'd like to be able to do is perform a function based on any one of those keys down or a combination there of.
This is what I've got working:
function KeyCombos()
total = 0
if IsShiftKeyDown() then
total = total + 1
end
if IsControlKeyDown() then
total = total + 2
end
if IsAltKeyDown() then
total = total + 4
end
end
Now my question isn't necessarily about Lua, as the above function is working as I can check if total equals 6 for example to see if Control and Alt are both pressed. My question is more of an algorithmic one. Is there a better way to perform this programmaticly?
If you are going to use a table, in the general case it would be much better to keep the same table.
function KeyCombos()
keys = keys or {}
keys.shift = IsShiftKeyDown()
keys.control = IsControlKeyDown()
keys.alt = IsAltKeyDown()
end
or, if you rather
function KeyCombos()
if not keys then
keys = {}
end
keys.shift = IsShiftKeyDown()
keys.control = IsControlKeyDown()
keys.alt = IsAltKeyDown()
end
The original example in the question, however, is much more performant using integer math.
However these examples all create globals. So:
function GetKeyCombos()
local keys = 0
if IsShiftKeyDown() then
keys = keys + 1
end
if IsControlKeyDown() then
keys = keys + 2
end
if IsAltKeyDown() then
keys = keys + 4
end
return keys
end
would be much better. In WoW all AddOns share the same global environment so its best to keep it clean.
Seems pretty sound to me already.
As an alternative, however, you could use a table:
function KeyCombos()
keys = {
shift = IsShiftKeyDown(),
control = IsControlKeyDown(),
alt = IsAltKeyDown(),
}
end
Then you could call keys.alt or keys["alt"] to see if it's pressed.
More readable? Probably. More efficient? Not likely. But I'm not very knowledgeable when it comes to Lua's performance.
I would just add a comment block above the KeyCombos function stating every possible combination and what it means.
As a side note, it's Lua, not LUA.
I am generating a unique and random alphanumeric string segment to represent certain links that will be generated by the users. For doing that I was approaching with "uuid" number to ensure it's uniqueness and randomness, but, as per my requirements the string shouldn't be more than 5 characters long. So I dropped that idea.
Then I decided to generate such a string using random function of ruby and current time stamp.
The code for my random string goes like this:-
temp=DateTime.now
temp=temp + rand(DateTime.now.to_i)
temp= hash.abs.to_s(36)
What I did is that I stored the current DateTime in a temp variable and then I generated a random number passing the current datetime as parameter. Then in the second line actually added current datetime and random number together to make a unique and random string.
Soon I found,while I was testing my application in two different machines and send the request at the same time, it generated the same string(Though it's rare) once after more than 100 trials.
Now I'm thinking that I should add one more parameter like mac address or client ip address before passing to_s(36) on temp variable. But can't figure out how to do it and even then whether it will be unique or nor...
Thanks....
SecureRandom in ruby uses process id (if available) and current time. You can use the urlsafe_base64(n= 16) class method to generate the sequence you need. According to your requirements I think this is your best bet.
Edit: After a bit of testing, I still think that this approach will generate non-unique keys. The way I solved this problem for barcode generation was:
barcode= barcode_sql_id_hash("#{sql_id}#{keyword}")
Here, your keyword can be time + pid.
If you are certain that you will never need more than a given M amount of unique values, and you don't need more than rudimentary protection against guessing the next generated id, you can use a Linear Congruentual Generator to generate your identificators. All you have to do is remember the last id generated, and use that to generate a new one using the following formula:
newid = (A * oldid + B) mod M
If 2³² distinct id values are enough to suit your needs, try:
def generate_id
if #lcg
#lcg = (1664525 * #lcg + 1013904223) % (2**32)
else
#lcg = rand(2**32) # Random seed
end
end
Now just pick a suitable set of characters to represent the id in as little as 6 character. Uppercase and lowercase letters should do the trick, since (26+26)^6 > 2^32:
ENCODE_CHARS = [*?a..?z, *?A..?Z]
def encode(n)
6.times.map { |i|
n, mod = n.divmod(ENCODE_CHARS.size)
ENCODE_CHARS[mod]
}.join
end
Example:
> 10.times { n = generate_id ; puts "%10d = %s" % [n, encode(n)] }
2574974483 = dyhjOg
3636751446 = QxyuDj
368621501 = bBGvYa
1689949688 = yuTgxe
1457610999 = NqzsRd
3936504298 = MPpusk
133820481 = PQLpsa
2956135596 = yvXpOh
3269402651 = VFUhFi
724653758 = knLfVb
Due to the nature of the LCG, the generated id will not repeat until all 2³² values have been used exactly once each.
There is no way you can generate a unique UUID with only five chars, with chars and numbers you have a basic space of around 56 chars, so there is a max of 56^5 combinations , aprox 551 million (Around 2^29).
If with this scheme you were about to generate 10.000 UUIDs (A very low number of UUIDs) you would have a probability of 1/5.000 of generating a collision.
When using crypto, the standard definition of a big enough space to avert collisions is around 2^80.
To put this into perspective, your algorithm would be better off if it generated just a random integer (a 32 bit uint is 2^32, 8 times the size you are proposing) which is clearly a bad idea.