friends 0.19 → 0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +16 -0
- data/README.md +105 -13
- data/bin/friends +125 -43
- data/friends.md +5 -5
- data/lib/friends.rb +1 -0
- data/lib/friends/activity.rb +16 -1
- data/lib/friends/friend.rb +33 -7
- data/lib/friends/introvert.rb +67 -14
- data/lib/friends/version.rb +1 -1
- data/test/activity_spec.rb +36 -2
- data/test/friend_spec.rb +40 -1
- data/test/introvert_spec.rb +203 -57
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1429eb3d555f53ebb68816b253350252370d37bc
|
4
|
+
data.tar.gz: 42095e0608c73c17fd75f8f4bedab8313e19a4b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab66c9042a1c23ec4e5c2a1685d03b91d5b66c1382ca3ed64b1822277bf08ed1bbe321a9144f59256d6c369b93f9b954d8d6307a9a50b6c9f6fc4374c9e7df6f
|
7
|
+
data.tar.gz: 87719311645cdff9c7af1a862c9533e8671c1e000c7221f7833226bfcad3f86935de92440acbad5a65aa87bf005485c2b31589cdb3d9ea094973093d13896add
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v0.20](https://github.com/JacobEvelyn/friends/tree/v0.20) (2016-05-08)
|
4
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.19...v0.20)
|
5
|
+
|
6
|
+
**Implemented enhancements:**
|
7
|
+
|
8
|
+
- Add --tagged option to `list friends` [\#119](https://github.com/JacobEvelyn/friends/issues/119)
|
9
|
+
- Add --verbose option to `list friends` [\#117](https://github.com/JacobEvelyn/friends/issues/117)
|
10
|
+
- Add `list hashtags` command [\#116](https://github.com/JacobEvelyn/friends/issues/116)
|
11
|
+
- Add hashtag capabilities to friends [\#90](https://github.com/JacobEvelyn/friends/issues/90)
|
12
|
+
- Add hashtag capabilities to activities [\#89](https://github.com/JacobEvelyn/friends/issues/89)
|
13
|
+
- Add location data to friends [\#66](https://github.com/JacobEvelyn/friends/issues/66)
|
14
|
+
|
15
|
+
**Merged pull requests:**
|
16
|
+
|
17
|
+
- Implement hashtags [\#118](https://github.com/JacobEvelyn/friends/pull/118) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
18
|
+
|
3
19
|
## [v0.19](https://github.com/JacobEvelyn/friends/tree/v0.19) (2016-05-02)
|
4
20
|
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.18...v0.19)
|
5
21
|
|
data/README.md
CHANGED
@@ -17,6 +17,7 @@ Extrovert-approved.
|
|
17
17
|
- `add`
|
18
18
|
- [`add activity`](#add-activity)
|
19
19
|
- [`add friend`](#add-friend)
|
20
|
+
- [`add hashtag`](#add-hashtag)
|
20
21
|
- [`add location`](#add-location)
|
21
22
|
- [`add nickname`](#add-nickname)
|
22
23
|
- [`clean`](#clean)
|
@@ -28,8 +29,11 @@ Extrovert-approved.
|
|
28
29
|
- [`list favorite friends`](#list-favorite-friends)
|
29
30
|
- [`list favorite locations`](#list-favorite-locations)
|
30
31
|
- [`list friends`](#list-friends)
|
32
|
+
- [`list hashtags`](#list-hashtags)
|
31
33
|
- [`list locations`](#list-locations)
|
32
|
-
-
|
34
|
+
- `remove`
|
35
|
+
- [`remove hashtag`](#remove-hashtag)
|
36
|
+
- [`remove nickname`](#remove-nickname)
|
33
37
|
- `rename`
|
34
38
|
- [`rename friend`](#rename-friend)
|
35
39
|
- [`rename location`](#rename-location)
|
@@ -83,10 +87,14 @@ Easy, huh?
|
|
83
87
|
`friends` is structured around several different types of things:
|
84
88
|
|
85
89
|
- **Activities**: The things you do. Each activity has a date associated with
|
86
|
-
it.
|
90
|
+
it. Activities may optionally contain any number of *friends*, *locations*,
|
91
|
+
and *hashtags*.
|
87
92
|
- **Friends**: The people you do *activities* with. Each friend has a name and,
|
88
|
-
optionally, one or several nicknames.
|
89
|
-
- **Locations**: The places in which *activities* happen.
|
93
|
+
optionally, one or several nicknames. (Examples: `John`, `Grace Hopper`)
|
94
|
+
- **Locations**: The places in which *activities* happen. (Examples: `Paris`,
|
95
|
+
`Marie's Diner`)
|
96
|
+
- **Hashtags**: A way to categorize your *activities* with tags of your
|
97
|
+
choosing. (Examples: `#exercise`, `#concert`)
|
90
98
|
|
91
99
|
The `friends.md` Markdown file that stores all of your data contains:
|
92
100
|
|
@@ -212,6 +220,14 @@ $ friends add activity "Went swimming near atlantis with George."
|
|
212
220
|
Activity added: "2016-01-06: Went swimming near Atlantis with George Washington Carver."
|
213
221
|
```
|
214
222
|
|
223
|
+
Hashtags will be colored if they're provided (though this README can't display
|
224
|
+
color so you'll just have to have faith here):
|
225
|
+
|
226
|
+
```bash
|
227
|
+
$ friends add activity "The office softball team wins a game! #work #exercise"
|
228
|
+
Activity added: "2016-05-05: The office softball team wins a game! #work #exercise"
|
229
|
+
```
|
230
|
+
|
215
231
|
You can of course specify a date for the activity:
|
216
232
|
|
217
233
|
```bash
|
@@ -247,6 +263,13 @@ $ friends add friend "Grace Hopper"
|
|
247
263
|
Friend added: "Grace Hopper"
|
248
264
|
```
|
249
265
|
|
266
|
+
#### `add hashtag`
|
267
|
+
|
268
|
+
```bash
|
269
|
+
$ friends add hashtag "Grace Hopper" science
|
270
|
+
Hashtag added to friend: "Grace Hopper #science
|
271
|
+
```
|
272
|
+
|
250
273
|
#### `add location`
|
251
274
|
|
252
275
|
```
|
@@ -350,8 +373,8 @@ Lists recent activities:
|
|
350
373
|
|
351
374
|
```bash
|
352
375
|
$ friends list activities
|
353
|
-
2015-01-04: Got lunch with Grace Hopper and George Washington Carver.
|
354
|
-
2014-12-31: Celebrated the new year with Marie Curie in New York City.
|
376
|
+
2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
|
377
|
+
2014-12-31: Celebrated the new year with Marie Curie in New York City. #partying
|
355
378
|
2014-11-15: Talked to George Washington Carver on the phone for an hour.
|
356
379
|
```
|
357
380
|
|
@@ -359,15 +382,15 @@ You can adjust how many activities are shown:
|
|
359
382
|
|
360
383
|
```bash
|
361
384
|
$ friends list activities --limit 2
|
362
|
-
2015-01-04: Got lunch with Grace Hopper and George Washington Carver.
|
363
|
-
2014-12-31: Celebrated the new year with Marie Curie in New York City.
|
385
|
+
2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
|
386
|
+
2014-12-31: Celebrated the new year with Marie Curie in New York City. #partying
|
364
387
|
```
|
365
388
|
|
366
389
|
Or only list the activities you did with a certain friend:
|
367
390
|
|
368
391
|
```bash
|
369
|
-
$ friends list activities --with
|
370
|
-
2015-01-04: Got lunch with Grace Hopper and George Washington Carver.
|
392
|
+
$ friends list activities --with George
|
393
|
+
2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
|
371
394
|
2014-11-15: Talked to George Washington Carver on the phone for an hour.
|
372
395
|
```
|
373
396
|
|
@@ -375,7 +398,21 @@ Or filter your activities by location:
|
|
375
398
|
|
376
399
|
```bash
|
377
400
|
$ friends list activities --in "New York"
|
378
|
-
2014-12-31: Celebrated the new year with Marie Curie in New York City.
|
401
|
+
2014-12-31: Celebrated the new year with Marie Curie in New York City. #partying
|
402
|
+
```
|
403
|
+
|
404
|
+
Or by hashtag:
|
405
|
+
|
406
|
+
```bash
|
407
|
+
$ friends list activities --tagged food
|
408
|
+
2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
|
409
|
+
```
|
410
|
+
|
411
|
+
And you can mix and match these options to your heart's content:
|
412
|
+
|
413
|
+
```bash
|
414
|
+
$ friends list activities --tagged food --with Grace
|
415
|
+
2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
|
379
416
|
```
|
380
417
|
|
381
418
|
#### `list favorite friends`
|
@@ -422,7 +459,7 @@ Your favorite locations:
|
|
422
459
|
|
423
460
|
#### `list friends`
|
424
461
|
|
425
|
-
Lists all of your friends:
|
462
|
+
Lists all of your friends in alphabetical order:
|
426
463
|
|
427
464
|
```bash
|
428
465
|
$ friends list friends
|
@@ -431,6 +468,15 @@ Grace Hopper
|
|
431
468
|
Marie Curie
|
432
469
|
```
|
433
470
|
|
471
|
+
You can also include friend nicknames, locations, and hashtags:
|
472
|
+
|
473
|
+
```bash
|
474
|
+
$ friends list friends --verbose
|
475
|
+
George Washington Carver
|
476
|
+
Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] #navy #science
|
477
|
+
Marie Curie [Atlantis] #science
|
478
|
+
```
|
479
|
+
|
434
480
|
You can filter your friends by location:
|
435
481
|
|
436
482
|
```bash
|
@@ -438,9 +484,46 @@ $ friends list friends --in Paris
|
|
438
484
|
Marie Curie
|
439
485
|
```
|
440
486
|
|
487
|
+
And you can also filter your friends by hashtag:
|
488
|
+
|
489
|
+
```bash
|
490
|
+
$ friends list friends --tagged science
|
491
|
+
Grace Hopper
|
492
|
+
Marie Curie
|
493
|
+
```
|
494
|
+
|
495
|
+
#### `list hashtags`
|
496
|
+
|
497
|
+
Lists all hashtags you've used, in alphabetical order:
|
498
|
+
|
499
|
+
```bash
|
500
|
+
$ friends list hashtags
|
501
|
+
#dancing
|
502
|
+
#food
|
503
|
+
#school
|
504
|
+
#swanky
|
505
|
+
```
|
506
|
+
|
507
|
+
You can limit this to only hashtags from activities:
|
508
|
+
|
509
|
+
```bash
|
510
|
+
$ friends list hashtags --from activities
|
511
|
+
#dancing
|
512
|
+
#food
|
513
|
+
#swanky
|
514
|
+
```
|
515
|
+
|
516
|
+
Or only hashtags from friends:
|
517
|
+
|
518
|
+
```bash
|
519
|
+
$ friends list hashtags --from friends
|
520
|
+
#school
|
521
|
+
#swanky
|
522
|
+
```
|
523
|
+
|
441
524
|
#### `list locations`
|
442
525
|
|
443
|
-
Lists all of the locations you've added
|
526
|
+
Lists all of the locations you've added, in alphabetical order::
|
444
527
|
|
445
528
|
```
|
446
529
|
$ friends list locations
|
@@ -449,6 +532,15 @@ New York City
|
|
449
532
|
Paris
|
450
533
|
```
|
451
534
|
|
535
|
+
#### `remove hashtag`
|
536
|
+
|
537
|
+
Removes a specific hashtag from a friend:
|
538
|
+
|
539
|
+
```bash
|
540
|
+
$ friends remove hashtag "Grace Hopper" fun
|
541
|
+
Hashtag removed from friend: "Grace Hopper (a.k.a. Amazing Grace) #OtherHashtag"
|
542
|
+
```
|
543
|
+
|
452
544
|
#### `remove nickname`
|
453
545
|
|
454
546
|
Removes a specific nickname from a friend:
|
data/bin/friends
CHANGED
@@ -18,6 +18,28 @@ version Friends::VERSION
|
|
18
18
|
subcommand_option_handling :normal
|
19
19
|
arguments :strict
|
20
20
|
|
21
|
+
class Hashtag
|
22
|
+
# @param str [String] of the form "hashtag" or "#hashtag"
|
23
|
+
# @return [String] the string, with whitespace stripped and a hashtag
|
24
|
+
# prepended if there isn't one already
|
25
|
+
# NOTE: This logic could be only in the accept block if GLI allowed type
|
26
|
+
# conversions for arguments.
|
27
|
+
# See: https://github.com/davetron5000/gli/issues/241
|
28
|
+
def self.convert_to_hashtag(str)
|
29
|
+
str = str.strip
|
30
|
+
str.size > 0 && str[0] == "#" ? str : "##{str}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
accept(Hashtag) do |value|
|
35
|
+
Hashtag.convert_to_hashtag(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
class Stripped ; end
|
39
|
+
accept(Stripped) do |value|
|
40
|
+
value.strip
|
41
|
+
end
|
42
|
+
|
21
43
|
switch [:quiet],
|
22
44
|
negatable: false,
|
23
45
|
desc: "Quiet output messages"
|
@@ -72,10 +94,75 @@ command :list do |list|
|
|
72
94
|
list.command :friends do |list_friends|
|
73
95
|
list_friends.flag [:in],
|
74
96
|
arg_name: "LOCATION",
|
75
|
-
desc: "List only friends in the given location"
|
97
|
+
desc: "List only friends in the given location",
|
98
|
+
type: Stripped
|
99
|
+
|
100
|
+
list_friends.flag [:tagged],
|
101
|
+
arg_name: "HASHTAG",
|
102
|
+
desc: "List only friends with the given hashtag",
|
103
|
+
type: Hashtag
|
104
|
+
|
105
|
+
list_friends.switch [:verbose],
|
106
|
+
negatable: false,
|
107
|
+
desc: "Output friend nicknames, locations, and hashtags"
|
76
108
|
|
77
109
|
list_friends.action do |_, options|
|
78
|
-
puts @introvert.list_friends(
|
110
|
+
puts @introvert.list_friends(
|
111
|
+
location_name: options[:in],
|
112
|
+
tagged: options[:tagged],
|
113
|
+
verbose: options[:verbose]
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
list.desc "Lists all activities"
|
119
|
+
list.command :activities do |list_activities|
|
120
|
+
list_activities.flag [:limit],
|
121
|
+
arg_name: "NUMBER",
|
122
|
+
desc: "The number of activities to return",
|
123
|
+
default_value: 10,
|
124
|
+
type: Integer
|
125
|
+
|
126
|
+
list_activities.flag [:with],
|
127
|
+
arg_name: "NAME",
|
128
|
+
desc: "List only activities with the given friend",
|
129
|
+
type: Stripped
|
130
|
+
|
131
|
+
list_activities.flag [:in],
|
132
|
+
arg_name: "LOCATION",
|
133
|
+
desc: "List only activities in the given location",
|
134
|
+
type: Stripped
|
135
|
+
|
136
|
+
list_activities.flag [:tagged],
|
137
|
+
arg_name: "HASHTAG",
|
138
|
+
desc: "List only activities with the given hashtag",
|
139
|
+
type: Hashtag
|
140
|
+
|
141
|
+
list_activities.action do |_, options|
|
142
|
+
puts @introvert.list_activities(
|
143
|
+
limit: options[:limit],
|
144
|
+
with: options[:with],
|
145
|
+
location_name: options[:in],
|
146
|
+
tagged: options[:tagged]
|
147
|
+
)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
list.desc "List all locations"
|
152
|
+
list.command :locations do |list_locations|
|
153
|
+
list_locations.action do
|
154
|
+
puts @introvert.list_locations
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
list.desc "List all hashtags used"
|
159
|
+
list.command :hashtags do |list_hashtags|
|
160
|
+
list_hashtags.flag [:from],
|
161
|
+
arg_name: '"activities" or "friends" (default: both)',
|
162
|
+
desc: "List only hashtags from activities or friends "\
|
163
|
+
"instead of both"
|
164
|
+
list_hashtags.action do |_, options|
|
165
|
+
puts @introvert.list_hashtags(from: options[:from])
|
79
166
|
end
|
80
167
|
end
|
81
168
|
|
@@ -85,12 +172,12 @@ command :list do |list|
|
|
85
172
|
list_favorite.command :friends do |list_favorite_friends|
|
86
173
|
list_favorite_friends.flag [:limit],
|
87
174
|
arg_name: "NUMBER",
|
175
|
+
desc: "The number of friends to return",
|
88
176
|
default_value: 10,
|
89
|
-
|
177
|
+
type: Integer
|
90
178
|
|
91
179
|
list_favorite_friends.action do |_, options|
|
92
|
-
|
93
|
-
favorites = @introvert.list_favorite_friends(limit: limit)
|
180
|
+
favorites = @introvert.list_favorite_friends(limit: options[:limit])
|
94
181
|
|
95
182
|
if limit == 1
|
96
183
|
puts "Your best friend is #{favorites.first}"
|
@@ -109,12 +196,12 @@ command :list do |list|
|
|
109
196
|
list_favorite.command :locations do |list_favorite_locations|
|
110
197
|
list_favorite_locations.flag [:limit],
|
111
198
|
arg_name: "NUMBER",
|
199
|
+
desc: "The number of locations to return",
|
112
200
|
default_value: 10,
|
113
|
-
|
201
|
+
type: Integer
|
114
202
|
|
115
203
|
list_favorite_locations.action do |_, options|
|
116
|
-
|
117
|
-
favorites = @introvert.list_favorite_locations(limit: limit)
|
204
|
+
favorites = @introvert.list_favorite_locations(limit: options[:limit])
|
118
205
|
|
119
206
|
if limit == 1
|
120
207
|
puts "Your favorite location is #{favorites.first}"
|
@@ -129,38 +216,6 @@ command :list do |list|
|
|
129
216
|
end
|
130
217
|
end
|
131
218
|
end
|
132
|
-
|
133
|
-
list.desc "Lists all activities"
|
134
|
-
list.command :activities do |list_activities|
|
135
|
-
list_activities.flag [:limit],
|
136
|
-
arg_name: "NUMBER",
|
137
|
-
default_value: 10,
|
138
|
-
desc: "The number of activities to return"
|
139
|
-
|
140
|
-
list_activities.flag [:with],
|
141
|
-
arg_name: "NAME",
|
142
|
-
desc: "List only activities involving the given friend"
|
143
|
-
|
144
|
-
list_activities.flag [:in],
|
145
|
-
arg_name: "LOCATION",
|
146
|
-
desc: "List only activities in the given location"
|
147
|
-
|
148
|
-
list_activities.action do |_, options|
|
149
|
-
limit = options[:limit].to_i
|
150
|
-
puts @introvert.list_activities(
|
151
|
-
limit: limit,
|
152
|
-
with: options[:with],
|
153
|
-
location_name: options[:in]
|
154
|
-
)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
list.desc "List all locations"
|
159
|
-
list.command :locations do |list_locations|
|
160
|
-
list_locations.action do
|
161
|
-
puts @introvert.list_locations
|
162
|
-
end
|
163
|
-
end
|
164
219
|
end
|
165
220
|
|
166
221
|
desc "Adds a friend (or nickname), activity, or location"
|
@@ -183,11 +238,11 @@ command :add do |add|
|
|
183
238
|
|
184
239
|
# If there's no description, prompt the user for one.
|
185
240
|
if activity.description.nil? || activity.description.empty?
|
186
|
-
activity.description = Readline.readline(activity.
|
241
|
+
activity.description = Readline.readline(activity.to_s)
|
187
242
|
activity.highlight_description(introvert: @introvert)
|
188
243
|
end
|
189
244
|
|
190
|
-
@message = "Activity added: \"#{activity
|
245
|
+
@message = "Activity added: \"#{activity}\""
|
191
246
|
@dirty = true # Mark the file for cleaning.
|
192
247
|
end
|
193
248
|
end
|
@@ -211,6 +266,19 @@ command :add do |add|
|
|
211
266
|
@dirty = true # Mark the file for cleaning.
|
212
267
|
end
|
213
268
|
end
|
269
|
+
|
270
|
+
add.desc "Adds a hashtag to a friend"
|
271
|
+
add.arg_name "NAME HASHTAG"
|
272
|
+
add.command :hashtag do |add_hashtag|
|
273
|
+
add_hashtag.action do |_, _, args|
|
274
|
+
friend = @introvert.add_hashtag(
|
275
|
+
name: args.first,
|
276
|
+
hashtag: Hashtag.convert_to_hashtag(args[1])
|
277
|
+
)
|
278
|
+
@message = "Hashtag added to friend: \"#{friend}\""
|
279
|
+
@dirty = true # Mark the file for cleaning.
|
280
|
+
end
|
281
|
+
end
|
214
282
|
end
|
215
283
|
|
216
284
|
desc "Set data about friends"
|
@@ -237,6 +305,19 @@ command :remove do |remove|
|
|
237
305
|
@dirty = true # Mark the file for cleaning.
|
238
306
|
end
|
239
307
|
end
|
308
|
+
|
309
|
+
remove.desc "Removes a hashtag to a friend"
|
310
|
+
remove.arg_name "NAME HASHTAG"
|
311
|
+
remove.command :hashtag do |remove_hashtag|
|
312
|
+
remove_hashtag.action do |_, _, args|
|
313
|
+
friend = @introvert.remove_hashtag(
|
314
|
+
name: args.first,
|
315
|
+
hashtag: Hashtag.convert_to_hashtag(args[1])
|
316
|
+
)
|
317
|
+
@message = "Hashtag removed from friend: \"#{friend}\""
|
318
|
+
@dirty = true # Mark the file for cleaning.
|
319
|
+
end
|
320
|
+
end
|
240
321
|
end
|
241
322
|
|
242
323
|
desc "Graph all activities or a friend's relationship over time"
|
@@ -268,7 +349,8 @@ desc "Suggest friends to do activities with"
|
|
268
349
|
command :suggest do |suggest|
|
269
350
|
suggest.flag [:in],
|
270
351
|
arg_name: "LOCATION",
|
271
|
-
desc: "Suggest only friends in the given location"
|
352
|
+
desc: "Suggest only friends in the given location",
|
353
|
+
type: Stripped
|
272
354
|
|
273
355
|
suggest.action do |_, options|
|
274
356
|
suggestions = @introvert.suggest(location_name: options[:in])
|
data/friends.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
### Activities:
|
2
|
-
- 2015-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute.
|
3
|
-
- 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**.
|
4
|
-
- 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**.
|
2
|
+
- 2015-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute. #food
|
3
|
+
- 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**. #food
|
4
|
+
- 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. #partying
|
5
5
|
- 2014-11-15: Talked to **George Washington Carver** on the phone for an hour.
|
6
6
|
|
7
7
|
### Friends:
|
8
8
|
- George Washington Carver
|
9
|
-
- Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris]
|
10
|
-
- Marie Curie [Atlantis]
|
9
|
+
- Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] #navy #science
|
10
|
+
- Marie Curie [Atlantis] #science
|
11
11
|
|
12
12
|
### Locations:
|
13
13
|
- Atlantis
|
data/lib/friends.rb
CHANGED
data/lib/friends/activity.rb
CHANGED
@@ -50,7 +50,7 @@ module Friends
|
|
50
50
|
attr_accessor :description
|
51
51
|
|
52
52
|
# @return [String] the command-line display text for the activity
|
53
|
-
def
|
53
|
+
def to_s
|
54
54
|
date_s = Paint[date, :bold]
|
55
55
|
description_s = description.to_s
|
56
56
|
# rubocop:disable Lint/AssignmentInCondition
|
@@ -68,6 +68,10 @@ module Friends
|
|
68
68
|
"#{Paint[match[1], :bold, :yellow]}"\
|
69
69
|
"#{match.post_match}"
|
70
70
|
end
|
71
|
+
|
72
|
+
description_s = description_s.
|
73
|
+
gsub(HASHTAG_REGEX, Paint['\0', :bold, :cyan])
|
74
|
+
|
71
75
|
"#{date_s}: #{description_s}"
|
72
76
|
end
|
73
77
|
|
@@ -120,6 +124,17 @@ module Friends
|
|
120
124
|
friend_names.include? friend.name
|
121
125
|
end
|
122
126
|
|
127
|
+
# @param hashtag [String] the hashtag to test, of the form "#hashtag"
|
128
|
+
# @return [Boolean] true iff this activity includes the given hashtag
|
129
|
+
def includes_hashtag?(hashtag:)
|
130
|
+
hashtags.include? hashtag
|
131
|
+
end
|
132
|
+
|
133
|
+
# @return [Set] all hashtags in this activity (including the "#")
|
134
|
+
def hashtags
|
135
|
+
Set.new(@description.scan(HASHTAG_REGEX))
|
136
|
+
end
|
137
|
+
|
123
138
|
# Find the names of all friends in this description.
|
124
139
|
# @return [Array] list of all friend names in the description
|
125
140
|
def friend_names
|
data/lib/friends/friend.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# Friend represents a friend. You know, a real-life friend!
|
3
3
|
|
4
|
+
require "friends"
|
4
5
|
require "friends/regex_builder"
|
5
6
|
require "friends/serializable"
|
6
7
|
|
@@ -15,7 +16,7 @@ module Friends
|
|
15
16
|
def self.deserialization_regex
|
16
17
|
# Note: this regex must be on one line because whitespace is important
|
17
18
|
# rubocop:disable Metrics/LineLength
|
18
|
-
/(#{SERIALIZATION_PREFIX})?(?<name>[^\(\[]
|
19
|
+
/(#{SERIALIZATION_PREFIX})?(?<name>[^\(\[#]*[^\(\[#\s])(\s+\(#{NICKNAME_PREFIX}(?<nickname_str>.+)\))?(\s+\[(?<location_name>[^\]]+)\])?(\s+(?<hashtags_str>(#{HASHTAG_REGEX}\s*)+))?/
|
19
20
|
# rubocop:enable Metrics/LineLength
|
20
21
|
end
|
21
22
|
|
@@ -25,16 +26,23 @@ module Friends
|
|
25
26
|
end
|
26
27
|
|
27
28
|
# @param name [String] the name of the friend
|
28
|
-
def initialize(
|
29
|
-
|
29
|
+
def initialize(
|
30
|
+
name:,
|
31
|
+
nickname_str: nil,
|
32
|
+
location_name: nil,
|
33
|
+
hashtags_str: nil
|
34
|
+
)
|
35
|
+
@name = name
|
30
36
|
@nicknames = nickname_str &&
|
31
|
-
nickname_str.split(NICKNAME_PREFIX)
|
37
|
+
nickname_str.split(" #{NICKNAME_PREFIX}") ||
|
32
38
|
[]
|
33
39
|
@location_name = location_name
|
40
|
+
@hashtags = hashtags_str && hashtags_str.split(/\s+/) || []
|
34
41
|
end
|
35
42
|
|
36
43
|
attr_accessor :name
|
37
44
|
attr_accessor :location_name
|
45
|
+
attr_reader :hashtags
|
38
46
|
|
39
47
|
# @return [String] the file serialization text for the friend
|
40
48
|
def serialize
|
@@ -52,10 +60,28 @@ module Friends
|
|
52
60
|
|
53
61
|
location_str = " [#{@location_name}]" unless @location_name.nil?
|
54
62
|
|
55
|
-
"#{@
|
63
|
+
hashtag_str = " #{@hashtags.join(' ')}" unless @hashtags.empty?
|
64
|
+
|
65
|
+
"#{@name}#{nickname_str}#{location_str}#{hashtag_str}"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Adds a hashtag, ignoring duplicates.
|
69
|
+
# @param hashtag [String] the hashtag to add
|
70
|
+
def add_hashtag(hashtag)
|
71
|
+
@hashtags << hashtag
|
72
|
+
@hashtags.uniq!
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param hashtag [String] the hashtag to remove
|
76
|
+
def remove_hashtag(hashtag)
|
77
|
+
unless @hashtags.include? hashtag
|
78
|
+
raise FriendsError, "Hashtag \"#{hashtag}\" not found for \"#{name}\""
|
79
|
+
end
|
80
|
+
|
81
|
+
@hashtags.delete(hashtag)
|
56
82
|
end
|
57
83
|
|
58
|
-
# Adds a nickname,
|
84
|
+
# Adds a nickname, ignoring duplicates.
|
59
85
|
# @param nickname [String] the nickname to add
|
60
86
|
def add_nickname(nickname)
|
61
87
|
@nicknames << nickname
|
@@ -63,7 +89,7 @@ module Friends
|
|
63
89
|
end
|
64
90
|
|
65
91
|
# @param nickname [String] the nickname to remove
|
66
|
-
# @
|
92
|
+
# @raise [FriendsError] if the friend does not have the given nickname
|
67
93
|
def remove_nickname(nickname)
|
68
94
|
unless @nicknames.include? nickname
|
69
95
|
raise FriendsError, "Nickname \"#{nickname}\" not found for \"#{name}\""
|
data/lib/friends/introvert.rb
CHANGED
@@ -116,9 +116,6 @@ module Friends
|
|
116
116
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
117
117
|
# @return [Friend] the existing friend
|
118
118
|
def rename_friend(old_name:, new_name:)
|
119
|
-
old_name.strip!
|
120
|
-
new_name.strip!
|
121
|
-
|
122
119
|
friend = friend_with_name_in(old_name)
|
123
120
|
@activities.each do |activity|
|
124
121
|
activity.update_friend_name(old_name: friend.name, new_name: new_name)
|
@@ -133,9 +130,6 @@ module Friends
|
|
133
130
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
134
131
|
# @return [Location] the existing location
|
135
132
|
def rename_location(old_name:, new_name:)
|
136
|
-
old_name.strip!
|
137
|
-
new_name.strip!
|
138
|
-
|
139
133
|
loc = location_with_name_in(old_name)
|
140
134
|
|
141
135
|
# Update locations in activities.
|
@@ -159,27 +153,54 @@ module Friends
|
|
159
153
|
# @return [Friend] the existing friend
|
160
154
|
def add_nickname(name:, nickname:)
|
161
155
|
friend = friend_with_name_in(name)
|
162
|
-
friend.add_nickname(nickname
|
156
|
+
friend.add_nickname(nickname)
|
157
|
+
friend
|
158
|
+
end
|
159
|
+
|
160
|
+
# Add a hashtag to an existing friend.
|
161
|
+
# @param name [String] the name of the friend
|
162
|
+
# @param hashtag [String] the hashtag to add to the friend
|
163
|
+
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
164
|
+
# @return [Friend] the existing friend
|
165
|
+
def add_hashtag(name:, hashtag:)
|
166
|
+
friend = friend_with_name_in(name)
|
167
|
+
friend.add_hashtag(hashtag)
|
168
|
+
friend
|
169
|
+
end
|
170
|
+
|
171
|
+
# Remove a hashtag from an existing friend.
|
172
|
+
# @param name [String] the name of the friend
|
173
|
+
# @param hashtag [String] the hashtag to remove from the friend
|
174
|
+
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
175
|
+
# @raise [FriendsError] if the friend does not have the given nickname
|
176
|
+
# @return [Friend] the existing friend
|
177
|
+
def remove_hashtag(name:, hashtag:)
|
178
|
+
friend = friend_with_name_in(name)
|
179
|
+
friend.remove_hashtag(hashtag)
|
163
180
|
friend
|
164
181
|
end
|
165
182
|
|
166
|
-
# Remove a nickname from an existing friend
|
167
|
-
# file.
|
183
|
+
# Remove a nickname from an existing friend.
|
168
184
|
# @param name [String] the name of the friend
|
169
185
|
# @param nickname [String] the nickname to remove from the friend
|
170
186
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
187
|
+
# @raise [FriendsError] if the friend does not have the given nickname
|
171
188
|
# @return [Friend] the existing friend
|
172
189
|
def remove_nickname(name:, nickname:)
|
173
190
|
friend = friend_with_name_in(name)
|
174
|
-
friend.remove_nickname(nickname
|
191
|
+
friend.remove_nickname(nickname)
|
175
192
|
friend
|
176
193
|
end
|
177
194
|
|
178
195
|
# List all friend names in the friends file.
|
179
196
|
# @param location_name [String] the name of a location to filter by, or nil
|
180
197
|
# for unfiltered
|
198
|
+
# @param tagged [String] the name of a hashtag to filter by, or nil for
|
199
|
+
# unfiltered
|
200
|
+
# @param verbose [Boolean] true iff we should output friend names with
|
201
|
+
# nicknames, locations, and hashtags; false for names only
|
181
202
|
# @return [Array] a list of all friend names
|
182
|
-
def list_friends(location_name:)
|
203
|
+
def list_friends(location_name:, tagged:, verbose:)
|
183
204
|
fs = @friends
|
184
205
|
|
185
206
|
# Filter by location if a name is passed.
|
@@ -188,7 +209,10 @@ module Friends
|
|
188
209
|
fs = fs.select { |friend| friend.location_name == location.name }
|
189
210
|
end
|
190
211
|
|
191
|
-
|
212
|
+
# Filter by hashtag if one is passed.
|
213
|
+
fs = fs.select { |friend| friend.hashtags.include? tagged } if tagged
|
214
|
+
|
215
|
+
verbose ? fs.map(&:to_s) : fs.map(&:name)
|
192
216
|
end
|
193
217
|
|
194
218
|
# List your favorite friends.
|
@@ -214,9 +238,11 @@ module Friends
|
|
214
238
|
# unfiltered
|
215
239
|
# @param location_name [String] the name of a location to filter by, or nil
|
216
240
|
# for unfiltered
|
241
|
+
# @param tagged [String] the name of a hashtag to filter by, or nil for
|
242
|
+
# unfiltered
|
217
243
|
# @return [Array] a list of all activity text values
|
218
244
|
# @raise [FriendsError] if 0 or 2+ friends match the given `with` text
|
219
|
-
def list_activities(limit:, with:, location_name:)
|
245
|
+
def list_activities(limit:, with:, location_name:, tagged:)
|
220
246
|
acts = @activities
|
221
247
|
|
222
248
|
# Filter by friend name if argument is passed.
|
@@ -231,10 +257,15 @@ module Friends
|
|
231
257
|
acts = acts.select { |act| act.includes_location?(location: location) }
|
232
258
|
end
|
233
259
|
|
260
|
+
# Filter by tag if argument is passed.
|
261
|
+
unless tagged.nil?
|
262
|
+
acts = acts.select { |act| act.includes_hashtag?(hashtag: tagged) }
|
263
|
+
end
|
264
|
+
|
234
265
|
# If we need to, trim the list.
|
235
266
|
acts = acts.take(limit) unless limit.nil?
|
236
267
|
|
237
|
-
acts.map(&:
|
268
|
+
acts.map(&:to_s)
|
238
269
|
end
|
239
270
|
|
240
271
|
# List all location names in the friends file.
|
@@ -243,6 +274,28 @@ module Friends
|
|
243
274
|
@locations.map(&:name)
|
244
275
|
end
|
245
276
|
|
277
|
+
# @param from [String] one of: ["activities", "friends", nil]
|
278
|
+
# If not nil, limits the hashtags returned to only those from either
|
279
|
+
# activities or friends.
|
280
|
+
# @return [Array] a sorted list of all hashtags in activity descriptions
|
281
|
+
def list_hashtags(from:)
|
282
|
+
output = Set.new
|
283
|
+
|
284
|
+
unless from == "friends" # If from is "activities" or nil.
|
285
|
+
@activities.each_with_object(output) do |activity, set|
|
286
|
+
set.merge(activity.hashtags)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
unless from == "activities" # If from is "friends" or nil.
|
291
|
+
@friends.each_with_object(output) do |friend, set|
|
292
|
+
set.merge(friend.hashtags)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
output.sort_by(&:downcase)
|
297
|
+
end
|
298
|
+
|
246
299
|
# Find data points for graphing activities over time.
|
247
300
|
# Optionally filter by a friend to see a given relationship over time.
|
248
301
|
#
|
data/lib/friends/version.rb
CHANGED
data/test/activity_spec.rb
CHANGED
@@ -95,8 +95,8 @@ describe Friends::Activity do
|
|
95
95
|
it { subject.description.must_equal description }
|
96
96
|
end
|
97
97
|
|
98
|
-
describe "#
|
99
|
-
subject { activity.
|
98
|
+
describe "#to_s" do
|
99
|
+
subject { activity.to_s }
|
100
100
|
|
101
101
|
it do
|
102
102
|
subject.
|
@@ -456,6 +456,40 @@ describe Friends::Activity do
|
|
456
456
|
end
|
457
457
|
end
|
458
458
|
|
459
|
+
describe "#hashtags" do
|
460
|
+
subject { activity.hashtags }
|
461
|
+
|
462
|
+
describe "when the activity has no hashtags" do
|
463
|
+
let(:activity) { Friends::Activity.new(str: "Enormous ball pit!") }
|
464
|
+
it { subject.must_be :empty? }
|
465
|
+
end
|
466
|
+
|
467
|
+
describe "when the activity has hashtags" do
|
468
|
+
let(:activity) { Friends::Activity.new(str: "Party! #fun #crazy #fun") }
|
469
|
+
it { subject.must_equal Set.new(["#fun", "#crazy"]) }
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe "#includes_hashtag?" do
|
474
|
+
subject { activity.includes_hashtag?(hashtag: hashtag) }
|
475
|
+
let(:activity) { Friends::Activity.new(str: "Enormous ball pit! #fun") }
|
476
|
+
|
477
|
+
describe "when the given hashtag is not in the activity" do
|
478
|
+
let(:hashtag) { "#garbage" }
|
479
|
+
it { subject.must_equal false }
|
480
|
+
end
|
481
|
+
|
482
|
+
describe "when the given word is in the activity but not as a hashtag" do
|
483
|
+
let(:hashtag) { "#ball" }
|
484
|
+
it { subject.must_equal false }
|
485
|
+
end
|
486
|
+
|
487
|
+
describe "when the given hashtag is in the activity" do
|
488
|
+
let(:hashtag) { "#fun" }
|
489
|
+
it { subject.must_equal true }
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
459
493
|
describe "#friend_names" do
|
460
494
|
subject { activity.friend_names }
|
461
495
|
|
data/test/friend_spec.rb
CHANGED
@@ -62,7 +62,7 @@ describe Friends::Friend do
|
|
62
62
|
|
63
63
|
describe "when the nickname is present" do
|
64
64
|
let(:friend) do
|
65
|
-
Friends::Friend.new(name: friend_name, nickname_str: "
|
65
|
+
Friends::Friend.new(name: friend_name, nickname_str: "Jake")
|
66
66
|
end
|
67
67
|
|
68
68
|
it "removes the nickname" do
|
@@ -79,6 +79,45 @@ describe Friends::Friend do
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
describe "#add_hashtag" do
|
83
|
+
subject { friend.add_hashtag("#college") }
|
84
|
+
|
85
|
+
it "adds the nickname" do
|
86
|
+
subject
|
87
|
+
friend.hashtags.must_include("#college")
|
88
|
+
end
|
89
|
+
|
90
|
+
it "does not keep duplicates" do
|
91
|
+
# Add the same nickname twice. Do not use `subject` because it's memoized.
|
92
|
+
friend.add_hashtag("#college")
|
93
|
+
friend.add_hashtag("#college")
|
94
|
+
|
95
|
+
friend.hashtags.must_equal ["#college"]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#remove_hashtag" do
|
100
|
+
subject { friend.remove_hashtag("#school") }
|
101
|
+
|
102
|
+
describe "when the hashtag is present" do
|
103
|
+
let(:friend) do
|
104
|
+
Friends::Friend.new(name: friend_name, hashtags_str: "#school #work")
|
105
|
+
end
|
106
|
+
|
107
|
+
it "removes the nickname" do
|
108
|
+
friend.instance_variable_get(:@hashtags).must_equal ["#school", "#work"]
|
109
|
+
subject
|
110
|
+
friend.instance_variable_get(:@hashtags).must_equal ["#work"]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "when the nickname is not present" do
|
115
|
+
it "raises an error if the nickname is not found" do
|
116
|
+
proc { subject }.must_raise Friends::FriendsError
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
82
121
|
describe "#n_activities" do
|
83
122
|
subject { friend.n_activities }
|
84
123
|
|
data/test/introvert_spec.rb
CHANGED
@@ -34,7 +34,11 @@ describe Friends::Introvert do
|
|
34
34
|
let(:args) { { filename: filename } }
|
35
35
|
let(:introvert) { Friends::Introvert.new(args) }
|
36
36
|
let(:friend_names) { ["George Washington Carver", "Betsy Ross"] }
|
37
|
-
let(:friends)
|
37
|
+
let(:friends) do
|
38
|
+
friend_names.map do |name|
|
39
|
+
Friends::Friend.new(name: name, hashtags_str: "#test")
|
40
|
+
end
|
41
|
+
end
|
38
42
|
let(:activities) do
|
39
43
|
[
|
40
44
|
Friends::Activity.new(
|
@@ -110,20 +114,18 @@ describe Friends::Introvert do
|
|
110
114
|
end
|
111
115
|
|
112
116
|
describe "#list_friends" do
|
113
|
-
subject
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
stub_friends(friends) do
|
120
|
-
subject.must_equal friend_names
|
121
|
-
end
|
122
|
-
end
|
117
|
+
subject do
|
118
|
+
introvert.list_friends(
|
119
|
+
location_name: location_name,
|
120
|
+
tagged: tagged,
|
121
|
+
verbose: verbose
|
122
|
+
)
|
123
123
|
end
|
124
124
|
|
125
125
|
describe "when a location name has been passed" do
|
126
126
|
let(:location_name) { "Atlantis" }
|
127
|
+
let(:tagged) { nil }
|
128
|
+
let(:verbose) { false }
|
127
129
|
let(:friends) do
|
128
130
|
[
|
129
131
|
Friends::Friend.new(name: "Mark Watney", location_name: "Mars"),
|
@@ -133,7 +135,29 @@ describe Friends::Introvert do
|
|
133
135
|
]
|
134
136
|
end
|
135
137
|
|
136
|
-
it "lists the names of friends" do
|
138
|
+
it "lists the names of friends in that location" do
|
139
|
+
stub_friends(friends) do
|
140
|
+
stub_locations(locations) do
|
141
|
+
subject.must_equal ["Aquaman", "Shark-Boy"]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "when a hashtag has been passed" do
|
148
|
+
let(:location_name) { nil }
|
149
|
+
let(:tagged) { "#superhero" }
|
150
|
+
let(:verbose) { false }
|
151
|
+
let(:friends) do
|
152
|
+
[
|
153
|
+
Friends::Friend.new(name: "Mark Watney"),
|
154
|
+
Friends::Friend.new(name: "Aquaman", hashtags_str: "#superhero"),
|
155
|
+
Friends::Friend.new(name: "Shark-Boy", hashtags_str: "#superhero"),
|
156
|
+
Friends::Friend.new(name: "Ms. Nowhere")
|
157
|
+
]
|
158
|
+
end
|
159
|
+
|
160
|
+
it "lists the names of friends in that location" do
|
137
161
|
stub_friends(friends) do
|
138
162
|
stub_locations(locations) do
|
139
163
|
subject.must_equal ["Aquaman", "Shark-Boy"]
|
@@ -141,6 +165,32 @@ describe Friends::Introvert do
|
|
141
165
|
end
|
142
166
|
end
|
143
167
|
end
|
168
|
+
|
169
|
+
describe "when not verbose" do
|
170
|
+
let(:verbose) { false }
|
171
|
+
let(:location_name) { nil }
|
172
|
+
let(:tagged) { nil }
|
173
|
+
it "lists the names of friends" do
|
174
|
+
stub_friends(friends) do
|
175
|
+
subject.must_equal friend_names
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe "when verbose" do
|
181
|
+
let(:verbose) { true }
|
182
|
+
let(:location_name) { nil }
|
183
|
+
let(:tagged) { nil }
|
184
|
+
it "lists the names and details of friends" do
|
185
|
+
stub_friends(friends) do
|
186
|
+
# Just check that there's a difference between the verbose and
|
187
|
+
# non-verbose versions of friends (otherwise our test is useless).
|
188
|
+
subject.wont_equal friend_names
|
189
|
+
|
190
|
+
subject.must_equal friends.map(&:to_s)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
144
194
|
end
|
145
195
|
|
146
196
|
describe "#add_friend" do
|
@@ -151,7 +201,9 @@ describe Friends::Introvert do
|
|
151
201
|
it "adds the given friend" do
|
152
202
|
stub_friends(friends) do
|
153
203
|
subject
|
154
|
-
introvert.
|
204
|
+
introvert.
|
205
|
+
instance_variable_get(:@friends).
|
206
|
+
map(&:name).
|
155
207
|
must_include new_friend_name
|
156
208
|
end
|
157
209
|
end
|
@@ -214,17 +266,77 @@ describe Friends::Introvert do
|
|
214
266
|
end
|
215
267
|
end
|
216
268
|
|
269
|
+
describe "#list_hashtags" do
|
270
|
+
subject { introvert.list_hashtags(from: from) }
|
271
|
+
|
272
|
+
let(:activities) do
|
273
|
+
[
|
274
|
+
Friends::Activity.new(str: "Lunch in the park. #picnic #food"),
|
275
|
+
Friends::Activity.new(str: "Fancy dinner. #food #swanky")
|
276
|
+
]
|
277
|
+
end
|
278
|
+
|
279
|
+
let(:friends) do
|
280
|
+
[
|
281
|
+
Friends::Friend.new(name: "Grace Hopper", hashtags_str: "#coder #navy"),
|
282
|
+
Friends::Friend.new(name: "James Bond", hashtags_str: "#cool #swanky")
|
283
|
+
]
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "when from flag is nil" do
|
287
|
+
let(:from) { nil }
|
288
|
+
it "lists all hashtags in sorted order" do
|
289
|
+
stub_activities(activities) do
|
290
|
+
stub_friends(friends) do
|
291
|
+
subject.must_equal [
|
292
|
+
"#coder",
|
293
|
+
"#cool",
|
294
|
+
"#food",
|
295
|
+
"#navy",
|
296
|
+
"#picnic",
|
297
|
+
"#swanky"
|
298
|
+
]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe 'when from flag is "activities"' do
|
305
|
+
let(:from) { "activities" }
|
306
|
+
it "lists all activity hashtags in sorted order" do
|
307
|
+
stub_activities(activities) do
|
308
|
+
stub_friends(friends) do
|
309
|
+
subject.must_equal ["#food", "#picnic", "#swanky"]
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe 'when from flag is "friends"' do
|
316
|
+
let(:from) { "friends" }
|
317
|
+
it "lists all friend hashtags in sorted order" do
|
318
|
+
stub_activities(activities) do
|
319
|
+
stub_friends(friends) do
|
320
|
+
subject.must_equal ["#coder", "#cool", "#navy", "#swanky"]
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
217
327
|
describe "#list_activities" do
|
218
328
|
subject do
|
219
329
|
introvert.list_activities(
|
220
330
|
limit: limit,
|
221
331
|
with: with,
|
222
|
-
location_name: location_name
|
332
|
+
location_name: location_name,
|
333
|
+
tagged: tagged
|
223
334
|
)
|
224
335
|
end
|
225
336
|
let(:limit) { nil }
|
226
337
|
let(:with) { nil }
|
227
338
|
let(:location_name) { nil }
|
339
|
+
let(:tagged) { nil }
|
228
340
|
|
229
341
|
describe "when the limit is lower than the number of activities" do
|
230
342
|
let(:limit) { 1 }
|
@@ -271,7 +383,7 @@ describe Friends::Introvert do
|
|
271
383
|
|
272
384
|
it "lists the activities" do
|
273
385
|
stub_activities(activities) do
|
274
|
-
subject.must_equal activities.map(&:
|
386
|
+
subject.must_equal activities.map(&:to_s)
|
275
387
|
end
|
276
388
|
end
|
277
389
|
end
|
@@ -308,7 +420,7 @@ describe Friends::Introvert do
|
|
308
420
|
stub_friends(friends) do
|
309
421
|
stub_activities(activities) do
|
310
422
|
# Only one activity has that friend.
|
311
|
-
subject.must_equal activities[0..0].map(&:
|
423
|
+
subject.must_equal activities[0..0].map(&:to_s)
|
312
424
|
end
|
313
425
|
end
|
314
426
|
end
|
@@ -320,7 +432,7 @@ describe Friends::Introvert do
|
|
320
432
|
|
321
433
|
it "lists the activities" do
|
322
434
|
stub_activities(activities) do
|
323
|
-
subject.must_equal activities.map(&:
|
435
|
+
subject.must_equal activities.map(&:to_s)
|
324
436
|
end
|
325
437
|
end
|
326
438
|
end
|
@@ -375,13 +487,60 @@ describe Friends::Introvert do
|
|
375
487
|
stub_locations(locations) do
|
376
488
|
stub_activities(activities) do
|
377
489
|
# Only one activity has that friend.
|
378
|
-
subject.must_equal activities[0..0].map(&:
|
490
|
+
subject.must_equal activities[0..0].map(&:to_s)
|
379
491
|
end
|
380
492
|
end
|
381
493
|
end
|
382
494
|
end
|
383
495
|
end
|
384
496
|
end
|
497
|
+
|
498
|
+
describe "when not filtering by a hashtag" do
|
499
|
+
let(:tagged) { nil }
|
500
|
+
|
501
|
+
it "lists the activities" do
|
502
|
+
stub_activities(activities) do
|
503
|
+
subject.must_equal activities.map(&:to_s)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
describe "when filtering by a hashtag" do
|
509
|
+
let(:activities) do
|
510
|
+
[
|
511
|
+
Friends::Activity.new(str: "Tennis after work. #exercise #tennis"),
|
512
|
+
Friends::Activity.new(str: "Wimbledon! #tennis"),
|
513
|
+
Friends::Activity.new(str: "Drinks after work. #beer")
|
514
|
+
]
|
515
|
+
end
|
516
|
+
|
517
|
+
describe "when the hashtag ('#hashtag') is not used at all" do
|
518
|
+
let(:tagged) { "#garbage" }
|
519
|
+
it "returns no results" do
|
520
|
+
stub_activities(activities) do
|
521
|
+
subject.must_equal []
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
describe "when the hashtag ('#hashtag') is used once" do
|
527
|
+
let(:tagged) { "#beer" }
|
528
|
+
it "returns the activity with that hashtag" do
|
529
|
+
stub_activities(activities) do
|
530
|
+
subject.must_equal [activities.last.to_s]
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
describe "when the hashtag ('#hashtag') is used multiple times" do
|
536
|
+
let(:tagged) { "#tennis" }
|
537
|
+
it "returns the activities with that hashtag" do
|
538
|
+
stub_activities(activities) do
|
539
|
+
subject.must_equal activities[0..1].map(&:to_s)
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
end
|
385
544
|
end
|
386
545
|
|
387
546
|
describe "#add_activity" do
|
@@ -426,24 +585,6 @@ describe Friends::Introvert do
|
|
426
585
|
end
|
427
586
|
end
|
428
587
|
end
|
429
|
-
|
430
|
-
describe "when given names with leading and trailing spaces" do
|
431
|
-
let(:new_name) { " David Bowie " }
|
432
|
-
let(:old_name) { friend_names.last + " " }
|
433
|
-
subject do
|
434
|
-
introvert.rename_friend(old_name: old_name, new_name: new_name)
|
435
|
-
end
|
436
|
-
|
437
|
-
it "correctly strips the spaces" do
|
438
|
-
stub_friends(friends) do
|
439
|
-
stub_activities(activities) do
|
440
|
-
subject
|
441
|
-
introvert.activities.first.description.must_include "David Bowie"
|
442
|
-
introvert.activities.last.description.must_include "David Bowie"
|
443
|
-
end
|
444
|
-
end
|
445
|
-
end
|
446
|
-
end
|
447
588
|
end
|
448
589
|
|
449
590
|
describe "#rename_location" do
|
@@ -498,25 +639,6 @@ describe Friends::Introvert do
|
|
498
639
|
end
|
499
640
|
end
|
500
641
|
end
|
501
|
-
|
502
|
-
describe "when given names with leading and trailing spaces" do
|
503
|
-
let(:new_name) { " Paris, France " }
|
504
|
-
let(:old_name) { " Paris " }
|
505
|
-
subject do
|
506
|
-
introvert.rename_location(old_name: old_name, new_name: new_name)
|
507
|
-
end
|
508
|
-
|
509
|
-
it "correctly strips the spaces" do
|
510
|
-
stub_locations(locations) do
|
511
|
-
stub_activities(activities) do
|
512
|
-
subject
|
513
|
-
introvert.activities.map do |activity|
|
514
|
-
activity.description.include? new_name
|
515
|
-
end.must_equal [true, true, false]
|
516
|
-
end
|
517
|
-
end
|
518
|
-
end
|
519
|
-
end
|
520
642
|
end
|
521
643
|
|
522
644
|
describe "#set_location" do
|
@@ -554,8 +676,32 @@ describe Friends::Introvert do
|
|
554
676
|
end
|
555
677
|
|
556
678
|
it "returns the modified friend" do
|
557
|
-
friend = Friends::Friend.new(name: "Jeff",
|
558
|
-
|
679
|
+
friend = Friends::Friend.new(name: "Jeff", nickname_str: "The Dude")
|
680
|
+
stub_friends([friend]) do
|
681
|
+
subject.must_equal friend
|
682
|
+
end
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
describe "#add_hashtag" do
|
687
|
+
subject do
|
688
|
+
introvert.add_hashtag(name: friend_names.first, hashtag: "#school")
|
689
|
+
end
|
690
|
+
|
691
|
+
it "returns the modified friend" do
|
692
|
+
stub_friends(friends) do
|
693
|
+
subject.must_equal friends.first
|
694
|
+
end
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
describe "#remove_hashtag" do
|
699
|
+
subject do
|
700
|
+
introvert.remove_hashtag(name: "Jeff", hashtag: "#school")
|
701
|
+
end
|
702
|
+
|
703
|
+
it "returns the modified friend" do
|
704
|
+
friend = Friends::Friend.new(name: "Jeff", hashtags_str: "#school")
|
559
705
|
stub_friends([friend]) do
|
560
706
|
subject.must_equal friend
|
561
707
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: friends
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.20'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Evelyn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chronic
|