Module: BrazilianUtils::LicensePlateUtils

Defined in:
lib/brazilian-utils/license-plate-utils.rb

Overview

Utilities for formatting, validating, and generating Brazilian license plates.

Brazilian license plates come in two formats:

  • Old format: LLLNNNN (3 letters + 4 numbers) e.g., "ABC-1234"
  • Mercosul format: LLLNLNN (3 letters + 1 number + 1 letter + 2 numbers) e.g., "ABC1D23"

The Mercosul format was introduced in 2018 as part of a standardization effort across Mercosul countries.

Constant Summary collapse

OLD_FORMAT_PATTERN =

Pattern for old format license plates (LLLNNNN)

/^[A-Za-z]{3}[0-9]{4}$/.freeze
MERCOSUL_PATTERN =

Pattern for Mercosul format license plates (LLLNLNN)

/^[A-Z]{3}\d[A-Z]\d{2}$/.freeze

Class Method Summary collapse

Class Method Details

.convert_to_mercosul(license_plate) ⇒ String?

Converts an old pattern license plate (LLLNNNN) to Mercosul format (LLLNLNN).

The conversion replaces the first digit (position 4) with its corresponding letter (0→A, 1→B, 2→C, ..., 9→J).

Examples:

convert_to_mercosul("ABC1234")
#=> "ABC1C34"

convert_to_mercosul("ABC4567")
#=> "ABC4F67"

convert_to_mercosul("ABC-1234")
#=> "ABC1C34"

convert_to_mercosul("ABC4*67")
#=> ""

Parameters:

  • license_plate (String) —

    A string representing the old pattern license plate

Returns:

  • (String, nil) —

    The converted Mercosul license plate (LLLNLNN) or nil if invalid



38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/brazilian-utils/license-plate-utils.rb', line 38

def self.convert_to_mercosul(license_plate)
  return '' unless license_plate.is_a?(String)

  clean = remove_symbols(license_plate).upcase
  return '' unless valid_old_format?(clean)

  chars = clean.chars

  # Convert the 5th character (index 4) - the first digit after the letters
  # 0→A, 1→B, 2→C, etc.
  chars[4] = ('A'.ord + chars[4].to_i).chr

  chars.join
end

.format_license_plate(license_plate) ⇒ String? Also known as: format

Formats a license plate into the correct pattern.

This function receives a license plate in any pattern (LLLNNNN or LLLNLNN) and returns a formatted version:

  • Old format: adds dash (ABC-1234)
  • Mercosul format: uppercase without dash (ABC1D34)

Examples:

format_license_plate("ABC1234")
#=> "ABC-1234" (old format with dash)

format_license_plate("abc1e34")
#=> "ABC1E34" (Mercosul format, uppercase)

format_license_plate("ABC-1234")
#=> "ABC-1234" (already formatted old format)

format_license_plate("ABC123")
#=> nil (invalid)

Parameters:

  • license_plate (String) —

    A license plate string

Returns:

  • (String, nil) —

    The formatted license plate string or nil if invalid



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/brazilian-utils/license-plate-utils.rb', line 76

def self.format_license_plate(license_plate)
  return nil unless license_plate.is_a?(String)

  clean = remove_symbols(license_plate).upcase

  if valid_old_format?(clean)
    # Old format: add dash after 3rd character
    "#{clean[0..2]}-#{clean[3..-1]}"
  elsif valid_mercosul?(clean)
    # Mercosul format: just uppercase, no dash
    clean
  else
    nil
  end
end

.generate(format = 'LLLNLNN') ⇒ String?

Generates a valid license plate in the given format.

In case no format is provided, it will return a license plate in the Mercosul format.

Examples:

generate
#=> "ABC1D23" (Mercosul format by default)

generate('LLLNLNN')
#=> "XYZ2E45" (Mercosul format)

generate('LLLNNNN')
#=> "ABC1234" (old format)

generate('invalid')
#=> nil

Parameters:

  • format (String) (defaults to: 'LLLNLNN') —

    The desired format for the license plate. 'LLLNNNN' for the old pattern or 'LLLNLNN' for the Mercosul one. Default is 'LLLNLNN'

