Feb 142010
 

For Inspectinator, I needed a lightweight token generator to allow people to share their results. I found a great solution, and learned a cool ruby trick, too.

The trick is in Fixnum (and Bignum), #to_s takes an optional argument: a number in (2..36). That number is the base it will print the number in (default is 10).

So, for instance (from the ruby docs), to convert a number to binary:

12345654321.to_s(2)      #=> “1011011111110110111011110000110001″

For my token, I went with an 8-character, base 36 number, which gives me almost 3 trillion possible values (should be plenty). To get the 8 characters, I use rand(36**8). I don’t do any checking on the length, so 8 is actually the max length (and 1 is the min). In practice, though, the vast majority of tokens will be the target length. On my system, I generated 10,000,000 tokens, and 9,722,373 were 8 characters. I chose to accept that as “close enough” because 1) it doesn’t really matter to me if tokens are shorter, and 2) I think the simplicity and elegance of this solution is worth a little uncertainty.

Then, (since I’m using active record), it’s just a matter of adding a before_create to set it up:

def set_token
  t = nil
  begin
    t = rand(36**8).to_s(36)
  end while InspectString.find_by_token(t)

  self.token = t
end

  3 Responses to “Drop-Dead Simple Token Generation — Fixnum#to_s”

  1. 1) I don't think you're going to have as many possible combinations as you think – Random functions are notorious that way. I wouldn't be surprised if Ruby's rand repeats after a few million values, but it is probably still ok for your purposes.

    2) If you need a token, and you are already in Rails, it might be better to use one of the methods in ActiveSupport::SecureRandom – something like this:

    token = ActiveSupport::secureRandom.hex(8)

    would give you what you are looking for.

  2. bokmann — Thanks for the comments.
    1) Good point. Should be able to get by with only a few million values, if that's all I get :)
    2) SecureRandom is a good tip. I'm not in rails and don't want to do ActiveSupport as a dependency but would use that if in rails.

  3. Nice and simple. If you ever do actually care about longer token strings and uniqueness, try this one:

    Digest::SHA1.hexdigest(rand(RAND_SEED).to_s)

    where RAND_SEED is set at application start up:

    RAND_SEED = 4.times.inject(Time.now.to_i){|t, n| t += rand(n)}

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>