Module: Vehicles::Plates
- Defined in:
- lib/vehicles/plates.rb,
lib/vehicles/plates/series.rb,
lib/vehicles/plates/jurisdiction.rb
Overview
License-plate & registration-mark knowledge (the VehiclesDB PRD-PLATES dataset, bundled offline like everything else in this gem). Ask it two kinds of question:
Vehicles.plates # => every Jurisdiction
Vehicles.plates(:nl) # => one Jurisdiction (series, charset, authority)
Vehicles.plate("12-GB-BD", jurisdiction: :nl) # => Plates::Match
Matching is TWO-TIER, because humans type plates every way at once:
exact — the input, upcased, matches a series' STRICT regex as-typed
(separators included): formatted exactly as issued.
lenient — the input with separators stripped ("1234XYZ", "1234 xyz",
"1234-XYZ" all collapse to "1234XYZ") matches the same strict
alphabet with the separators removed. Still the authority's
real issuing alphabet — only the punctuation is forgiven.
A lenient-only match carries suggestion: the serial re-formatted the
way it appears on the physical plate ("did you mean 1234 XYZ").
Same posture as every lookup in this gem: garbage in => empty result,
never an exception.
Defined Under Namespace
Classes: Jurisdiction, Match, Series
Constant Summary collapse
- DATA_DIR =
File.("../../data/plates", __dir__)
- SEPARATORS =
What people type between plate groups: space(s), hyphen/en/em dash, middot, period. Stripped for the lenient tier (and from the strict regexes to derive their lenient twins).
/[\s\-–—·.]+/
Class Method Summary collapse
- .classes ⇒ Object
- .jurisdiction(code) ⇒ Object
- .jurisdictions ⇒ Object
-
.match(input, jurisdiction:) ⇒ Object
The matcher behind Vehicles.plate.
Class Method Details
.classes ⇒ Object
47 48 49 |
# File 'lib/vehicles/plates.rb', line 47 def classes @classes ||= YAML.safe_load_file(File.join(DATA_DIR, "_meta", "classes.yml")) || {} end |
.jurisdiction(code) ⇒ Object
43 44 45 |
# File 'lib/vehicles/plates.rb', line 43 def jurisdiction(code) jurisdictions.find { |j| j.code == code.to_s.downcase.tr("_", "-") } end |
.jurisdictions ⇒ Object
37 38 39 40 41 |
# File 'lib/vehicles/plates.rb', line 37 def jurisdictions @jurisdictions ||= Dir.glob(File.join(DATA_DIR, "*.yml")).sort.map do |path| Jurisdiction.load(path) end end |
.match(input, jurisdiction:) ⇒ Object
The matcher behind Vehicles.plate. Unknown jurisdiction => empty Match.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/vehicles/plates.rb', line 52 def match(input, jurisdiction:) juris = jurisdiction(jurisdiction) typed = input.to_s.upcase.strip canon = typed.gsub(SEPARATORS, "") return Match.new(input: input.to_s, jurisdiction: juris, canon: canon, hits: []) if juris.nil? || canon.empty? hits = juris.series.filter_map do |series| next nil unless series.validation_regexp exact = series.validation_regexp.match?(typed) lenient = exact || series.lenient_regexp&.match?(canon) Match::Hit.new(series: series, exact: exact) if lenient end Match.new(input: input.to_s, jurisdiction: juris, canon: canon, hits: hits) end |