Class: Friends::Event

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

Direct Known Subclasses

Activity, Note

Constant Summary collapse

SERIALIZATION_PREFIX =
"- ".freeze
DATE_PARTITION =
": ".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Serializable

deserialize

Constructor Details

#initialize(str: "") ⇒ Activity

Returns the new activity.

Parameters:

  • str (String) (defaults to: "")

    the text of the activity, of one of the formats: “<date>: <description>” “<date>” (Program will prompt for description.) “<description>” (The current date will be used by default.)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/friends/event.rb', line 30

def initialize(str: "")
  # Partition lets us parse "Today" and "Today: I awoke." identically.
  date_s, _, description = str.partition(DATE_PARTITION)

  time = if date_s =~ /^\d{4}-\d{2}-\d{2}$/
           Time.parse(date_s)
         else
           # If the user inputed a non YYYY-MM-DD format, asssume
           # it is in the past.
           past_time = Chronic.parse(date_s, context: :past)

           # If there's no year, Chronic will sometimes parse the date
           # as being the next occurrence of that date in the future.
           # Instead, we want to subtract one year to make it the last
           # occurrence of the date in the past.
           # NOTE: This is a hacky workaround for the fact that
           # Chronic's `context: :past` doesn't actually work. We should
           # remove this when that behavior is fixed.
           if past_time && past_time > Time.now
             Time.local(past_time.year - 1, past_time.month, past_time.day)
           else
             past_time
           end
         end

  if time
    @date = time.to_date
    @description = description
  else
    # If the user didn't input a date, we fall back to the current date.
    @date = Date.today
    @description = str # Use str in case DATE_PARTITION occurred naturally.
  end
end

Instance Attribute Details

#dateObject (readonly)

Returns the value of attribute date


65
66
67
# File 'lib/friends/event.rb', line 65

def date
  @date
end

#descriptionObject

Returns the value of attribute description


66
67
68
# File 'lib/friends/event.rb', line 66

def description
  @description
end

Class Method Details

.deserialization_regexRegexp

Returns the regex for capturing groups in deserialization.

Returns:

  • (Regexp)

    the regex for capturing groups in deserialization


21
22
23
# File 'lib/friends/event.rb', line 21

def self.deserialization_regex
  /(#{SERIALIZATION_PREFIX})?(?<str>.+)?/
end

Instance Method Details

#friend_namesArray

Find the names of all friends in this description.

Returns:

  • (Array)

    list of all friend names in the description


159
160
161
# File 'lib/friends/event.rb', line 159

def friend_names
  @description.scan(/(?<=\*\*)\w[^\*]*(?=\*\*)/).uniq
end

#highlight_description(introvert:) ⇒ Object

Modify the description to turn inputted friend names (e.g. “Jacob” or “Jacob Evelyn”) into full asterisk'd names (e.g. “**Jacob Evelyn**”) and inputted location names (e.g. “Atlantis”) into full underscore'd names (e.g. “Atlantis”).

Parameters:

  • introvert (Introvert)

    used to access internal data structures to perform object matching


105
106
107
108
# File 'lib/friends/event.rb', line 105

def highlight_description(introvert:)
  highlight_locations(introvert: introvert)
  highlight_friends(introvert: introvert)
end

#includes_friend?(friend) ⇒ Boolean

Returns true iff this activity includes the given friend.

Parameters:

  • friend (Friend)

    the friend to test

Returns:

  • (Boolean)

    true iff this activity includes the given friend


142
143
144
# File 'lib/friends/event.rb', line 142

def includes_friend?(friend)
  friend_names.include? friend.name
end

#includes_location?(location) ⇒ Boolean

Returns true iff this activity includes the given location.

Parameters:

  • location (Location)

    the location to test

Returns:

  • (Boolean)

    true iff this activity includes the given location


136
137
138
# File 'lib/friends/event.rb', line 136

def includes_location?(location)
  @description.scan(/(?<=_)[^_]+(?=_)/).include? location.name
end

#includes_tag?(tag) ⇒ Boolean

Returns true iff this activity includes the given tag.

Parameters:

  • tag (String)

    the tag to test, of the form “@tag”

Returns:

  • (Boolean)

    true iff this activity includes the given tag


148
149
150
# File 'lib/friends/event.rb', line 148

def includes_tag?(tag)
  tags.include? tag.downcase
end

#location_namesArray

Find the names of all locations in this description.

Returns:

  • (Array)

    list of all location names in the description


165
166
167
# File 'lib/friends/event.rb', line 165

def location_names
  @description.scan(/(?<=_)\w[^_]*(?=_)/).uniq
end

#serializeString

Returns the file serialization text for the activity.

Returns:

  • (String)

    the file serialization text for the activity


95
96
97
# File 'lib/friends/event.rb', line 95

def serialize
  "#{SERIALIZATION_PREFIX}#{date}: #{description}"
end

#tagsSet

Returns all tags in this activity (including the “@”).

Returns:

  • (Set)

    all tags in this activity (including the “@”)


153
154
155
# File 'lib/friends/event.rb', line 153

def tags
  Set.new(@description.scan(TAG_REGEX).map(&:downcase))
end

#to_sString

Returns the command-line display text for the activity.

Returns:

  • (String)

    the command-line display text for the activity


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/friends/event.rb', line 69

def to_s
  date_s = Paint[date, :bold]
  description_s = description.to_s
  # rubocop:disable Lint/AssignmentInCondition
  while match = description_s.match(/\*\*([^\*]+)\*\*/)
    # rubocop:enable Lint/AssignmentInCondition
    description_s = "#{match.pre_match}"\
                    "#{Paint[match[1], :bold, :magenta]}"\
                    "#{match.post_match}"
  end

  # rubocop:disable Lint/AssignmentInCondition
  while match = description_s.match(/_([^_]+)_/)
    # rubocop:enable Lint/AssignmentInCondition
    description_s = "#{match.pre_match}"\
                    "#{Paint[match[1], :bold, :yellow]}"\
                    "#{match.post_match}"
  end

  description_s = description_s.
                  gsub(TAG_REGEX, Paint['\0', :bold, :cyan])

  "#{date_s}: #{description_s}"
end

#update_friend_name(old_name:, new_name:) ⇒ String?

Updates a friend's old_name to their new_name

Parameters:

  • old_name (String)
  • new_name (String)

Returns:

  • (String)

    if name found in description

  • (nil)

    if no change was made


115
116
117
118
119
120
# File 'lib/friends/event.rb', line 115

def update_friend_name(old_name:, new_name:)
  @description = @description.gsub(
    Regexp.new("(?<=\\*\\*)#{old_name}(?=\\*\\*)"),
    new_name
  )
end

#update_location_name(old_name:, new_name:) ⇒ String?

Updates a location's old_name to their new_name

Parameters:

  • old_name (String)
  • new_name (String)

Returns:

  • (String)

    if name found in description

  • (nil)

    if no change was made


127
128
129
130
131
132
# File 'lib/friends/event.rb', line 127

def update_location_name(old_name:, new_name:)
  @description = @description.gsub(
    Regexp.new("(?<=_)#{old_name}(?=_)"),
    new_name
  )
end