Module: Bible270::Mentions

Defined in:
lib/bible270/mentions.rb

Overview

Finding "@someone" in a reflection, and deciding who that is.

There is no username field, so mentions match against the names readers already have. Two forms are accepted, both case-insensitively:

@andrew                 a first name
@andrew.vonderluft      first and last, joined by a dot

A mention that matches more than one reader resolves to nobody. Guessing would mean mailing the wrong person, and silence is easier to notice than a misdelivered message: the writer sees their mention unlinked.

Constant Summary collapse

PATTERN =

Deliberately narrow: letters, digits, dots, hyphens and apostrophes, so "@andrew," and "@andrew." at the end of a sentence still work, and an email address in the body is not mistaken for a mention.

%r{(?<![\w@])@([\p{L}\p{N}][\p{L}\p{N}.'-]{0,60})}
MAX_PER_COMMENT =
10

Class Method Summary collapse

Class Method Details

.extract(text) ⇒ Object

The handles written in a piece of text, normalised and de-duplicated.



26
27
28
29
30
31
32
33
34
# File 'lib/bible270/mentions.rb', line 26

def extract(text)
  return [] if text.nil?

  text.to_s.scan(PATTERN).flatten
    .map { |handle| normalize(handle) }
    .reject(&:empty?)
    .uniq
    .first(MAX_PER_COMMENT)
end

.handles_for(first_name, last_name) ⇒ Object

Every handle a reader answers to, given their names.



42
43
44
45
46
47
48
49
50
# File 'lib/bible270/mentions.rb', line 42

def handles_for(first_name, last_name)
  first = normalize(first_name)
  last  = normalize(last_name)
  return [] if first.empty?

  handles = [first]
  handles << "#{first}.#{last}" unless last.empty?
  handles.uniq
end

.highlight(text) ⇒ Object

Wraps each mention so a view can style it, leaving the text otherwise untouched. The block receives the handle and returns the replacement.



63
64
65
66
67
68
69
70
# File 'lib/bible270/mentions.rb', line 63

def highlight(text)
  return text.to_s if text.nil?

  text.to_s.gsub(PATTERN) do
    handle = Regexp.last_match(1)
    yield(normalize(handle), "@#{handle}")
  end
end

.normalize(handle) ⇒ Object

Trailing punctuation is dropped so "@andrew." and "@andrew" agree.



37
38
39
# File 'lib/bible270/mentions.rb', line 37

def normalize(handle)
  handle.to_s.downcase.gsub(%r{[^\p{L}\p{N}.'-]}, '').sub(%r{[.'-]+\z}, '')
end

.preferred_handle(first_name, last_name, ambiguous: false) ⇒ Object

The form to write when there is more than one reader with the same first name: the longer handle is unambiguous.



54
55
56
57
58
59
# File 'lib/bible270/mentions.rb', line 54

def preferred_handle(first_name, last_name, ambiguous: false)
  handles = handles_for(first_name, last_name)
  return nil if handles.empty?

  ambiguous ? handles.last : handles.first
end