Masked Identifier
Masked Identifier makes it easy to add a non-incrementing identifier to any ActiveRecord object.
Instead of
http://localhost:3000/users/1
user = User.find(1)
with Masked Identifier you can
http://localhost:3000/users/2xfYgm1pQ8
user = User.find_by_masked_identifier('2xfYgm1pQ8')
Installation
Add gem 'masked-identifier'
to your Rails project Gemfile.
Verified to work with Rails 3.1.0.rc5. Let me know if you use it in older versions of Rails.
Usage
Add a column named 'masked_identifier' to the table of the ActiveRecord object that you want to be able to find using a masked identifier. Also, don't forget to add an index for this new column
def change
add_column :users, :masked_identifier
add_index :users, :masked_identifier
end
Add has_masked_identifier
to the ActiveRecord object
class User < ActiveRecord::Base
has_masked_identifier
end
Create a new object and you'll see that a masked identifier has been added
user = User.create!(name: 'Matt')
user.masked_identifier
=> "2xfYgm1pQ8"
Lookup users based on the object's masked_identifier
property
user = User.find_by_masked_identifier('2xfYgm1pQ8')
user.name
=> "Matt"
If you want User
to automatically return masked_identifier
instead of id
when
to_param
is called, you can override to_param
in the User
class
class User < ActiveRecord::Base
def to_param
self.masked_identifier
end
end
Options
Masked Identifier lets you edit a number of settings by including an options hash following
```ruby
class User < ActiveRecord::Base
has_masked_identifier length: 8, property: 'mid', charset: %w{ a b c d }, attempts: 10
end
Note: This is for example purposes only. You should never use a charset this small. You run the risk of running out of possible unique values and throwing a CodeGenerator::TooManyFailedAttempts error.
length
- the number of characters to include in the masked identifier. Note: As the length is increased, the number of possible unique masked identifiers also increasesproperty
- override the defaultmasked_identifier
property with a custom named property. Don't forget you'll need to use this custom name in your database migration toocharset
- override the default character set used when generating masked identifiers (default character set includes a-z, A-Z, and 0-9). Note: As the size of the character set is increased, the number of possible unique masked identifiers also increasesattempts
- override the number of attempts to identifiy a unique masked identifier. Each 'attempt' requires one SELECT query on the database. If you have a sufficiently large charset and length >=10 chars, your app should rarely require more than one attempt
Exceptions
Make sure you handle the following possible exceptions in your application:
[YourActiveRecordObject]::InvalidProperty
- if object does not have amasked_identifier
property (or the custom property passed to theproperty
option does not exist)CodeGenerator::TooManyFailedAttempts
- if unable to find a unique code to use as a masked identifierCodeGenerator::InvalidAttemptsValue
- if the value provided to theattempts
option is not an Integer or < 1CodeGenerator::InvalidCodeLength
- if the value provided to thelength
option is not anInteger
or is < 1CodeGenerator::InvalidCharset
- if value provided to thecharset
is not anArray
with a size >= 1