Class: Friends::Friend

Inherits:
Object
  • Object
show all
Extended by:
Serializable
Defined in:
lib/friends/friend.rb

Constant Summary collapse

SERIALIZATION_PREFIX =
"- "
NICKNAME_PREFIX =
"a.k.a. "

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Serializable

deserialize

Constructor Details

#initialize(name:, nickname_str: nil, location_name: nil, tags_str: nil) ⇒ Friend

Returns a new instance of Friend.

Parameters:

  • name (String)

    the name of the friend



28
29
30
31
32
33
34
35
36
37
38
# 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

Instance Attribute Details

#likelihood_scoreObject



110
111
112
# File 'lib/friends/friend.rb', line 110

def likelihood_score
  defined?(@likelihood_score) ? @likelihood_score : 0
end

#location_nameObject

Returns the value of attribute location_name.



41
42
43
# File 'lib/friends/friend.rb', line 41

def location_name
  @location_name
end

#n_activitiesObject



100
101
102
# File 'lib/friends/friend.rb', line 100

def n_activities
  defined?(@n_activities) ? @n_activities : 0
end

#nameObject

Returns the value of attribute name.



40
41
42
# File 'lib/friends/friend.rb', line 40

def name
  @name
end

#tagsObject (readonly)

Returns the value of attribute tags.



42
43
44
# File 'lib/friends/friend.rb', line 42

def tags
  @tags
end

Class Method Details

.deserialization_expectationRegexp

Returns the string of what we expected during deserialization.

Returns:

  • (Regexp)

    the string of what we expected during deserialization



23
24
25
# File 'lib/friends/friend.rb', line 23

def self.deserialization_expectation
  "[Friend Name]"
end

.deserialization_regexRegexp

Returns the regex for capturing groups in deserialization.

Returns:

  • (Regexp)

    the regex for capturing groups in deserialization



17
18
19
20
# 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

Instance Method Details

#add_nickname(nickname) ⇒ Object

Adds a nickname, ignoring duplicates.

Parameters:

  • nickname (String)

    the nickname to add



82
83
84
85
# File 'lib/friends/friend.rb', line 82

def add_nickname(nickname)
  @nicknames << nickname
  @nicknames.uniq!
end

#add_tag(tag) ⇒ Object

Adds a tag, ignoring duplicates.

Parameters:

  • tag (String)

    the tag to add, of the format: “@tag”



68
69
70
71
# File 'lib/friends/friend.rb', line 68

def add_tag(tag)
  @tags << tag
  @tags.uniq!
end

#regexes_for_nameArray

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

Returns:

  • (Array)

    a list of all regexes to match the name in a string Example: [

    /Jacob\s+Morris\s+Evelyn/,
    /Jacob/
    

    ]



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# 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) ⇒ Object

Parameters:

  • nickname (String)

    the nickname to remove

Raises:

  • (FriendsError)

    if the friend does not have the given nickname



89
90
91
92
93
94
95
# 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) ⇒ Object

Parameters:

  • tag (String)

    the tag to remove, of the format: “@tag”

Raises:



74
75
76
77
78
# 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

#serializeString

Returns the file serialization text for the friend.

Returns:

  • (String)

    the file serialization text for the friend



45
46
47
48
# File 'lib/friends/friend.rb', line 45

def serialize
  # Remove terminal effects for serialization.
  Paint.unpaint("#{SERIALIZATION_PREFIX}#{self}")
end

#to_sString

Returns a string representing the friend’s name and nicknames.

Returns:

  • (String)

    a string representing the friend’s name and nicknames



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 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