friends 0.35 → 0.36
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +20 -0
- data/README.md +103 -6
- data/bin/friends +12 -16
- data/lib/friends/commands/add.rb +5 -17
- data/lib/friends/commands/edit.rb +13 -2
- data/lib/friends/commands/list.rb +4 -4
- data/lib/friends/commands/remove.rb +2 -4
- data/lib/friends/commands/rename.rb +2 -4
- data/lib/friends/commands/set.rb +1 -2
- data/lib/friends/commands/stats.rb +1 -7
- data/lib/friends/commands/suggest.rb +1 -8
- data/lib/friends/commands/update.rb +12 -13
- data/lib/friends/event.rb +2 -2
- data/lib/friends/introvert.rb +122 -110
- data/lib/friends/version.rb +1 -1
- data/test/commands/clean_spec.rb +88 -1
- data/test/commands/edit_spec.rb +125 -17
- data/test/commands/rename/friend_spec.rb +7 -0
- data/test/commands/rename/location_spec.rb +8 -0
- data/test/commands/set/location_spec.rb +2 -2
- data/test/editor +15 -0
- data/test/helper.rb +2 -2
- metadata +4 -2
@@ -8,13 +8,6 @@ command :suggest do |suggest|
|
|
8
8
|
type: Stripped
|
9
9
|
|
10
10
|
suggest.action do |_, options|
|
11
|
-
|
12
|
-
|
13
|
-
puts "Distant friend: "\
|
14
|
-
"#{Paint[suggestions[:distant].sample || 'None found', :bold, :magenta]}"
|
15
|
-
puts "Moderate friend: "\
|
16
|
-
"#{Paint[suggestions[:moderate].sample || 'None found', :bold, :magenta]}"
|
17
|
-
puts "Close friend: "\
|
18
|
-
"#{Paint[suggestions[:close].sample || 'None found', :bold, :magenta]}"
|
11
|
+
@introvert.suggest(location_name: options[:in])
|
19
12
|
end
|
20
13
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
desc "Updates the `friends` program"
|
4
4
|
command :update do |update|
|
5
|
-
update.action do
|
5
|
+
update.action do |global_options|
|
6
6
|
# rubocop:disable Lint/AssignmentInCondition
|
7
7
|
if match = `gem search friends`.match(/^friends\s\(([^\)]+)\)$/)
|
8
8
|
# rubocop:enable Lint/AssignmentInCondition
|
@@ -10,19 +10,18 @@ command :update do |update|
|
|
10
10
|
if Semverse::Version.coerce(remote_version) >
|
11
11
|
Semverse::Version.coerce(Friends::VERSION)
|
12
12
|
`gem update friends && gem cleanup friends`
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
13
|
+
|
14
|
+
unless global_options[:quiet]
|
15
|
+
if $?.success?
|
16
|
+
puts Paint["Updated to friends #{remote_version}", :bold, :green]
|
17
|
+
else
|
18
|
+
puts Paint["Error updating to friends version #{remote_version}", :bold, :red]
|
19
|
+
end
|
20
|
+
end
|
22
21
|
else
|
23
|
-
|
24
|
-
"Already up-to-date (#{Friends::VERSION})", :bold, :green
|
25
|
-
|
22
|
+
unless global_options[:quiet]
|
23
|
+
puts Paint["Already up-to-date (#{Friends::VERSION})", :bold, :green]
|
24
|
+
end
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
data/lib/friends/event.rb
CHANGED
@@ -156,13 +156,13 @@ module Friends
|
|
156
156
|
# Find the names of all friends in this description.
|
157
157
|
# @return [Array] list of all friend names in the description
|
158
158
|
def friend_names
|
159
|
-
@
|
159
|
+
@description.scan(/(?<=\*\*)\w[^\*]*(?=\*\*)/).uniq
|
160
160
|
end
|
161
161
|
|
162
162
|
# Find the names of all locations in this description.
|
163
163
|
# @return [Array] list of all location names in the description
|
164
164
|
def location_names
|
165
|
-
@
|
165
|
+
@description.scan(/(?<=_)\w[^_]*(?=_)/).uniq
|
166
166
|
end
|
167
167
|
|
168
168
|
private
|
data/lib/friends/introvert.rb
CHANGED
@@ -15,15 +15,16 @@ require "friends/friends_error"
|
|
15
15
|
|
16
16
|
module Friends
|
17
17
|
class Introvert
|
18
|
-
DEFAULT_FILENAME = "./friends.md".freeze
|
19
18
|
ACTIVITIES_HEADER = "### Activities:".freeze
|
20
19
|
NOTES_HEADER = "### Notes:".freeze
|
21
20
|
FRIENDS_HEADER = "### Friends:".freeze
|
22
21
|
LOCATIONS_HEADER = "### Locations:".freeze
|
23
22
|
|
24
23
|
# @param filename [String] the name of the friends Markdown file
|
25
|
-
|
24
|
+
# @param quiet [Boolean] true iff we should suppress all STDOUT output
|
25
|
+
def initialize(filename:, quiet:)
|
26
26
|
@filename = filename
|
27
|
+
@quiet = quiet
|
27
28
|
|
28
29
|
# Read in the input file. It's easier to do this now and optimize later
|
29
30
|
# than try to overly be clever about what we read and write.
|
@@ -31,7 +32,25 @@ module Friends
|
|
31
32
|
end
|
32
33
|
|
33
34
|
# Write out the friends file with cleaned/sorted data.
|
34
|
-
|
35
|
+
# @param clean_command [Boolean] true iff the command the user
|
36
|
+
# executed is `friends clean`; false if this is called as the
|
37
|
+
# result of another command
|
38
|
+
def clean(clean_command:)
|
39
|
+
friend_names = Set.new(@friends.map(&:name))
|
40
|
+
location_names = Set.new(@locations.map(&:name))
|
41
|
+
|
42
|
+
# Iterate through all events and add missing friends and
|
43
|
+
# locations.
|
44
|
+
(@activities + @notes).each do |event|
|
45
|
+
event.friend_names.each do |name|
|
46
|
+
add_friend(name: name) unless friend_names.include? name
|
47
|
+
end
|
48
|
+
|
49
|
+
event.location_names.each do |name|
|
50
|
+
add_location(name: name) unless location_names.include? name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
35
54
|
File.open(@filename, "w") do |file|
|
36
55
|
file.puts(ACTIVITIES_HEADER)
|
37
56
|
stable_sort(@activities).each { |act| file.puts(act.serialize) }
|
@@ -46,13 +65,14 @@ module Friends
|
|
46
65
|
@locations.sort.each { |location| file.puts(location.serialize) }
|
47
66
|
end
|
48
67
|
|
49
|
-
|
68
|
+
# This is a special-case piece of code that lets us print a message that
|
69
|
+
# includes the filename when `friends clean` is called.
|
70
|
+
safe_puts "File cleaned: \"#{@filename}\"" if clean_command
|
50
71
|
end
|
51
72
|
|
52
73
|
# Add a friend.
|
53
74
|
# @param name [String] the name of the friend to add
|
54
75
|
# @raise [FriendsError] when a friend with that name is already in the file
|
55
|
-
# @return [Friend] the added friend
|
56
76
|
def add_friend(name:)
|
57
77
|
if @friends.any? { |friend| friend.name == name }
|
58
78
|
raise FriendsError, "Friend named \"#{name}\" already exists"
|
@@ -62,32 +82,45 @@ module Friends
|
|
62
82
|
|
63
83
|
@friends << friend
|
64
84
|
|
65
|
-
|
85
|
+
safe_puts "Friend added: \"#{friend.name}\""
|
66
86
|
end
|
67
87
|
|
68
88
|
# Add an activity.
|
69
89
|
# @param serialization [String] the serialized activity
|
70
|
-
# @return [Activity] the added activity
|
71
90
|
def add_activity(serialization:)
|
72
91
|
Activity.deserialize(serialization).tap do |activity|
|
73
|
-
|
92
|
+
# If there's no description, prompt the user for one.
|
93
|
+
if activity.description.nil? || activity.description.empty?
|
94
|
+
activity.description = Readline.readline(activity.to_s)
|
95
|
+
end
|
96
|
+
|
97
|
+
activity.highlight_description(introvert: self)
|
98
|
+
|
74
99
|
@activities.unshift(activity)
|
100
|
+
|
101
|
+
safe_puts "Activity added: \"#{activity}\""
|
75
102
|
end
|
76
103
|
end
|
77
104
|
|
78
105
|
# Add a note.
|
79
106
|
# @param serialization [String] the serialized note
|
80
|
-
# @return [Note] the added note
|
81
107
|
def add_note(serialization:)
|
82
108
|
Note.deserialize(serialization).tap do |note|
|
83
|
-
|
109
|
+
# If there's no description, prompt the user for one.
|
110
|
+
if note.description.nil? || note.description.empty?
|
111
|
+
note.description = Readline.readline(note.to_s)
|
112
|
+
end
|
113
|
+
|
114
|
+
note.highlight_description(introvert: self)
|
115
|
+
|
84
116
|
@notes.unshift(note)
|
117
|
+
|
118
|
+
safe_puts "Note added: \"#{note}\""
|
85
119
|
end
|
86
120
|
end
|
87
121
|
|
88
122
|
# Add a location.
|
89
123
|
# @param name [String] the serialized location
|
90
|
-
# @return [Location] the added location
|
91
124
|
# @raise [FriendsError] if a location with that name already exists
|
92
125
|
def add_location(name:)
|
93
126
|
if @locations.any? { |location| location.name == name }
|
@@ -98,7 +131,7 @@ module Friends
|
|
98
131
|
|
99
132
|
@locations << location
|
100
133
|
|
101
|
-
location # Return the added location.
|
134
|
+
safe_puts "Location added: \"#{location.name}\"" # Return the added location.
|
102
135
|
end
|
103
136
|
|
104
137
|
# Set a friend's location.
|
@@ -106,39 +139,38 @@ module Friends
|
|
106
139
|
# @param location_name [String] the name of an existing location
|
107
140
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
108
141
|
# @raise [FriendsError] if 0 or 2+ locations match the given location name
|
109
|
-
# @return [Friend] the modified friend
|
110
142
|
def set_location(name:, location_name:)
|
111
143
|
friend = thing_with_name_in(:friend, name)
|
112
144
|
location = thing_with_name_in(:location, location_name)
|
113
145
|
friend.location_name = location.name
|
114
|
-
|
146
|
+
|
147
|
+
safe_puts "#{friend.name}'s location set to: \"#{location.name}\""
|
115
148
|
end
|
116
149
|
|
117
150
|
# Rename an existing friend.
|
118
151
|
# @param old_name [String] the name of the friend
|
119
152
|
# @param new_name [String] the new name of the friend
|
120
153
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
121
|
-
# @return [Friend] the existing friend
|
122
154
|
def rename_friend(old_name:, new_name:)
|
123
155
|
friend = thing_with_name_in(:friend, old_name)
|
124
|
-
@activities.each do |
|
125
|
-
|
156
|
+
(@activities + @notes).each do |event|
|
157
|
+
event.update_friend_name(old_name: friend.name, new_name: new_name)
|
126
158
|
end
|
127
159
|
friend.name = new_name
|
128
|
-
|
160
|
+
|
161
|
+
safe_puts "Name changed: \"#{friend}\""
|
129
162
|
end
|
130
163
|
|
131
164
|
# Rename an existing location.
|
132
165
|
# @param old_name [String] the name of the location
|
133
166
|
# @param new_name [String] the new name of the location
|
134
167
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
135
|
-
# @return [Location] the existing location
|
136
168
|
def rename_location(old_name:, new_name:)
|
137
169
|
loc = thing_with_name_in(:location, old_name)
|
138
170
|
|
139
|
-
# Update locations in activities.
|
140
|
-
@activities.each do |
|
141
|
-
|
171
|
+
# Update locations in activities and notes.
|
172
|
+
(@activities + @notes).each do |event|
|
173
|
+
event.update_location_name(old_name: loc.name, new_name: new_name)
|
142
174
|
end
|
143
175
|
|
144
176
|
# Update locations of friends.
|
@@ -147,29 +179,30 @@ module Friends
|
|
147
179
|
end
|
148
180
|
|
149
181
|
loc.name = new_name # Update location itself.
|
150
|
-
|
182
|
+
|
183
|
+
safe_puts "Location renamed: \"#{loc.name}\""
|
151
184
|
end
|
152
185
|
|
153
186
|
# Add a nickname to an existing friend.
|
154
187
|
# @param name [String] the name of the friend
|
155
188
|
# @param nickname [String] the nickname to add to the friend
|
156
189
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
157
|
-
# @return [Friend] the existing friend
|
158
190
|
def add_nickname(name:, nickname:)
|
159
191
|
friend = thing_with_name_in(:friend, name)
|
160
192
|
friend.add_nickname(nickname)
|
161
|
-
|
193
|
+
|
194
|
+
safe_puts "Nickname added: \"#{friend}\""
|
162
195
|
end
|
163
196
|
|
164
197
|
# Add a tag to an existing friend.
|
165
198
|
# @param name [String] the name of the friend
|
166
199
|
# @param tag [String] the tag to add to the friend, of the form: "@tag"
|
167
200
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
168
|
-
# @return [Friend] the existing friend
|
169
201
|
def add_tag(name:, tag:)
|
170
202
|
friend = thing_with_name_in(:friend, name)
|
171
203
|
friend.add_tag(tag)
|
172
|
-
|
204
|
+
|
205
|
+
safe_puts "Tag added to friend: \"#{friend}\""
|
173
206
|
end
|
174
207
|
|
175
208
|
# Remove a tag from an existing friend.
|
@@ -177,11 +210,11 @@ module Friends
|
|
177
210
|
# @param tag [String] the tag to remove from the friend, of the form: "@tag"
|
178
211
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
179
212
|
# @raise [FriendsError] if the friend does not have the given nickname
|
180
|
-
# @return [Friend] the existing friend
|
181
213
|
def remove_tag(name:, tag:)
|
182
214
|
friend = thing_with_name_in(:friend, name)
|
183
215
|
friend.remove_tag(tag)
|
184
|
-
|
216
|
+
|
217
|
+
safe_puts "Tag removed from friend: \"#{friend}\""
|
185
218
|
end
|
186
219
|
|
187
220
|
# Remove a nickname from an existing friend.
|
@@ -189,11 +222,11 @@ module Friends
|
|
189
222
|
# @param nickname [String] the nickname to remove from the friend
|
190
223
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
191
224
|
# @raise [FriendsError] if the friend does not have the given nickname
|
192
|
-
# @return [Friend] the existing friend
|
193
225
|
def remove_nickname(name:, nickname:)
|
194
226
|
friend = thing_with_name_in(:friend, name)
|
195
227
|
friend.remove_nickname(nickname)
|
196
|
-
|
228
|
+
|
229
|
+
safe_puts "Nickname removed: \"#{friend}\""
|
197
230
|
end
|
198
231
|
|
199
232
|
# List all friend names in the friends file.
|
@@ -203,7 +236,6 @@ module Friends
|
|
203
236
|
# unfiltered
|
204
237
|
# @param verbose [Boolean] true iff we should output friend names with
|
205
238
|
# nicknames, locations, and tags; false for names only
|
206
|
-
# @return [Array] a list of all friend names
|
207
239
|
def list_friends(location_name:, tagged:, verbose:)
|
208
240
|
fs = @friends
|
209
241
|
|
@@ -220,69 +252,41 @@ module Friends
|
|
220
252
|
end
|
221
253
|
end
|
222
254
|
|
223
|
-
verbose ? fs.map(&:to_s) : fs.map(&:name)
|
255
|
+
safe_puts(verbose ? fs.map(&:to_s) : fs.map(&:name))
|
224
256
|
end
|
225
257
|
|
226
258
|
# List your favorite friends.
|
227
259
|
# @param limit [Integer] the number of favorite friends to return
|
228
|
-
# @return [Array] a list of the favorite friends' names and activity
|
229
|
-
# counts
|
230
260
|
def list_favorite_friends(limit:)
|
231
261
|
list_favorite_things(:friend, limit: limit)
|
232
262
|
end
|
233
263
|
|
234
264
|
# List your favorite friends.
|
235
265
|
# @param limit [Integer] the number of favorite locations to return
|
236
|
-
# @return [Array] a list of the favorite locations' names and activity
|
237
|
-
# counts
|
238
266
|
def list_favorite_locations(limit:)
|
239
267
|
list_favorite_things(:location, limit: limit)
|
240
268
|
end
|
241
269
|
|
242
270
|
# See `list_events` for all of the parameters we can pass.
|
243
|
-
# @return [Array] a list of all activities' text values
|
244
271
|
def list_activities(**args)
|
245
272
|
list_events(events: @activities, **args)
|
246
273
|
end
|
247
274
|
|
248
275
|
# See `list_events` for all of the parameters we can pass.
|
249
|
-
# @return [Array] a list of all notes' text values
|
250
276
|
def list_notes(**args)
|
251
277
|
list_events(events: @notes, **args)
|
252
278
|
end
|
253
279
|
|
254
280
|
# List all location names in the friends file.
|
255
|
-
# @return [Array] a list of all location names
|
256
281
|
def list_locations
|
257
|
-
@locations.map(&:name)
|
282
|
+
safe_puts @locations.map(&:name)
|
258
283
|
end
|
259
284
|
|
260
285
|
# @param from [Array] containing any of: ["activities", "friends", "notes"]
|
261
286
|
# If not empty, limits the tags returned to only those from either
|
262
287
|
# activities, notes, or friends.
|
263
|
-
# @return [Array] a sorted list of all tags in activity descriptions
|
264
288
|
def list_tags(from:)
|
265
|
-
|
266
|
-
|
267
|
-
if from.empty? || from.include?("activities")
|
268
|
-
@activities.each_with_object(output) do |activity, set|
|
269
|
-
set.merge(activity.tags)
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
if from.empty? || from.include?("notes")
|
274
|
-
@notes.each_with_object(output) do |note, set|
|
275
|
-
set.merge(note.tags)
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
if from.empty? || from.include?("friends")
|
280
|
-
@friends.each_with_object(output) do |friend, set|
|
281
|
-
set.merge(friend.tags)
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
output.sort_by(&:downcase)
|
289
|
+
safe_puts tags(from: from).sort_by(&:downcase)
|
286
290
|
end
|
287
291
|
|
288
292
|
# Find data points for graphing activities over time.
|
@@ -305,7 +309,6 @@ module Friends
|
|
305
309
|
# unfiltered
|
306
310
|
# @param since_date [Date] a date on or after which to find activities, or nil for unfiltered
|
307
311
|
# @param until_date [Date] a date before or on which to find activities, or nil for unfiltered
|
308
|
-
# @return [Hash{String => Integer}]
|
309
312
|
# @raise [FriendsError] if friend, location or tag cannot be found or
|
310
313
|
# is ambiguous
|
311
314
|
def graph(with:, location_name:, tagged:, since_date:, until_date:)
|
@@ -350,7 +353,6 @@ module Friends
|
|
350
353
|
#
|
351
354
|
# @param location_name [String] the name of a location to filter by, or nil
|
352
355
|
# for unfiltered
|
353
|
-
# @return [Hash{String => Array<String>}]
|
354
356
|
def suggest(location_name:)
|
355
357
|
# Filter our friends by location if necessary.
|
356
358
|
fs = @friends
|
@@ -359,22 +361,25 @@ module Friends
|
|
359
361
|
# Sort our friends, with the least favorite friend first.
|
360
362
|
sorted_friends = fs.sort_by(&:n_activities)
|
361
363
|
|
362
|
-
output = Hash.new { |h, k| h[k] = [] }
|
363
|
-
|
364
364
|
# Set initial value in case there are no friends and the while loop is
|
365
365
|
# never entered.
|
366
|
-
|
366
|
+
distant_friend_names = []
|
367
367
|
|
368
368
|
# First, get not-so-good friends.
|
369
369
|
while !sorted_friends.empty? && sorted_friends.first.n_activities < 2
|
370
|
-
|
370
|
+
distant_friend_names << sorted_friends.shift.name
|
371
371
|
end
|
372
372
|
|
373
|
-
|
374
|
-
|
375
|
-
|
373
|
+
moderate_friend_names = sorted_friends.slice!(0, sorted_friends.size * 3 / 4).
|
374
|
+
map!(&:name)
|
375
|
+
close_friend_names = sorted_friends.map!(&:name)
|
376
376
|
|
377
|
-
|
377
|
+
safe_puts "Distant friend: "\
|
378
|
+
"#{Paint[distant_friend_names.sample || 'None found', :bold, :magenta]}"
|
379
|
+
safe_puts "Moderate friend: "\
|
380
|
+
"#{Paint[moderate_friend_names.sample || 'None found', :bold, :magenta]}"
|
381
|
+
safe_puts "Close friend: "\
|
382
|
+
"#{Paint[close_friend_names.sample || 'None found', :bold, :magenta]}"
|
378
383
|
end
|
379
384
|
|
380
385
|
###################################################################
|
@@ -452,41 +457,50 @@ module Friends
|
|
452
457
|
end
|
453
458
|
end
|
454
459
|
|
455
|
-
|
456
|
-
|
457
|
-
@friends.size
|
458
|
-
|
460
|
+
def stats
|
461
|
+
safe_puts "Total activities: #{@activities.size}"
|
462
|
+
safe_puts "Total friends: #{@friends.size}"
|
463
|
+
safe_puts "Total locations: #{@locations.size}"
|
464
|
+
safe_puts "Total notes: #{@notes.size}"
|
465
|
+
safe_puts "Total tags: #{tags.size}"
|
459
466
|
|
460
|
-
|
461
|
-
def total_activities
|
462
|
-
@activities.size
|
463
|
-
end
|
467
|
+
events = @activities + @notes
|
464
468
|
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
+
elapsed_days = if events.size < 2
|
470
|
+
0
|
471
|
+
else
|
472
|
+
sorted_events = events.sort
|
473
|
+
(sorted_events.first.date - sorted_events.last.date).to_i
|
474
|
+
end
|
469
475
|
|
470
|
-
|
471
|
-
def total_notes
|
472
|
-
@notes.size
|
476
|
+
safe_puts "Total time elapsed: #{elapsed_days} day#{'s' if elapsed_days != 1}"
|
473
477
|
end
|
474
478
|
|
475
|
-
|
476
|
-
def total_tags
|
477
|
-
list_tags(from: []).size
|
478
|
-
end
|
479
|
+
private
|
479
480
|
|
480
|
-
#
|
481
|
-
#
|
482
|
-
def
|
483
|
-
|
484
|
-
return 0 if events.size < 2
|
485
|
-
sorted_events = events.sort
|
486
|
-
(sorted_events.first.date - sorted_events.last.date).to_i
|
481
|
+
# Print the message unless we're in `quiet` mode.
|
482
|
+
# @param str [String] a message to print
|
483
|
+
def safe_puts(str)
|
484
|
+
puts str unless @quiet
|
487
485
|
end
|
488
486
|
|
489
|
-
|
487
|
+
# @param from [Array] containing any of: ["activities", "friends", "notes"]
|
488
|
+
# If not empty, limits the tags returned to only those from either
|
489
|
+
# activities, notes, or friends.
|
490
|
+
# @return [Set] the set of tags present in the given things
|
491
|
+
def tags(from: [])
|
492
|
+
Set.new.tap do |output|
|
493
|
+
if from.empty? || from.include?("activities")
|
494
|
+
@activities.each { |activity| output.merge(activity.tags) }
|
495
|
+
end
|
496
|
+
|
497
|
+
@notes.each { |note| output.merge(note.tags) } if from.empty? || from.include?("notes")
|
498
|
+
|
499
|
+
if from.empty? || from.include?("friends")
|
500
|
+
@friends.each { |friend| output.merge(friend.tags) }
|
501
|
+
end
|
502
|
+
end
|
503
|
+
end
|
490
504
|
|
491
505
|
# List all event details.
|
492
506
|
# @param events [Array<Event>] the base events to list, either @activities or @notes
|
@@ -500,7 +514,6 @@ module Friends
|
|
500
514
|
# unfiltered
|
501
515
|
# @param since_date [Date] a date on or after which to find events, or nil for unfiltered
|
502
516
|
# @param until_date [Date] a date before or on which to find events, or nil for unfiltered
|
503
|
-
# @return [Array] a list of all event (activity or note) text values
|
504
517
|
# @raise [ArgumentError] if limit is present but limit < 1
|
505
518
|
# @raise [FriendsError] if friend, location or tag cannot be found or
|
506
519
|
# is ambiguous
|
@@ -519,7 +532,7 @@ module Friends
|
|
519
532
|
# If we need to, trim the list.
|
520
533
|
events = events.take(limit) unless limit.nil?
|
521
534
|
|
522
|
-
events.map(&:to_s)
|
535
|
+
safe_puts events.map(&:to_s)
|
523
536
|
end
|
524
537
|
|
525
538
|
# @param arr [Array] an unsorted array
|
@@ -538,7 +551,7 @@ module Friends
|
|
538
551
|
# unfiltered
|
539
552
|
# @param since_date [Date] a date on or after which to find activities, or nil for unfiltered
|
540
553
|
# @param until_date [Date] a date before or on which to find activities, or nil for unfiltered
|
541
|
-
# @return [Array] an array of activities
|
554
|
+
# @return [Array] an array of activities or notes
|
542
555
|
# @raise [FriendsError] if friend, location or tag cannot be found or
|
543
556
|
# is ambiguous
|
544
557
|
def filtered_events(events:, with:, location_name:, tagged:, since_date:, until_date:)
|
@@ -572,7 +585,6 @@ module Friends
|
|
572
585
|
|
573
586
|
# @param type [Symbol] one of: [:friend, :location]
|
574
587
|
# @param limit [Integer] the number of favorite things to return
|
575
|
-
# @return [Array] a list of the favorite things' names and activity counts
|
576
588
|
# @raise [ArgumentError] if type is not one of: [:friend, :location]
|
577
589
|
# @raise [ArgumentError] if limit is < 1
|
578
590
|
def list_favorite_things(type, limit:)
|
@@ -589,12 +601,12 @@ module Friends
|
|
589
601
|
|
590
602
|
if results.size == 1
|
591
603
|
favorite = results.first
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
604
|
+
safe_puts "Your favorite #{type} is "\
|
605
|
+
"#{favorite.name} "\
|
606
|
+
"(#{favorite.n_activities} "\
|
607
|
+
"#{favorite.n_activities == 1 ? 'activity' : 'activities'})"
|
596
608
|
else
|
597
|
-
|
609
|
+
safe_puts "Your favorite #{type}s:"
|
598
610
|
|
599
611
|
max_str_size = results.map(&:name).map(&:size).max
|
600
612
|
|
@@ -621,7 +633,7 @@ module Friends
|
|
621
633
|
# elements, which may be too large if the last element in the list is a tie.
|
622
634
|
num_str_size = data.last.first.to_s.size + 1 unless data.empty?
|
623
635
|
data.each do |ranking, str|
|
624
|
-
|
636
|
+
safe_puts "#{"#{ranking}.".ljust(num_str_size)} #{str}"
|
625
637
|
end
|
626
638
|
end
|
627
639
|
end
|