Skip to content

Commit

Permalink
Add module for generating driving licences (faker-ruby#1433)
Browse files Browse the repository at this point in the history
  • Loading branch information
jellyfunk authored and vbrazo committed Oct 26, 2018
1 parent 29a6340 commit f2972d7
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
21 changes: 21 additions & 0 deletions doc/unreleased/driving_licence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Faker::DrivingLicence

```ruby
# Generate a licence number in GB format, as issued in England, Scotland and Wales
# The DVSA does not publish their checksum algorithm, so the last 3 characters
# are random
# Optional arguments: last_name, initials, date_of_birth, gender
Faker::DrivingLicence.british_driving_licence #=> "MCDER712081VF7EK"
Faker::DrivingLicence.british_driving_licence(last_name: "O'Carroll",
initials: "J",
gender: :female,
date_of_birth: Date.parse("1986-10-24")) #=> "OCARR815246J91HT"

# Generate a Northern Irish licence number
Faker::DrivingLicence.northern_irish_driving_licence #=> "70702548"

# Generate a UK driving licence number in either GB or NI format, at a rate
# consistent with their relative populations
# Optional arguments: last_name, initials, date_of_birth, gender
Faker::DrivingLicence.uk_driving_licence #=> "OCARR815246J91HT"
Faker::DrivingLicence.uk_driving_licence #=> "70702548"
59 changes: 59 additions & 0 deletions lib/faker/driving_licence.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# frozen_string_literal: true

module Faker
class DrivingLicence < Base
GB_PADDING = '9999'
NI_CHANCE = 0.03 # NI Pop is about 3% of total UK population

class << self
def british_driving_licence(last_name: Faker::Name.last_name,
initials: Faker::Name.initials,
gender: random_gender,
date_of_birth: Faker::Date.birthday(18, 65))
[
gb_licence_padding(last_name, 5),
gb_licence_year(date_of_birth, gender),
gb_licence_padding(initials, 2),
gb_licence_checksum
].join
end

def northern_irish_driving_licence
Faker::Number.number(8)
end

def uk_driving_licence(*args)
if Faker::Config.random.rand < NI_CHANCE
northern_irish_driving_licence
else
british_driving_licence(*args)
end
end

private

def random_gender
%i[male female].sample(random: Faker::Config.random)
end

def gb_licence_padding(str, num_chars)
prepped = str.upcase.gsub(%r{[^A-Z]}, '') + GB_PADDING
prepped[0..(num_chars - 1)]
end

def gb_licence_year(dob, gender)
decade = (dob.year / 10) % 10
year = dob.year % 10
month = gender == :female ? dob.month + 5 : dob.month
# Rubocop's preferred formatting is pretty gory
# rubocop:disable FormatString
"#{decade}#{'%02d' % month}#{'%02d' % dob.day}#{year}"
# rubocop:enable FormatString
end

def gb_licence_checksum
regexify(/[0-9][A-Z][A-Z]/)
end
end
end
end
68 changes: 68 additions & 0 deletions test/test_faker_driving_licence.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

require_relative 'test_helper'

class TestFakerDrivingLicence < Test::Unit::TestCase
def setup
@tester = Faker::DrivingLicence
end

def test_valid_gb_licence
sample = @tester.british_driving_licence
# GB Licence number is 16 characters long
assert_equal 16, sample.length
# First 5 characters are the last_name, right-padded with '9'
assert_match %r{[A-Z][A-Z9]{4}}, sample[0..4]
# Next 6 are digits
assert_match %r{[0-9]{6}}, sample[5..10]
# comprising:
# Single digit decade of birth
assert_includes 0..9, sample[5].to_i
# 2 digit month of birth (add 5 if female)
assert_includes 1..17, sample[6..7].to_i
# 2 digit day of birth
assert_includes 1..31, sample[8..9].to_i
# and least significant digit of birth year
assert_includes 0..9, sample[10].to_i
# Next 2 are first 2 initials of forenames, padded with '9'
assert_match %r{[A-Z][A-Z9]}, sample[11..12]
# Last stanza is a tie-breaker digit + 2 letter checksum
assert_match %r{[0-9][A-Z0-9]{2}}, sample[13..15]
end

def test_valid_northern_irish_licence
sample = @tester.northern_irish_driving_licence
# NI licence is an opaque 8-digit number
assert_equal 8, sample.length
assert_match %r{[0-9]{8}}, sample
end

def test_uk_licence
sample = @tester.uk_driving_licence
assert_includes [8, 16], sample.length
end

def test_british_licence_correctly_mangles_last_name
padded = @tester.british_driving_licence(last_name: 'Judd')
assert_equal 'JUDD9', padded[0..4]
truncated = @tester.british_driving_licence(last_name: 'Hamilton')
assert_match %r{HAMIL[0-9]}, truncated[0..5]
cleaned = @tester.british_driving_licence(last_name: "O'Carroll")
assert_equal 'OCARR', cleaned[0..4]
end

def test_british_licence_correctly_mangles_date_of_birth
date_of_birth = Date.parse('1978-02-13')
male = @tester.british_driving_licence(date_of_birth: date_of_birth, gender: :male)
assert_equal '702138', male[5..10]
female = @tester.british_driving_licence(date_of_birth: date_of_birth, gender: :female)
assert_equal '707138', female[5..10]
end

def test_british_licence_correctly_builds_initials
padded = @tester.british_driving_licence(initials: 'A')
assert_equal 'A9', padded[11..12]
truncated = @tester.british_driving_licence(initials: 'NLTC')
assert_equal 'NL', truncated[11..12]
end
end
1 change: 1 addition & 0 deletions unreleased_CONTENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Contents
- [Faker::Demographic](doc/unreleased/demographic.md)
- [Faker::Dessert](doc/unreleased/dessert.md)
- [Faker::Device](doc/unreleased/device.md)
- [Faker::DrivingLicence](doc/unreleased/driving_licence.md)
- [Faker::DrWho](doc/unreleased/dr_who.md)
- [Faker::DumbAndDumber](doc/unreleased/dumb_and_dumber.md)
- [Faker::Dune](doc/unreleased/dune.md)
Expand Down

0 comments on commit f2972d7

Please sign in to comment.