Module: NameTamer::Strings
- Defined in:
- lib/name_tamer/strings/core.rb,
lib/name_tamer/strings/spacing.rb,
lib/name_tamer/strings/bad_encoding.rb,
lib/name_tamer/strings/approximations.rb,
lib/name_tamer/strings/capitalization.rb,
lib/name_tamer/strings/compound_names.rb,
lib/name_tamer/strings/name_modifiers.rb
Overview
Pure string transformations used throughout the gem. Every method takes a string and returns a new string; arguments are never mutated.
Constant Summary collapse
- BAD_ENCODING =
{ "\xC3\x8D" => 'Í', "\xC3\x8F" => 'Ï', "\xC3\x90" => 'Ð', "\xC3\x9D" => 'Ý', ' ' => ' ', '¡' => '¡', '¢' => '¢', '£' => '£', '¤' => '¤', 'Â¥' => '¥', '¦' => '¦', '§' => '§', '¨' => '¨', '©' => '©', 'ª' => 'ª', '«' => '«', '¬' => '¬', 'Â' => '', '®' => '®', '¯' => '¯', '°' => '°', '±' => '±', '²' => '²', '³' => '³', '´' => '´', 'µ' => 'µ', '¶' => '¶', '·' => '·', '¸' => '¸', '¹' => '¹', 'º' => 'º', '»' => '»', '¼' => '¼', '½' => '½', '¾' => '¾', '¿' => '¿', '€' => '€', 'â„¢' => '™', 'â€' => '”', # Note the invisible Ux009D in the key '†' => '†', '‡' => '‡', '•' => '•', '…' => '…', '‰' => '‰', '′' => '′', # Manually added. Some seem to use this instead of Ux2019 '‹' => '‹', '›' => '›', '“' => '“', '‚' => '‚', '„' => '„', '‘' => '‘', '–' => '–', '—' => '—', '’' => '’', 'à ' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'Ã¥' => 'å', 'æ' => 'æ', 'ç' => 'ç', 'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'Ã' => 'í', 'î' => 'î', 'ï' => 'ï', 'ð' => 'ð', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', '÷' => '÷', 'ø' => 'ø', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'þ' => 'þ', 'ÿ' => 'ÿ', 'ß' => 'ß', 'ÃŒ' => 'Ì', 'Ü' => 'Ü', 'Ê' => 'Ê', 'Ú' => 'Ú', 'ÃŽ' => 'Î', 'Þ' => 'Þ', 'Ã' => 'Ã', 'È' => 'È', 'Ø' => 'Ø', 'Ö' => 'Ö', '×' => '×', 'Ñ' => 'Ñ', 'Ã’' => 'Ò', 'Â' => 'Â', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Ä' => 'Ä', 'Æ' => 'Æ', 'Ç' => 'Ç', 'Õ' => 'Õ', 'Ã…' => 'Å', 'É' => 'É', 'Ë' => 'Ë', 'Û' => 'Û', 'À' => 'À', 'Ù' => 'Ù', 'Ã�' => 'Á', 'Å ' => 'Š', 'Å¡' => 'š', 'Ÿ' => 'Ÿ', 'Ž' => 'Ž', 'ž' => 'ž', 'Å’' => 'Œ', 'Å“' => 'œ', 'Æ’' => 'ƒ', 'Ëœ' => '˜', 'ˆ' => 'ˆ', "\x00" => '', # Manually added to avoid Bad Argument exception }.freeze
- BAD_ENCODING_PATTERNS =
/(#{BAD_ENCODING.keys.join('|')})/- APPROXIMATIONS =
{ 'İ' => 'I', '×' => 'x', 'ß' => 'ss', 'À' => 'A', 'à' => 'a', 'Á' => 'A', 'á' => 'a', 'Â' => 'A', 'â' => 'a', 'Ã' => 'A', 'ã' => 'a', 'Ä' => 'A', 'ä' => 'a', 'Å' => 'A', 'å' => 'a', 'Æ' => 'AE', 'æ' => 'ae', 'Ç' => 'C', 'ç' => 'c', 'È' => 'E', 'è' => 'e', 'É' => 'E', 'é' => 'e', 'Ê' => 'E', 'ê' => 'e', 'Ë' => 'E', 'ë' => 'e', 'Ì' => 'I', 'ì' => 'i', 'Í' => 'I', 'í' => 'i', 'Î' => 'I', 'î' => 'i', 'Ï' => 'I', 'ï' => 'i', 'Ð' => 'D', 'ð' => 'd', 'Ñ' => 'N', 'ñ' => 'n', 'Ò' => 'O', 'ò' => 'o', 'Ó' => 'O', 'ó' => 'o', 'Ô' => 'O', 'ô' => 'o', 'Õ' => 'O', 'õ' => 'o', 'Ö' => 'O', 'ö' => 'o', 'Ø' => 'O', 'ø' => 'o', 'Ù' => 'U', 'ù' => 'u', 'Ú' => 'U', 'ú' => 'u', 'Û' => 'U', 'û' => 'u', 'Ü' => 'U', 'ü' => 'u', 'Ý' => 'Y', 'ý' => 'y', 'Þ' => 'Th', 'þ' => 'th', 'ÿ' => 'y', 'Ÿ' => 'Y', 'Ā' => 'A', 'ā' => 'a', 'Ă' => 'A', 'ă' => 'a', 'Ą' => 'A', 'ą' => 'a', 'Ć' => 'C', 'ć' => 'c', 'Ĉ' => 'C', 'ĉ' => 'c', 'Ċ' => 'C', 'ċ' => 'c', 'Č' => 'C', 'č' => 'c', 'Ď' => 'D', 'ď' => 'd', 'Đ' => 'D', 'đ' => 'd', 'Ē' => 'E', 'ē' => 'e', 'Ĕ' => 'E', 'ĕ' => 'e', 'Ė' => 'E', 'ė' => 'e', 'Ę' => 'E', 'ę' => 'e', 'Ě' => 'E', 'ě' => 'e', 'Ĝ' => 'G', 'ĝ' => 'g', 'Ğ' => 'G', 'ğ' => 'g', 'Ġ' => 'G', 'ġ' => 'g', 'Ģ' => 'G', 'ģ' => 'g', 'Ĥ' => 'H', 'ĥ' => 'h', 'Ħ' => 'H', 'ħ' => 'h', 'Ĩ' => 'I', 'ĩ' => 'i', 'Ī' => 'I', 'ī' => 'i', 'Ĭ' => 'I', 'ĭ' => 'i', 'Į' => 'I', 'į' => 'i', 'ı' => 'i', 'IJ' => 'IJ', 'ij' => 'ij', 'Ĵ' => 'J', 'ĵ' => 'j', 'Ķ' => 'K', 'ķ' => 'k', 'ĸ' => 'k', 'Ĺ' => 'L', 'ĺ' => 'l', 'Ļ' => 'L', 'ļ' => 'l', 'Ľ' => 'L', 'ľ' => 'l', 'Ŀ' => 'L', 'ŀ' => 'l', 'Ł' => 'L', 'ł' => 'l', 'Ń' => 'N', 'ń' => 'n', 'Ņ' => 'N', 'ņ' => 'n', 'Ň' => 'N', 'ň' => 'n', 'ʼn' => "'n", 'Ŋ' => 'NG', 'ŋ' => 'ng', 'Ō' => 'O', 'ō' => 'o', 'Ŏ' => 'O', 'ŏ' => 'o', 'Ő' => 'O', 'ő' => 'o', 'Œ' => 'OE', 'œ' => 'oe', 'Ŕ' => 'R', 'ŕ' => 'r', 'Ŗ' => 'R', 'ŗ' => 'r', 'Ř' => 'R', 'ř' => 'r', 'Ś' => 'S', 'ś' => 's', 'Ŝ' => 'S', 'ŝ' => 's', 'Ş' => 'S', 'ş' => 's', 'Š' => 'S', 'š' => 's', 'Ţ' => 'T', 'ţ' => 't', 'Ť' => 'T', 'ť' => 't', 'Ŧ' => 'T', 'ŧ' => 't', 'Ũ' => 'U', 'ũ' => 'u', 'Ū' => 'U', 'ū' => 'u', 'Ŭ' => 'U', 'ŭ' => 'u', 'Ů' => 'U', 'ů' => 'u', 'Ű' => 'U', 'ű' => 'u', 'Ų' => 'U', 'ų' => 'u', 'Ŵ' => 'W', 'ŵ' => 'w', 'Ŷ' => 'Y', 'ŷ' => 'y', 'Ź' => 'Z', 'ź' => 'z', 'Ż' => 'Z', 'ż' => 'z', 'ž' => 'z', 'Ž' => 'Z', }.freeze
- MAC_EXCEPTIONS =
%w[ MacEdo MacEvicius MacHado MacHar MacHin MacHlin MacIas MacIulis MacKie MacKle MacKlin MacKmin MacKmurdo MacQuarie MacLise MacKenzie ].freeze
- FF_NAMES =
%w[Fforbes Fforde Ffinch Ffrench Ffoulkes].freeze
- COMPOUND_NAMES =
[ # Known families with a space in their surname 'Baron Cohen', 'Bonham Carter', 'Holmes a Court', 'Holmes à Court', 'Lane Fox', 'Lloyd Webber', 'Pitt Rivers', 'Sebag Montefiore', 'Strang Steel', 'Wedgwood Benn', 'Wingfield Digby', # Sometimes companies appear as people 'Corporation Company', 'Corporation System', 'Incorporations Limited', 'Service Company', ].freeze
- NAME_MODIFIERS =
[ 'Al', 'Ap', 'Ben', 'D[aeiou]', 'D[ao]s', 'De[lrn]', 'Dell[ae]', 'El', 'L[eo]', 'La', 'Of', 'San', 'St[\.]?', 'V[ao]n', 'Zur', ].freeze
Class Method Summary collapse
-
.approximate_latin_chars(string) ⇒ Object
Any characters that resemble latin characters might usefully be transliterated into ones that are easy to type on an anglophone keyboard.
- .downcase_after_apostrophe(string) ⇒ Object
- .ensure_safe(string) ⇒ Object
- .ensure_space_after_initials(string) ⇒ Object
- .fix_apostrophe_modifiers(string) ⇒ Object
-
.fix_encoding_errors(string) ⇒ Object
Strings that were wrongly encoded with single-byte encodings sometimes have tell-tale substrings that we can put back into the correct UTF-8 character.
-
.fix_ff(string) ⇒ Object
Fix ff wierdybonks.
-
.fix_mac(string) ⇒ Object
Our list of terminal characters that indicate a non-celtic name used to include o but we removed it because of MacMurdo.
-
.fix_name_modifiers(string) ⇒ Object
Fixes for name modifiers followed by space Also replaces spaces with non-breaking spaces Fixes for name modifiers followed by an apostrophe, e.g.
-
.fix_separators(string, separator) ⇒ Object
Make sure separators are not where they shouldn't be.
-
.invalid_chars_to(string, separator) ⇒ Object
Change some characters embedded in words to our separator character e.g.
-
.nbsp_in_compound_name(string) ⇒ Object
Fix known last names that have spaces (not hyphens!).
- .nbsp_in_name_modifier(string) ⇒ Object
- .presence(string) ⇒ Object
- .remove_periods_from_initials(string) ⇒ Object
- .remove_spaces_from_initials(string) ⇒ Object
-
.safe_unescape(string) ⇒ Object
Unescape percent-encoded characters This might introduce UTF-8 invalid byte sequence so we take precautions.
-
.space_around_comma(string) ⇒ Object
Ensure commas have exactly one space after them.
-
.strip_unwanted(string, filter) ⇒ Object
Strip unwanted characters out completely.
-
.unescape_html(string) ⇒ Object
Remove HTML entities.
- .upcase_first_letter(string) ⇒ Object
-
.upcase_initials(string) ⇒ Object
Upcase words with no vowels, e.g JPR Williams Except Ng http://en.wikipedia.org/wiki/Ng.
-
.whitespace_to(string, separator) ⇒ Object
Change any whitespace into our separator character.
Class Method Details
.approximate_latin_chars(string) ⇒ Object
Any characters that resemble latin characters might usefully be transliterated into ones that are easy to type on an anglophone keyboard.
12 13 14 |
# File 'lib/name_tamer/strings/approximations.rb', line 12 def approximate_latin_chars(string) string.gsub(/[^\x00-\x7f]/u) { |char| APPROXIMATIONS[char] || char } end |
.downcase_after_apostrophe(string) ⇒ Object
11 12 13 |
# File 'lib/name_tamer/strings/capitalization.rb', line 11 def downcase_after_apostrophe(string) string.gsub(/'\w\b/, &:downcase) # Lowercase 's end |
.ensure_safe(string) ⇒ Object
58 59 60 |
# File 'lib/name_tamer/strings/core.rb', line 58 def ensure_safe(string) string.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') end |
.ensure_space_after_initials(string) ⇒ Object
18 19 20 |
# File 'lib/name_tamer/strings/spacing.rb', line 18 def ensure_space_after_initials(string) string.gsub(/\b([a-z]\.)(?=[a-z0-9]{2,})/i) { "#{Regexp.last_match[1]} " } end |
.fix_apostrophe_modifiers(string) ⇒ Object
21 22 23 24 25 |
# File 'lib/name_tamer/strings/name_modifiers.rb', line 21 def fix_apostrophe_modifiers(string) %w[Dell D].reduce(string) do |name, modifier| name.gsub(/(.#{modifier}')(\w)/) { "#{Regexp.last_match[1].rstrip.downcase}#{Regexp.last_match[2]}" } end end |
.fix_encoding_errors(string) ⇒ Object
Strings that were wrongly encoded with single-byte encodings sometimes have tell-tale substrings that we can put back into the correct UTF-8 character
14 15 16 |
# File 'lib/name_tamer/strings/bad_encoding.rb', line 14 def fix_encoding_errors(string) string.gsub(BAD_ENCODING_PATTERNS) { |substring| BAD_ENCODING[substring] || substring } end |
.fix_ff(string) ⇒ Object
Fix ff wierdybonks
27 28 29 |
# File 'lib/name_tamer/strings/capitalization.rb', line 27 def fix_ff(string) FF_NAMES.reduce(string) { |name, ff_name| name.gsub(ff_name, ff_name.downcase) } end |
.fix_mac(string) ⇒ Object
Our list of terminal characters that indicate a non-celtic name used to include o but we removed it because of MacMurdo.
17 18 19 20 21 22 23 24 |
# File 'lib/name_tamer/strings/capitalization.rb', line 17 def fix_mac(string) return string unless /\bMac[A-Za-z]{2,}[^acizj]\b/.match?(string) || /\bMc/.match?(string) fixed = string.gsub(/\b(Ma?c)([A-Za-z]+)/) { Regexp.last_match[1] + Regexp.last_match[2].capitalize } # Fix Mac exceptions MAC_EXCEPTIONS.reduce(fixed) { |name, mac_name| name.gsub(/\b#{mac_name}/, mac_name.capitalize) } end |
.fix_name_modifiers(string) ⇒ Object
Fixes for name modifiers followed by space Also replaces spaces with non-breaking spaces Fixes for name modifiers followed by an apostrophe, e.g. d'Artagnan, Commedia dell'Arte
11 12 13 14 15 16 17 18 19 |
# File 'lib/name_tamer/strings/name_modifiers.rb', line 11 def fix_name_modifiers(string) fixed = NAME_MODIFIERS.reduce(string) do |name, modifier| name.gsub(/((?:[[:space:]]|^)#{modifier})([[:space:]]+|-)/) do "#{Regexp.last_match[1].rstrip.downcase}#{Regexp.last_match[2].tr(ASCII_SPACE, NONBREAKING_SPACE)}" end end fix_apostrophe_modifiers(fixed) end |
.fix_separators(string, separator) ⇒ Object
Make sure separators are not where they shouldn't be
30 31 32 33 34 35 36 37 |
# File 'lib/name_tamer/strings/core.rb', line 30 def fix_separators(string, separator) return string if separator.nil? || separator.empty? r = Regexp.escape(separator) # No more than one separator in a row, no leading or trailing separator string.gsub(/#{r}{2,}/, separator).gsub(/^#{r}|#{r}$/i, '') end |
.invalid_chars_to(string, separator) ⇒ Object
Change some characters embedded in words to our separator character e.g. example.com -> example-com
20 21 22 |
# File 'lib/name_tamer/strings/core.rb', line 20 def invalid_chars_to(string, separator) string.gsub(%r{(?<![[:space:]])[./](?![[:space:]])}, separator) end |
.nbsp_in_compound_name(string) ⇒ Object
Fix known last names that have spaces (not hyphens!)
8 9 10 11 12 |
# File 'lib/name_tamer/strings/compound_names.rb', line 8 def nbsp_in_compound_name(string) COMPOUND_NAMES.reduce(string) do |name, compound_name| name.gsub(compound_name, compound_name.tr(ASCII_SPACE, NONBREAKING_SPACE)) end end |
.nbsp_in_name_modifier(string) ⇒ Object
27 28 29 30 31 |
# File 'lib/name_tamer/strings/name_modifiers.rb', line 27 def nbsp_in_name_modifier(string) NAME_MODIFIERS.reduce(string) do |name, modifier| name.gsub(/([[:space:]]#{modifier})([[:space:]])/i) { "#{Regexp.last_match[1]}#{NONBREAKING_SPACE}" } end end |
.presence(string) ⇒ Object
9 10 11 |
# File 'lib/name_tamer/strings/core.rb', line 9 def presence(string) string unless string.empty? end |
.remove_periods_from_initials(string) ⇒ Object
39 40 41 |
# File 'lib/name_tamer/strings/core.rb', line 39 def remove_periods_from_initials(string) string.gsub(/\b([a-z])\./i) { Regexp.last_match[1] } end |
.remove_spaces_from_initials(string) ⇒ Object
12 13 14 15 16 |
# File 'lib/name_tamer/strings/spacing.rb', line 12 def remove_spaces_from_initials(string) string.gsub(/\b([a-z])(\.)* \b(?![a-z0-9'À-ÿ]{2,})/i) do "#{Regexp.last_match[1]}#{Regexp.last_match[2]}" end end |
.safe_unescape(string) ⇒ Object
Unescape percent-encoded characters This might introduce UTF-8 invalid byte sequence so we take precautions
51 52 53 54 55 56 |
# File 'lib/name_tamer/strings/core.rb', line 51 def safe_unescape(string) unescaped = CGI.unescape(string.gsub('+', '%2B')) return string if string == unescaped ensure_safe(unescaped) end |
.space_around_comma(string) ⇒ Object
Ensure commas have exactly one space after them
8 9 10 |
# File 'lib/name_tamer/strings/spacing.rb', line 8 def space_around_comma(string) string.gsub(/[[:space:]]*,[[:space:]]*/, ', ') end |
.strip_unwanted(string, filter) ⇒ Object
Strip unwanted characters out completely
44 45 46 |
# File 'lib/name_tamer/strings/core.rb', line 44 def strip_unwanted(string, filter) string.gsub(filter, '') end |
.unescape_html(string) ⇒ Object
Remove HTML entities
25 26 27 |
# File 'lib/name_tamer/strings/core.rb', line 25 def unescape_html(string) CGI.unescapeHTML string end |
.upcase_first_letter(string) ⇒ Object
7 8 9 |
# File 'lib/name_tamer/strings/capitalization.rb', line 7 def upcase_first_letter(string) string.gsub(/\b\w/, &:upcase) end |
.upcase_initials(string) ⇒ Object
Upcase words with no vowels, e.g JPR Williams Except Ng http://en.wikipedia.org/wiki/Ng
33 34 35 36 37 |
# File 'lib/name_tamer/strings/capitalization.rb', line 33 def upcase_initials(string) string .gsub(/\b([bcdfghjklmnpqrstvwxz]+)\b/i) { Regexp.last_match[1].upcase } .gsub(/\b(NG)\b/i) { Regexp.last_match[1].capitalize } end |
.whitespace_to(string, separator) ⇒ Object
Change any whitespace into our separator character
14 15 16 |
# File 'lib/name_tamer/strings/core.rb', line 14 def whitespace_to(string, separator) string.gsub(/[[:space:]]+/, separator) end |