class String
def truncate_bytes(truncate_to, omission: "…")
to "…", for a total length not exceeding truncate_to.
The truncated text ends with the :omission string, defaulting
=> "🔪🔪🔪🔪…"
>> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".truncate_bytes(20)
=> 80
>> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".bytesize
=> 20
>> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".size
characters.
grapheme clusters ("perceptual characters") by truncating at combining
breaking string encoding by splitting multibyte characters or breaking
Truncates +text+ to at most truncate_to bytes in length without
def truncate_bytes(truncate_to, omission: "…") omission ||= "" case when bytesize <= truncate_to dup when omission.bytesize > truncate_to raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{truncate_to} bytes" when omission.bytesize == truncate_to omission.dup else self.class.new.tap do |cut| cut_at = truncate_to - omission.bytesize each_grapheme_cluster do |grapheme| if cut.bytesize + grapheme.bytesize <= cut_at cut << grapheme else break end end cut << omission end end end