friends 0.19 → 0.20
Sign up to get free protection for your applications and to get access to all the features.
- 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
|