Sometimes you don’t want to reveal the real id of the object in the system to the user for some reason. For example, you don’t want to reveal the number of items in the system, or let the user view all of them by simply changing the id in the address.
The most obvious and easy way to do it is to generate a fake id for the object and use it instead. Lets say we have a model named Foo and you want it to have a fake id. Here’s the code for it:
class Foo < ActiveRecord::Base before_validation_on_create :generate_fake_id def to_param fake_id end protected def generate_fake_id string = random_string while Foo.find_by_fake_id(string) string = random_string end self.fake_id = string end def random_string(size = 8) chars = (('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a) (1..size).collect{|a| chars[rand(chars.size)] }.join end end
Now, when you create a Foo with for example “Foo.create(:name => ‘first foo’)”, the model generates a random string of 8 characters and stores it in fake_id. The fake id is only generated once, on creation, so it’s not changed later on. Now, for the helpers. Suppose you have map.resources :foos in your routes.rb. The helper you user before, foo_path(@foo) now generates a url with the fake id (foos/Hd45jdg3), because of the to_param method. All you have to do, is to change your foos_controller this way:
class FoosController < ApplicationController def show @foo = Foo.find_by_fake_id(params[:id]) end end
The uniqueness of the fake_id is guarantied by the Foo.find_by_fake_id call in the generate_fake_id method. Yes, it does add an extra query to the database, but it’s not really impaction performance much unless you are generating a lot of objects all of the time. You can change the way the fake id looks like by modifying the array used in random_string. For example if you want to eliminate symbols that look much alike you can have it this way:
def random_string(size = 8) chars = (('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a) - %w(i o 0 1 l O) (1..size).collect{|a| chars[rand(chars.size)] }.join end
And also: don’t forget to add the fake_id field to the table in the database.