class Friends::Friend

Constants

NICKNAME_PREFIX
SERIALIZATION_PREFIX

Attributes

likelihood_score[W]

The likelihood_score that an activity description that matches part of this friend's name does in fact refer to this friend. A higher likelihood_score means it is more likely to be this friend. For more information see the activity#highlight_friends and introvert#set_likelihood_score! methods.

location_name[RW]
n_activities[W]

The number of activities this friend is in. This is for internal use only and is set by the Introvert as needed.

name[RW]
tags[R]

Public Class Methods

deserialization_expectation() click to toggle source

@return [Regexp] the string of what we expected during deserialization

# File lib/friends/friend.rb, line 23
def self.deserialization_expectation
  "[Friend Name]"
end
deserialization_regex() click to toggle source

@return [Regexp] the regex for capturing groups in deserialization

# File lib/friends/friend.rb, line 17
def self.deserialization_regex
  # Note: this regex must be on one line because whitespace is important
  /(#{SERIALIZATION_PREFIX})?(?<name>[^\(\[@]*[^\(\[@\s])(\s+\(#{NICKNAME_PREFIX}(?<nickname_str>.+)\))?(\s+\[(?<location_name>[^\]]+)\])?(\s+(?<tags_str>(#{TAG_REGEX}\s*)+))?/ # rubocop:disable Layout/LineLength
end
new( name:, nickname_str: nil, location_name: nil, tags_str: nil ) click to toggle source

@param name [String] the name of the friend

# File lib/friends/friend.rb, line 28
def initialize(
  name:,
  nickname_str: nil,
  location_name: nil,
  tags_str: nil
)
  @name = name
  @nicknames = nickname_str&.split(" #{NICKNAME_PREFIX}") || []
  @location_name = location_name
  @tags = tags_str&.split(/\s+/) || []
end

Public Instance Methods

add_nickname(nickname) click to toggle source

Adds a nickname, ignoring duplicates. @param nickname [String] the nickname to add

# File lib/friends/friend.rb, line 82
def add_nickname(nickname)
  @nicknames << nickname
  @nicknames.uniq!
end
add_tag(tag) click to toggle source

Adds a tag, ignoring duplicates. @param tag [String] the tag to add, of the format: “@tag”

# File lib/friends/friend.rb, line 68
def add_tag(tag)
  @tags << tag
  @tags.uniq!
end
likelihood_score() click to toggle source
# File lib/friends/friend.rb, line 110
def likelihood_score
  defined?(@likelihood_score) ? @likelihood_score : 0
end
n_activities() click to toggle source
# File lib/friends/friend.rb, line 100
def n_activities
  defined?(@n_activities) ? @n_activities : 0
end
regexes_for_name() click to toggle source

@return [Array] a list of all regexes to match the name in a string

Example: [
  /Jacob\s+Morris\s+Evelyn/,
  /Jacob/
]

NOTE: For now we only match on full names or first names.

# File lib/friends/friend.rb, line 120
def regexes_for_name
  # We generously allow any amount of whitespace between parts of a name.
  splitter = "\\s+"

  # Create the list of regexes and return it.
  chunks = name.split(Regexp.new(splitter))

  # We check nicknames before first names because nicknames may contain
  # first names, as in "Amazing Grace" being a nickname for Grace Hopper.
  [
    chunks, # Match a full name with the highest priority.
    *@nicknames.map { |n| [n] },

    # Match a first name followed by a last name initial, period (that via
    # lookahead is *NOT* a part of an ellipsis), and then (via lookahead)
    # either:
    # - other punctuation that would indicate we want to swallow the period
    #   (note that we do not include closing parentheses in this list because
    #   they could be part of an offset sentence), OR
    # - anything, so long as the first alphabetical character afterwards is
    #   lowercase.
    # This matches the "Jake E." part of something like "Jake E. and I went
    # skiing." or "Jake E., Marie Curie, and I studied science." This
    # allows us to correctly count the period as part of the name when it's
    # in the middle of a sentence.
    (
      if chunks.size > 1
        [chunks.first, "#{chunks.last[0]}\\.(?!\\.\\.)(?=([,!?;:—]+|(?-i)[^A-Z]+[a-z]))"]
      end
    ),

    # If the above doesn't match, we check for just the first name and then
    # a last name initial. This matches the "Jake E" part of something like
    # "I went skiing with Jake E." This allows us to correctly exclude the
    # period from the name when it's at the end of a sentence.
    ([chunks.first, chunks.last[0]] if chunks.size > 1),

    *(1..chunks.size - 1).map { |i| chunks.take(i) }.reverse
  ].compact.map do |words|
    Friends::RegexBuilder.regex(words.join(splitter))
  end
end
remove_nickname(nickname) click to toggle source

@param nickname [String] the nickname to remove @raise [FriendsError] if the friend does not have the given nickname

# File lib/friends/friend.rb, line 89
def remove_nickname(nickname)
  unless @nicknames.include? nickname
    raise FriendsError, "Nickname \"#{nickname}\" not found for \"#{name}\""
  end

  @nicknames.delete(nickname)
end
remove_tag(tag) click to toggle source

@param tag [String] the tag to remove, of the format: “@tag”

# File lib/friends/friend.rb, line 74
def remove_tag(tag)
  raise FriendsError, "Tag \"#{tag}\" not found for \"#{name}\"" unless @tags.include? tag

  @tags.delete(tag)
end
serialize() click to toggle source

@return [String] the file serialization text for the friend

# File lib/friends/friend.rb, line 45
def serialize
  # Remove terminal effects for serialization.
  Paint.unpaint("#{SERIALIZATION_PREFIX}#{self}")
end
to_s() click to toggle source

@return [String] a string representing the friend's name and nicknames

# File lib/friends/friend.rb, line 51
def to_s
  unless @nicknames.empty?
    nickname_str = " (" +
                   @nicknames.map do |nickname|
                     "#{NICKNAME_PREFIX}#{Paint[nickname, :bold, :magenta]}"
                   end.join(" ") + ")"
  end

  location_str = " [#{Paint[@location_name, :bold, :yellow]}]" unless @location_name.nil?

  tag_str = " #{Paint[@tags.join(' '), :bold, :cyan]}" unless @tags.empty?

  "#{Paint[@name, :bold]}#{nickname_str}#{location_str}#{tag_str}"
end

Private Instance Methods

<=>(other) click to toggle source

Default sorting for an array of friends is alphabetical.

# File lib/friends/friend.rb, line 166
def <=>(other)
  name <=> other.name
end