Returns:

  • (String, nil) —

    A randomly generated license plate number or nil if format is invalid



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/brazilian-utils/license-plate-utils.rb', line 227

def self.generate(format = 'LLLNLNN')
  return nil unless format.is_a?(String)

  format_upper = format.upcase

  return nil unless ['LLLNLNN', 'LLLNNNN'].include?(format_upper)

  generated = ''
  
  format_upper.each_char do |char|
    if char == 'L'
      # Generate random uppercase letter
      generated += ('A'..'Z').to_a.sample
    else  # char == 'N'
      # Generate random digit
      generated += rand(0..9).to_s
    end
  end

  generated
end

.get_format(license_plate) ⇒ String?

Returns the format of a license plate.

Returns 'LLLNNNN' for the old pattern and 'LLLNLNN' for the Mercosul one.

Examples:

get_format("ABC1234")
#=> "LLLNNNN"

get_format("abc1d23")
#=> "LLLNLNN"

get_format("ABC-1234")
#=> "LLLNNNN" (dash is removed automatically)

get_format("ABCD123")
#=> nil

Parameters:

  • license_plate (String) —

    A license plate string without symbols

Returns:

  • (String, nil) —

    The format of the license plate (LLLNNNN, LLLNLNN) or nil if invalid



191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/brazilian-utils/license-plate-utils.rb', line 191

def self.get_format(license_plate)
  return nil unless license_plate.is_a?(String)

  clean = remove_symbols(license_plate)

  if valid_old_format?(clean)
    'LLLNNNN'
  elsif valid_mercosul?(clean)
    'LLLNLNN'
  else
    nil
  end
end

.is_valid(license_plate, type = nil) ⇒ Boolean Also known as: valid?

Returns if a Brazilian license plate number is valid.

It does not verify if the plate actually exists, only validates the format.

Examples:

is_valid("ABC1234")
#=> true (old format)

is_valid("ABC1D34")
#=> true (Mercosul format)

is_valid("ABC-1234")
#=> true (old format with dash)

is_valid("ABC1234", :old_format)
#=> true

is_valid("ABC1D34", :old_format)
#=> false

is_valid("ABC1D34", :mercosul)
#=> true

is_valid("ABCD123")
#=> false

Parameters:

  • license_plate (String) —

    The license plate number to be validated

  • type (Symbol, String, nil) (defaults to: nil) —

    :old_format, :mercosul, "old_format", or "mercosul". If not specified, checks for either format.

Returns:

  • (Boolean) —

    True if the plate number is valid, false otherwise



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/brazilian-utils/license-plate-utils.rb', line 128

def self.is_valid(license_plate, type = nil)
  return false unless license_plate.is_a?(String)

  clean = remove_symbols(license_plate)

  type_str = type.to_s if type

  case type_str
  when 'old_format'
    valid_old_format?(clean)
  when 'mercosul'
    valid_mercosul?(clean)
  else
    valid_old_format?(clean) || valid_mercosul?(clean)
  end
end

.parse(value) ⇒ String

Removes separators from a license plate, upper-cases it and caps it to 7 characters.

Examples:

parse("abc-1234")     #=> "ABC1234"
parse("abc123456")    #=> "ABC1234"

Parameters:

  • value (String) —

    A license plate string, in any case, with or without separators.

Returns:

  • (String) —

    The parsed value.



293
294
295
296
297
# File 'lib/brazilian-utils/license-plate-utils.rb', line 293

def self.parse(value)
  return '' unless value.is_a?(String)

  remove_symbols(value).upcase[0, 7]
end

.remove_symbols(license_plate_number) ⇒ String

Removes the dash (-) symbol from a license plate string.

Examples:

remove_symbols("ABC-1234")
#=> "ABC1234"

remove_symbols("abc123")
#=> "abc123"

remove_symbols("ABCD123")
#=> "ABCD123"

Parameters:

  • license_plate_number (String) —

    A license plate number containing symbols

Returns:

  • (String) —

    The license plate number with dashes removed



165
166
167
168
169
# File 'lib/brazilian-utils/license-plate-utils.rb', line 165

def self.remove_symbols(license_plate_number)
  return '' unless license_plate_number.is_a?(String)
  
  license_plate_number.gsub('-', '')
end