Class: Vehicles::Plates::Series

Inherits:
Object
  • Object
show all
Defined in:
lib/vehicles/plates/series.rb

Overview

One registration series: a pattern identity with a period, a class (standard/taxi/diplomatic/…), categories, sourced design facts, and the two regexes of the dataset's regex policy — regex (recall: the pattern's own alphabet) and regex_strict (validation: the alphabet the authority actually issues). Validation prefers strict.

Constant Summary collapse

SEPARATOR_TOKENS =

Tokens that read as "a separator" when they make up a whole character class — [- ] in the ES consular series is dash-or-space, i.e. a separator spelled as a class.

[ " ", "-", "·", ".", '\s', '\-', '\.', '\ ' ].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(entry) ⇒ Series

Returns a new instance of Series.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/vehicles/plates/series.rb', line 15

def initialize(entry)
  @id           = entry["id"]
  @klass        = entry["class"] || "standard"
  @categories   = Array(entry["categories"]).freeze
  @period       = (entry["period"] || {}).freeze
  @pattern      = entry.dig("format", "pattern")
  @regex        = entry.dig("format", "regex")
  @regex_strict = entry.dig("format", "regex_strict")
  @design       = (entry["design"] || {}).freeze
  @variants     = Array(entry["variants"]).freeze
  @sources      = Array(entry["sources"]).freeze
  @notes        = entry["notes"]
  # Compiled eagerly — the object is frozen, so no lazy memoization.
  @validation_regexp = compile(@regex_strict || @regex)
  @lenient_regexp    = compile(self.class.strip_separators(@regex_strict || @regex))
  freeze
end

Instance Attribute Details

#categoriesObject (readonly)

Returns the value of attribute categories.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def categories
  @categories
end

#designObject (readonly)

Returns the value of attribute design.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def design
  @design
end

#idObject (readonly)

Returns the value of attribute id.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def id
  @id
end

#klassObject (readonly)

Returns the value of attribute klass.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def klass
  @klass
end

#lenient_regexpObject (readonly)

Returns the value of attribute lenient_regexp.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def lenient_regexp
  @lenient_regexp
end

#notesObject (readonly)

Returns the value of attribute notes.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def notes
  @notes
end

#patternObject (readonly)

Returns the value of attribute pattern.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def pattern
  @pattern
end

#periodObject (readonly)

Returns the value of attribute period.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def period
  @period
end

#regexObject (readonly)

Returns the value of attribute regex.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def regex
  @regex
end

#regex_strictObject (readonly)

Returns the value of attribute regex_strict.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def regex_strict
  @regex_strict
end

#sourcesObject (readonly)

Returns the value of attribute sources.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def sources
  @sources
end

#validation_regexpObject (readonly)

Returns the value of attribute validation_regexp.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def validation_regexp
  @validation_regexp
end

#variantsObject (readonly)

Returns the value of attribute variants.



11
12
13
# File 'lib/vehicles/plates/series.rb', line 11

def variants
  @variants
end

Class Method Details

.strip_separators(src) ⇒ Object

Remove separator LITERALS from a regex source, respecting character classes (the "-" inside [A-Z] is a range, not a separator; a class composed ONLY of separators, like [- ], is a separator and goes). A quantifier left dangling by a removed separator ("-?", "[- ]?") is swallowed with it. Linear scan — regexes in this dataset are simple.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/vehicles/plates/series.rb', line 75

def self.strip_separators(src)
  return nil if src.nil?

  out = +""
  chars = src.chars
  i = 0
  while i < chars.size
    ch = chars[i]

    if ch == "\\" && i + 1 < chars.size
      nxt = chars[i + 1]
      if [ "s", "-", ".", " " ].include?(nxt)
        i += 2
        i += 1 if [ "?", "*" ].include?(chars[i]) # optional separator: drop its quantifier too
      else
        out << ch << nxt
        i += 2
      end
      next
    end

    if ch == "["
      # Copy or drop the WHOLE class span in one decision.
      closing = i + 1
      closing += 1 while closing < chars.size && chars[closing] != "]"
      content = chars[(i + 1)...closing].join
      tokens = content.scan(/\\.|./m)
      if tokens.any? && tokens.all? { |t| SEPARATOR_TOKENS.include?(t) }
        i = closing + 1
        i += 1 if [ "?", "*" ].include?(chars[i])
      else
        out << chars[i..closing].join
        i = closing + 1
      end
      next
    end

    if [ "-", " ", "·" ].include?(ch)
      i += 1
      i += 1 if [ "?", "*" ].include?(chars[i])
    else
      out << ch
      i += 1
    end
  end
  out
end

Instance Method Details

#format_serial(serial) ⇒ Object

Re-print a separator-less serial the way this series formats it: walk the pattern; 9/L and literal serial characters consume one input character, separator characters re-emerge from the pattern itself.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/vehicles/plates/series.rb', line 50

def format_serial(serial)
  return nil if serial.nil? || pattern.nil?

  idx = 0
  out = pattern.each_char.with_object(+"") do |ch, str|
    if [ "-", " ", "·", "." ].include?(ch)
      str << ch
    else
      str << (serial[idx] || "")
      idx += 1
    end
  end
  out.freeze
end

#inspectObject



123
# File 'lib/vehicles/plates/series.rb', line 123

def inspect = %(#<Vehicles::Plates::Series #{id} "#{pattern}">)

#period_labelObject

"2000 →" (open, runs until exhausted) or "1971–2000".



42
43
44
45
# File 'lib/vehicles/plates/series.rb', line 42

def period_label
  finish = period["end"]
  finish ? "#{period["start"]}#{finish}" : "#{period["start"]}"
end

#strict?Boolean

A strict-validated series has an authority-alphabet regex twin; a recall-only series (export/dark-blue catch-alls that accept any current shape) matches loosely by design. Consumers validating user input should weight strict hits above recall-only ones — Match does.

Returns:

  • (Boolean)


37
38
39
# File 'lib/vehicles/plates/series.rb', line 37

def strict?
  !regex_strict.nil?
end