dominicsayers/name_tamer

View on GitHub
lib/name_tamer/constants.rb

Summary

Maintainability
B
6 hrs
Test Coverage
# frozen_string_literal: true

module NameTamer
  NONBREAKING_SPACE = "\u00a0"
  ASCII_SPACE = ' '
  ADFIX_JOINERS = "[#{ASCII_SPACE}-]"
  SLUG_DELIMITER = '-'
  ZERO_WIDTH_FILTER = /[\u180E\u200B\u200C\u200D\u2063\uFEFF]/

  # Constants for parameterizing Unicode strings for IRIs
  #
  # Allowed characters in an IRI segment are defined by RFC 3987
  # (https://tools.ietf.org/html/rfc3987#section-2.2) as follows:
  #
  #    isegment-nz-nc = 1*( iunreserved / pct-encoded / sub-delims
  #                         / "@" )
  #                   ; non-zero-length segment without any colon ":"
  #
  #    iunreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~" / ucschar
  #
  #    pct-encoded    = "%" HEXDIG HEXDIG
  #
  #    sub-delims     = "!" / "$" / "&" / "'" / "(" / ")"
  #                   / "*" / "+" / "," / ";" / "="
  #
  #    ucschar        = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
  #                   / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
  #                   / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
  #                   / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
  #                   / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
  #                   / %xD0000-DFFFD / %xE1000-EFFFD
  #
  # Note that we can't use Unicode code points above \uFFFF because of
  # regex limitations, so we'll ignore ucschar above that point.
  #
  # We're using the most restrictive segment definition (isegment-nz-nc)
  # to avoid any possible problems with the IRI that it one day might
  # get placed in.
  ALPHA = 'A-Za-z'
  DIGIT = '0-9'
  UCSCHAR = '\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF'
  IUNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~#{UCSCHAR}"
  SUBDELIMS = '!$&\'\(\)\*+,;='
  ISEGMENT_NZ_NC = "#{IUNRESERVED}#{SUBDELIMS}@" # pct-encoded not needed
  FILTER_RFC3987 = /[^#{ISEGMENT_NZ_NC}]/
  FILTER_COMPAT = /[^#{ALPHA}#{DIGIT}\-_#{UCSCHAR}]/

  # These are the prefixes and suffixes we want to remove
  # If you add to the list, you can use spaces and dots where appropriate
  # Ensure any single letters are followed by a dot because we'll add one to the string
  # during processing, e.g. "y Cia." should be "y. Cia."
  ADFIXES = {
    prefix: {
      person: [
        'Baroness',
        'Capt.',
        'Captain',
        'Col.',
        'Colonel',
        'Dame',
        'Doctor',
        'Doktor',
        'Dr.',
        'Frau',
        'Herr',
        'Judge',
        'Justice',
        'Lady',
        'Lieut.',
        'Lieutenant',
        'Lord',
        'Madame',
        'Major',
        'Master',
        'Matron',
        'Messrs.',
        'Mgr.',
        'Miss',
        'Mister',
        'Mlle.',
        'Mme.',
        'Mons.',
        'Mr. & Mrs.',
        'Mr. and Mrs.',
        'Mr.',
        'Mrs.',
        'Ms.',
        'Msgr.',
        'Prof.',
        'Professor',
        'Rev.',
        'Reverend',
        'Sir',
        'Sister',
        'The Hon.',
        'The Lady.',
        'The Lord',
        'The Rt. Hon.',
      ],
      organization: [
        'Fa.',
        'P.T. Tbk.',
        'P.T.',
        'U.D.',
      ],
      before: '\\A', after: ADFIX_JOINERS
    },
    suffix: {
      person: [
        'A.A.M.S.',
        'A.C.A.',
        'A.C.C.',
        'A.C.C.A.',
        'A.C.M.A.',
        'A.E.P.',
        'A.I.F.',
        'A.I.F.A.',
        'A.S.A.',
        'A.W.M.A.',
        'B.A.',
        'B.Ed.',
        'B.Eng.',
        'B.Sc.',
        'B.Tech.',
        'C.A.',
        'C.A.I.A.',
        'C.A.P.M.',
        'C.B.V.',
        'C.C.I.M.',
        'C.D.F.A.',
        'C.E.M.',
        'C.E.P.P.',
        'C.Eng.',
        'C.F.A.',
        'C.F.B.S.',
        'C.F.F.',
        'C.F.P.',
        'C.F.S.',
        'C.G.A.',
        'C.G.B.',
        'C.G.M.A.',
        'C.G.P.',
        'C.I.M.',
        'C.I.S.S.P.',
        'C.I.T.P.',
        'C.L.P.',
        'C.L.T.C.',
        'C.L.U.',
        'C.M.A.',
        'C.M.T.',
        'C.P.A.',
        'C.P.C.C.',
        'C.R.P.C.',
        'C.R.P.S.',
        'C.S.O.X.',
        'C.S.S.D.',
        'C.T.A.',
        'C.W.S.',
        'Cantab.',
        'Ch.F.C.',
        'Chartered F.C.S.I.',
        'Chartered M.C.S.I.',
        'D.B.E.',
        'D.D.S.',
        'D.Phil.',
        'D.V.M.',
        'Dip. D.M.',
        'E.A.',
        'E.R.P.',
        'Ed.D.',
        'Ed.M.',
        'Eng.D.',
        'Esq.',
        'F.B.C.S.',
        'F.C.A.',
        'F.C.C.A.',
        'F.C.I.P.S.',
        'F.C.M.I.',
        'F.C.S.I.',
        'F.I.E.T.',
        'F.I.R.P.',
        'F.Inst.L.M.',
        'F.P.C.',
        'F.R.M.',
        'F.R.M.',
        'G.S.P.',
        'Hons.',
        'I.F.R.S. Certified',
        'I.T.I.L. v3',
        'II',
        'III',
        'IV',
        'J.D.',
        'Jr.',
        'K.C.',
        'L.P.S.',
        'LL.B.',
        'LL.D.',
        'LL.M.',
        'M.A.',
        'M.B.A.',
        'M.B.E.',
        'M.D.',
        'M.E.P.',
        'M.Ed.',
        'M.Eng.',
        'M.I.E.T.',
        'M.Io.D.',
        'M.Jur.',
        'M.P.',
        'M.P.A.',
        'M.R.I.C.S.',
        'M.S.',
        'M.S.F.',
        'M.S.F.S.',
        'M.S.P.',
        'M.Sc. D.',
        'M.Sc.',
        'O.B.E.',
        'O.K.',
        'O.R.S.C.',
        'Oxon.',
        'P.A.',
        'P.C.C.',
        'P.F.S.',
        'P.H.R.',
        'P.M.C.',
        'P.M.P.',
        'P.M.P.',
        'P.S.P.',
        'Ph.D.',
        'Q.C.',
        'R.D.',
        'R.F.C.',
        'R.I.C.P.',
        'S.C.M.P',
        'Sr.',
        'T.M.I.E.T.',
        'V',
        'V.M.D.',
      ],
      organization: [
        # These are sorted in length order: don't alphabetize them.
        'S. de R.L. de C.V.',
        'S.A.P.I. de C.V.',
        'y. Cía. S. en C.',
        'Private Limited',
        'S.M. Pte. Ltd.',
        'Cía. S. C. A.',
        'y. Cía. S. C.',
        'S.A. de C.V.',
        'spol. s.r.o.',
        '(Pty.) Ltd.',
        '(Pvt.) Ltd.',
        'A.D.S.I.Tz.',
        'S.p. z.o.o.',
        '(Pvt.)Ltd.',
        'akc. spol.',
        'Cía. Ltda.',
        'E.B.V.B.A.',
        'P. Limited',
        'S. de R.L.',
        'S.I.C.A.V.',
        'S.P.R.L.U.',
        'А.Д.С.И.Ц.',
        '(P.) Ltd.',
        'C. por A.',
        'Comm.V.A.',
        'Ltd. Şti.',
        'Plc. Ltd.',
        'Pte. Ltd.',
        'Pty. Ltd.',
        'Pvt. Ltd.',
        'Soc. Col.',
        'A.M.B.A.',
        'A.S.B.L.',
        'A.V.E.E.',
        'B.V.B.A.',
        'B.V.I.O.',
        'C.V.B.A.',
        'C.V.O.A.',
        'E.E.I.G.',
        'E.I.R.L.',
        'E.O.O.D.',
        'E.U.R.L.',
        'F.M.B.A.',
        'G.m.b.H.',
        'Ges.b.R.',
        'K.G.a.A.',
        'L.L.L.P.',
        'Ltd. Co.',
        'Ltd. Co.',
        'M.E.P.E.',
        'n.y.r.t.',
        'O.V.E.E.',
        'P.E.E.C.',
        'P.L.L.C.',
        'P.L.L.C.',
        'S. en C.',
        'S.a.p.a.',
        'S.A.R.L.',
        'S.à.R.L.',
        'S.A.S.U.',
        'S.C.e.I.',
        'S.C.O.P.',
        'S.C.p.A.',
        'S.C.R.I.',
        'S.C.R.L.',
        'S.M.B.A.',
        'S.P.R.L.',
        'Е.О.О.Д.',
        '&. Cie.',
        'and Co.',
        'Comm.V.',
        'Limited',
        'P. Ltd.',
        'Part.G.',
        'Sh.p.k.',
        '&. Co.',
        'C.X.A.',
        'd.n.o.',
        'd.o.o.',
        'E.A.D.',
        'e.h.f.',
        'E.P.E.',
        'E.S.V.',
        'F.C.P.',
        'F.I.E.',
        'G.b.R.',
        'G.I.E.',
        'G.M.K.',
        'G.S.K.',
        'H.U.F.',
        'K.D.A.',
        'k.f.t.',
        'k.h.t.',
        'k.k.t.',
        'L.L.C.',
        'L.L.P.',
        'o.h.f.',
        'O.H.G.',
        'O.O.D.',
        'O.y.j.',
        'p.l.c.',
        'P.S.U.',
        'S.A.E.',
        'S.A.S.',
        'S.C.A.',
        'S.C.E.',
        'S.C.S.',
        'S.E.M.',
        'S.E.P.',
        's.e.s.',
        'S.G.R.',
        'S.N.C.',
        'S.p.A.',
        'S.P.E.',
        'S.R.L.',
        's.r.o.',
        'Unltd.',
        'V.O.F.',
        'V.o.G.',
        'v.o.s.',
        'V.Z.W.',
        'z.r.t.',
        'А.А.Т.',
        'Е.А.Д.',
        'З.А.Т.',
        'К.Д.А.',
        'О.О.Д.',
        'Т.А.А.',
        '股份有限公司',
        'Ap.S.',
        'Corp.',
        'ltda.',
        'Sh.A.',
        'st.G.',
        'Ultd.',
        'a.b.',
        'A.D.',
        'A.E.',
        'A.G.',
        'A.S.',
        'A.Ş.',
        'A.y.',
        'B.M.',
        'b.t.',
        'B.V.',
        'C.A.',
        'C.V.',
        'd.d.',
        'e.c.',
        'E.E.',
        'e.G.',
        'E.I.',
        'E.P.',
        'E.T.',
        'E.U.',
        'e.v.',
        'G.K.',
        'G.P.',
        'h.f.',
        'Inc.',
        'K.D.',
        'K.G.',
        'K.K.',
        'k.s.',
        'k.v.',
        'K.y.',
        'L.C.',
        'L.P.',
        'Ltd.',
        'N.K.',
        'N.L.',
        'N.V.',
        'O.E.',
        'O.G.',
        'O.Ü.',
        'O.y.',
        'P.C.',
        'p.l.',
        'Pty.',
        'PUP.',
        'Pvt.',
        'r.t.',
        'S.A.',
        'S.D.',
        'S.E.',
        's.f.',
        'S.L.',
        'S.P.',
        'S.s.',
        'T.K.',
        'T.Ü.',
        'U.Ü.',
        'Y.K.',
        'А.Д.',
        'І.П.',
        'К.Д.',
        'ПУП.',
        'С.Д.',
        'בע"מ',
        '任意組合',
        '匿名組合',
        '合同会社',
        '合名会社',
        '合資会社',
        '有限会社',
        '有限公司',
        '株式会社',
        'A/S',
        'G/S',
        'I/S',
        'K/S',
        'P/S',
        'S/A',
      ],
      before: ADFIX_JOINERS, after: '\\z'
    },
  }.freeze

  ADFIX_PATTERNS = Hash[%i[prefix suffix].map do |adfix_type|
    patterns = {}
    adfix = ADFIXES[adfix_type]

    %i[person organization].each do |ct|
      with_optional_spaces = adfix[ct].map { |p| p.gsub(ASCII_SPACE, ' *') }
      pattern_string = with_optional_spaces.join('|').gsub('.', '\.*')
      patterns[ct] = /#{adfix[:before]}\(*(?:#{pattern_string})[®™\)]*#{adfix[:after]}/i
    end

    [adfix_type, patterns]
  end]
end