Format.initials: one letter, not a guess at two
Review feedback: splitting a bare FN on whitespace and taking the
first letter of up to two words was still a guess dressed up as two
initials. Falling back from a missing N property to a single first
letter is honest about having only unstructured text to go on.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYiigu3aFNaiTP9SyKW9mX
diff --git a/lib/pro_tacts/admin/contact_fields.rb b/lib/pro_tacts/admin/contact_fields.rb
index 5a261d9..cbdba5b 100644
--- a/lib/pro_tacts/admin/contact_fields.rb
+++ b/lib/pro_tacts/admin/contact_fields.rb
@@ -51,10 +51,10 @@ module ProTacts
# RFC 2426 section 3.1.2: N is Family;Given;Additional;Prefixes;
# Suffixes. A given and family name are structured data — reading
# them is the real answer to "what are this contact's initials,"
- # not guessing at word boundaries in the free-text FN the way
- # Format.initials does. That guess is kept only as the fallback
- # for a card with no N at all (an organization's own address-book
- # entry, say, which has a name but no person to split in two).
+ # rather than guessing at where a first/last split falls in the
+ # free-text FN. A card with no N at all (an organization's own
+ # address-book entry, say, with a name but no person to split in
+ # two) falls back to Format.initials' single first letter.
#: (VCard::Property? property) -> String?
def initials_from(property)
return nil if property.nil?
diff --git a/lib/pro_tacts/admin/format.rb b/lib/pro_tacts/admin/format.rb
index 6016a48..e558fb8 100644
--- a/lib/pro_tacts/admin/format.rb
+++ b/lib/pro_tacts/admin/format.rb
@@ -4,9 +4,13 @@ module ProTacts
module Admin
# Small display helpers shared by the admin views.
module Format
+ # The first letter of a name with nothing more structured to go
+ # on — an id, or a display name with no N property behind it
+ # (see ContactFields#initials_from). One letter rather than a
+ # guess at where a first/last split would fall in free text.
#: (String name) -> String
def self.initials(name)
- name.to_s.split(/\s+/).reject(&:empty?).first(2).map { it[0].upcase }.join
+ name.to_s.strip[0].to_s.upcase
end
# A card's updated_at, UTC ISO 8601 to the millisecond (see
diff --git a/test/pro_tacts/admin/test_contact_fields.rb b/test/pro_tacts/admin/test_contact_fields.rb
index 0e68165..afd416a 100644
--- a/test/pro_tacts/admin/test_contact_fields.rb
+++ b/test/pro_tacts/admin/test_contact_fields.rb
@@ -25,11 +25,11 @@ class ContactFieldsTest < Minitest::Test
end
# A card with no N at all — an organization's own entry, say — falls
- # back to Format.initials' word-splitting guess on FN.
+ # back to Format.initials' single first letter of FN.
def test_initials_fall_back_to_the_display_name_with_no_n_property
no_n = "BEGIN:VCARD\r\nVERSION:3.0\r\nFN:Riverside Elementary\r\nUID:office\r\nEND:VCARD\r\n"
- assert_equal "RE", fields(no_n).initials
+ assert_equal "R", fields(no_n).initials
end
# N with only one of given/family present (a mononym, or a card that
diff --git a/test/pro_tacts/admin/test_format.rb b/test/pro_tacts/admin/test_format.rb
index 315ebf3..c8710b3 100644
--- a/test/pro_tacts/admin/test_format.rb
+++ b/test/pro_tacts/admin/test_format.rb
@@ -3,10 +3,10 @@ require_relative "../../test_helper"
require "pro_tacts/admin/format"
class FormatTest < Minitest::Test
- def test_initials_takes_the_first_letter_of_up_to_two_words
- assert_equal "AL", ProTacts::Admin::Format.initials("Ada Lovelace")
+ def test_initials_is_the_first_letter_of_the_name
+ assert_equal "A", ProTacts::Admin::Format.initials("Ada Lovelace")
assert_equal "A", ProTacts::Admin::Format.initials("Ada")
- assert_equal "RE", ProTacts::Admin::Format.initials("Riverside Elementary Front Office")
+ assert_equal "R", ProTacts::Admin::Format.initials("Riverside Elementary Front Office")
end
def test_initials_of_a_blank_name_is_blank