friends 0.54 → 0.55
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/.github/PULL_REQUEST_TEMPLATE.md +2 -2
- data/.github/workflows/main.yml +36 -0
- data/CHANGELOG.md +19 -0
- data/README.md +85 -49
- data/bin/friends +1 -1
- data/lib/friends/commands/list.rb +29 -19
- data/lib/friends/introvert.rb +66 -56
- data/lib/friends/version.rb +1 -1
- data/test/commands/list/friends_spec.rb +101 -7
- data/test/commands/list/locations_spec.rb +92 -4
- data/test/default_file_spec.rb +1 -1
- data/test/helper.rb +12 -1
- metadata +3 -7
- data/.travis.yml +0 -24
- data/test/commands/list/favorite/friends_spec.rb +0 -113
- data/test/commands/list/favorite/locations_spec.rb +0 -149
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04bb70e6973e141306240dba6c4c8f0d26322ad9e795c70b9407b36fa243386d
|
4
|
+
data.tar.gz: 4495b8c22adc4274ca870c81c5112d0ef616a2bf804e168cf469cdbfafee6a99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e2638a0504eca66dc731052edaa2c86c070222571509187165a08eaff29a57f89c69aa00ee7b2ca1bd633cc783fbeb7f880767151ec7b6de737938b5dea875e
|
7
|
+
data.tar.gz: 16c2a9a1ba086507060dc53cc1a76ef4789195bf8152a45a80a8f453027ee7c494921b7ec30c26ebcebb89c0a3c8653a63a9de6d258273d62d7b3cd474201a3f
|
@@ -8,9 +8,9 @@ merge this change:
|
|
8
8
|
- [ ] The code in these changes works correctly.
|
9
9
|
- [ ] Code has tests as appropriate.
|
10
10
|
- [ ] Code has been reviewed by @JacobEvelyn.
|
11
|
-
- [ ] All tests pass on
|
11
|
+
- [ ] All tests pass on GitHub.
|
12
12
|
- [ ] Code coverage remains high.
|
13
|
-
- [ ]
|
13
|
+
- [ ] RuboCop reports no issues on GitHub.
|
14
14
|
- [ ] The `README.md` file is updated as appropriate.
|
15
15
|
|
16
16
|
Don't worry—this list will get checked off in no time!
|
@@ -0,0 +1,36 @@
|
|
1
|
+
name: Main
|
2
|
+
on:
|
3
|
+
pull_request:
|
4
|
+
branches:
|
5
|
+
- main
|
6
|
+
push:
|
7
|
+
branches:
|
8
|
+
- main
|
9
|
+
jobs:
|
10
|
+
ci:
|
11
|
+
name: CI
|
12
|
+
strategy:
|
13
|
+
fail-fast: false
|
14
|
+
matrix:
|
15
|
+
ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0]
|
16
|
+
runs-on: ubuntu-latest
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
|
20
|
+
# Conditionally configure bundler via environment variables as advised
|
21
|
+
# * https://github.com/ruby/setup-ruby#bundle-config
|
22
|
+
- name: Set code coverage environment variable
|
23
|
+
run: echo "CODE_COVERAGE=true" >> $GITHUB_ENV
|
24
|
+
if: matrix.ruby == 3.0
|
25
|
+
|
26
|
+
# Use 'bundler-cache: true' instead of actions/cache as advised:
|
27
|
+
# * https://github.com/actions/cache/blob/main/examples.md#ruby---bundler
|
28
|
+
- uses: ruby/setup-ruby@v1
|
29
|
+
with:
|
30
|
+
ruby-version: ${{ matrix.ruby }}
|
31
|
+
bundler-cache: true
|
32
|
+
|
33
|
+
- run: bundle exec rake test
|
34
|
+
|
35
|
+
- run: bundle exec rubocop
|
36
|
+
if: matrix.ruby == 3.0
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,25 @@
|
|
4
4
|
making a small donation (🙏) with the **Sponsor** button at the top of this page to
|
5
5
|
show you appreciate its continued development.
|
6
6
|
|
7
|
+
## [v0.55](https://github.com/JacobEvelyn/friends/tree/v0.55) (2021-07-25)
|
8
|
+
|
9
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.54...v0.55)
|
10
|
+
|
11
|
+
**Implemented enhancements:**
|
12
|
+
|
13
|
+
- Add --sort flag to list friends/locations [\#276](https://github.com/JacobEvelyn/friends/pull/276) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
14
|
+
|
15
|
+
**Closed issues:**
|
16
|
+
|
17
|
+
- Add tests for Ruby 3.0 [\#279](https://github.com/JacobEvelyn/friends/issues/279)
|
18
|
+
- Switch from Travis to GitHub Actions [\#277](https://github.com/JacobEvelyn/friends/issues/277)
|
19
|
+
- Add new list command to log any activity or note related to specified friend [\#270](https://github.com/JacobEvelyn/friends/issues/270)
|
20
|
+
- Replace `favorites` commands with more flexible `--sort` options [\#247](https://github.com/JacobEvelyn/friends/issues/247)
|
21
|
+
|
22
|
+
**Merged pull requests:**
|
23
|
+
|
24
|
+
- Move CI from Travis to GitHub Actions [\#278](https://github.com/JacobEvelyn/friends/pull/278) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
25
|
+
|
7
26
|
## [v0.54](https://github.com/JacobEvelyn/friends/tree/v0.54) (2020-10-29)
|
8
27
|
|
9
28
|
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.53...v0.54)
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
[](https://badge.fury.io/rb/friends)
|
2
2
|
[](https://codecov.io/gh/JacobEvelyn/friends)
|
3
|
-
[](https://github.com/JacobEvelyn/friends/actions?query=workflow%3AMain)
|
4
4
|
[](http://clayallsopp.github.io/readme-score?url=JacobEvelyn/friends)
|
5
5
|
[](http://inch-ci.org/github/JacobEvelyn/friends)
|
6
6
|
[](https://rubygems.org/gems/friends)
|
@@ -32,22 +32,19 @@ lots of help), and give feedback! This project is
|
|
32
32
|
- [Command reference](#command-reference)
|
33
33
|
- `add`
|
34
34
|
- [`add activity`](#add-activity)
|
35
|
+
- [Setting a default location](#setting-a-default-location)
|
35
36
|
- [`add note`](#add-note)
|
36
37
|
- [`add friend`](#add-friend)
|
37
38
|
- [`add tag`](#add-tag)
|
38
39
|
- [`add location`](#add-location)
|
39
40
|
- [`add nickname`](#add-nickname)
|
40
41
|
- [`add alias`](#add-alias)
|
41
|
-
- [Adding a default location](#adding-a-default-location)
|
42
42
|
- [`clean`](#clean)
|
43
43
|
- [`graph`](#graph)
|
44
44
|
- [`help`](#help)
|
45
45
|
- `list`
|
46
46
|
- [`list activities`](#list-activities)
|
47
47
|
- [`list notes`](#list-notes)
|
48
|
-
- `list favorite`
|
49
|
-
- [`list favorite friends`](#list-favorite-friends)
|
50
|
-
- [`list favorite locations`](#list-favorite-locations)
|
51
48
|
- [`list friends`](#list-friends)
|
52
49
|
- [`list tags`](#list-tags)
|
53
50
|
- [`list locations`](#list-locations)
|
@@ -354,6 +351,27 @@ This is really handy for when you have an activity involving a friend or locatio
|
|
354
351
|
you can't remember if you've already added. Just use the signifiers and
|
355
352
|
they'll be added if necessary!
|
356
353
|
|
354
|
+
##### Setting a default location
|
355
|
+
|
356
|
+
When an activity includes the phrase to \_LOCATION\_ (e.g., Took a plane to \_Paris\_), all future activities that have no explicit location will be associated with that location:
|
357
|
+
|
358
|
+
```bash
|
359
|
+
$ friends add activity Took a plane to Paris
|
360
|
+
Activity added: "2020-01-04: Took a plane to Paris"
|
361
|
+
Default location set to: "Paris"
|
362
|
+
$ friends add activity Ate lunch at a charming café
|
363
|
+
Activity added: "2020-01-04: Ate lunch at a charming café"
|
364
|
+
$ friends add activity Left the city to go to Chamonix
|
365
|
+
Activity added: "2020-01-04: Left the city to go to Chamonix"
|
366
|
+
Default location set to: "Chamonix"
|
367
|
+
```
|
368
|
+
|
369
|
+
```bash
|
370
|
+
$ friends list activities --in Paris
|
371
|
+
2019-01-04: Ate lunch at a charming café
|
372
|
+
2019-01-04: Took a plane to Paris
|
373
|
+
```
|
374
|
+
|
357
375
|
#### `add note`
|
358
376
|
|
359
377
|
Notes can be added exactly like activities, either on one line:
|
@@ -410,7 +428,7 @@ Location added: "Atlantis"
|
|
410
428
|
|
411
429
|
```bash
|
412
430
|
$ friends add nickname "Grace Hopper" "The Admiral"
|
413
|
-
Nickname added: "Grace Hopper (a.k.a. The Admiral)
|
431
|
+
Nickname added: "Grace Hopper (a.k.a. The Admiral)"
|
414
432
|
$ friends add nickname "Grace Hopper" "Amazing Grace"
|
415
433
|
Nickname added: "Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace)"
|
416
434
|
```
|
@@ -419,32 +437,11 @@ Nickname added: "Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace)"
|
|
419
437
|
|
420
438
|
```bash
|
421
439
|
$ friends add alias "New York City" "NYC"
|
422
|
-
Alias added: "New York City (a.k.a. NYC)
|
440
|
+
Alias added: "New York City (a.k.a. NYC)"
|
423
441
|
$ friends add alias "New York City" "Big Apple"
|
424
442
|
Alias added: "New York City (a.k.a. NYC a.k.a. Big Apple)"
|
425
443
|
```
|
426
444
|
|
427
|
-
#### Setting a default location
|
428
|
-
|
429
|
-
When an activity includes the phrase to \_LOCATION\_ (e.g., Took a plane to \_Paris\_), all future activities that have no explicit location will be associated with that location:
|
430
|
-
|
431
|
-
```bash
|
432
|
-
$ friends add activity Took a plane to Paris
|
433
|
-
Activity added: "2020-01-04: Took a plane to Paris"
|
434
|
-
Default location set to: "Paris"
|
435
|
-
$ friends add activity Ate lunch at a charming café
|
436
|
-
Activity added: "2020-01-04: Ate lunch at a charming café"
|
437
|
-
$ friends add activity Left the city to go to Chamonix
|
438
|
-
Activity added: "2020-01-04: Left the city to go to Chamonix"
|
439
|
-
Default location set to: "Chamonix"
|
440
|
-
```
|
441
|
-
|
442
|
-
```bash
|
443
|
-
$ friends list activities --in Paris
|
444
|
-
2019-01-04: Ate lunch at a charming café
|
445
|
-
2019-01-04: Took a plane to Paris
|
446
|
-
```
|
447
|
-
|
448
445
|
#### `clean`
|
449
446
|
|
450
447
|
Reads and re-writes the `friends.md` file:
|
@@ -711,39 +708,42 @@ $ friends list notes --tagged school --with Marie
|
|
711
708
|
2015-06-06: Marie Curie just got accepted into a PhD program in Paris. @school
|
712
709
|
```
|
713
710
|
|
714
|
-
#### `list
|
711
|
+
#### `list friends`
|
715
712
|
|
716
|
-
Lists your
|
713
|
+
Lists all of your friends in alphabetical order:
|
717
714
|
|
718
715
|
```bash
|
719
|
-
$ friends list
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
3. Marie Curie (0)
|
716
|
+
$ friends list friends
|
717
|
+
George Washington Carver
|
718
|
+
Grace Hopper
|
719
|
+
Marie Curie
|
724
720
|
```
|
725
721
|
|
726
|
-
|
727
|
-
|
728
|
-
Lists your "favorite" locations (by total number of activities):
|
722
|
+
Or you can choose to sort by number of activities:
|
729
723
|
|
730
724
|
```bash
|
731
|
-
$ friends list
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
3. London (0)
|
725
|
+
$ friends list friends --sort n-activities
|
726
|
+
2 activities: George Washington Carver
|
727
|
+
2 activities: Grace Hopper
|
728
|
+
1 activity: Marie Curie
|
736
729
|
```
|
737
730
|
|
738
|
-
|
731
|
+
Or by most recent activity:
|
739
732
|
|
740
|
-
|
733
|
+
```bash
|
734
|
+
$ friends list friends --sort recency
|
735
|
+
7 days ago: Grace Hopper
|
736
|
+
308 days ago: George Washington Carver
|
737
|
+
312 days ago: Marie Curie
|
738
|
+
```
|
739
|
+
|
740
|
+
And you can reverse the sorting at any time:
|
741
741
|
|
742
742
|
```bash
|
743
|
-
$ friends list friends
|
744
|
-
|
745
|
-
Grace Hopper
|
746
|
-
|
743
|
+
$ friends list friends --sort n-activities --reverse
|
744
|
+
1 activity: Marie Curie
|
745
|
+
2 activities: Grace Hopper
|
746
|
+
2 activities: George Washington Carver
|
747
747
|
```
|
748
748
|
|
749
749
|
You can also include friend nicknames, locations, and tags:
|
@@ -836,6 +836,42 @@ New York City
|
|
836
836
|
Paris
|
837
837
|
```
|
838
838
|
|
839
|
+
Or you can choose to sort by number of activities:
|
840
|
+
|
841
|
+
```bash
|
842
|
+
$ friends list locations --sort n-activities
|
843
|
+
1 activity: New York City
|
844
|
+
1 activity: Paris
|
845
|
+
0 activities: Atlantis
|
846
|
+
```
|
847
|
+
|
848
|
+
Or by most recent activity:
|
849
|
+
|
850
|
+
```bash
|
851
|
+
$ friends list locations --sort recency
|
852
|
+
N/A days ago: Atlantis
|
853
|
+
7 days ago: New York City
|
854
|
+
312 days ago: Paris
|
855
|
+
```
|
856
|
+
|
857
|
+
And you can reverse the sorting at any time:
|
858
|
+
|
859
|
+
```bash
|
860
|
+
$ friends list friends --sort n-activities --reverse
|
861
|
+
0 activities: Atlantis
|
862
|
+
1 activity: Paris
|
863
|
+
1 activity: New York City
|
864
|
+
```
|
865
|
+
|
866
|
+
You can also include location aliases:
|
867
|
+
|
868
|
+
```bash
|
869
|
+
$ friends list locations --verbose
|
870
|
+
Atlantis
|
871
|
+
New York City (a.k.a. NYC)
|
872
|
+
Paris
|
873
|
+
```
|
874
|
+
|
839
875
|
#### Advanced searching
|
840
876
|
|
841
877
|
Since `friends` is a command-line program, we can easily support
|
data/bin/friends
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
if ENV["
|
4
|
+
if ENV["CI"] == "true" && ENV["CODE_COVERAGE"] == "true"
|
5
5
|
require "simplecov"
|
6
6
|
SimpleCov.print_error_status = false
|
7
7
|
SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter
|
@@ -19,11 +19,23 @@ command :list do |list|
|
|
19
19
|
negatable: false,
|
20
20
|
desc: "Output friend nicknames, locations, and tags"
|
21
21
|
|
22
|
+
list_friends.flag :sort,
|
23
|
+
default_value: "alphabetical",
|
24
|
+
arg_name: "ATTRIBUTE",
|
25
|
+
must_match: %w[alphabetical n-activities recency],
|
26
|
+
desc: "Sort output by one of: alphabetical, n-activities, recency"
|
27
|
+
|
28
|
+
list_friends.switch :reverse,
|
29
|
+
negatable: false,
|
30
|
+
desc: "Reverse the sort order"
|
31
|
+
|
22
32
|
list_friends.action do |_, options|
|
23
33
|
@introvert.list_friends(
|
24
34
|
location_name: options[:in],
|
25
35
|
tagged: options[:tagged],
|
26
|
-
verbose: options[:verbose]
|
36
|
+
verbose: options[:verbose],
|
37
|
+
sort: options[:sort],
|
38
|
+
reverse: options[:reverse]
|
27
39
|
)
|
28
40
|
end
|
29
41
|
end
|
@@ -76,8 +88,23 @@ command :list do |list|
|
|
76
88
|
list_locations.switch [:verbose],
|
77
89
|
negatable: false,
|
78
90
|
desc: "Output location aliases"
|
91
|
+
|
92
|
+
list_locations.flag :sort,
|
93
|
+
default_value: "alphabetical",
|
94
|
+
arg_name: "ATTRIBUTE",
|
95
|
+
must_match: %w[alphabetical n-activities recency],
|
96
|
+
desc: "Sort output by one of: alphabetical, n-activities, recency"
|
97
|
+
|
98
|
+
list_locations.switch :reverse,
|
99
|
+
negatable: false,
|
100
|
+
desc: "Reverse the sort order"
|
101
|
+
|
79
102
|
list_locations.action do |_, options|
|
80
|
-
@introvert.list_locations(
|
103
|
+
@introvert.list_locations(
|
104
|
+
verbose: options[:verbose],
|
105
|
+
sort: options[:sort],
|
106
|
+
reverse: options[:reverse]
|
107
|
+
)
|
81
108
|
end
|
82
109
|
end
|
83
110
|
|
@@ -92,21 +119,4 @@ command :list do |list|
|
|
92
119
|
@introvert.list_tags(from: options[:from])
|
93
120
|
end
|
94
121
|
end
|
95
|
-
|
96
|
-
list.desc "List favorite friends and locations"
|
97
|
-
list.command :favorite do |list_favorite|
|
98
|
-
list_favorite.desc "List favorite friends"
|
99
|
-
list_favorite.command :friends do |list_favorite_friends|
|
100
|
-
list_favorite_friends.action do
|
101
|
-
@introvert.list_favorite_friends
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
list_favorite.desc "List favorite locations"
|
106
|
-
list_favorite.command :locations do |list_favorite_locations|
|
107
|
-
list_favorite_locations.action do
|
108
|
-
@introvert.list_favorite_locations
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
122
|
end
|
data/lib/friends/introvert.rb
CHANGED
@@ -297,7 +297,11 @@ module Friends
|
|
297
297
|
# unfiltered
|
298
298
|
# @param verbose [Boolean] true iff we should output friend names with
|
299
299
|
# nicknames, locations, and tags; false for names only
|
300
|
-
|
300
|
+
# @param sort [String] one of:
|
301
|
+
# ["alphabetical", "n-activities", "recency"]
|
302
|
+
# @param reverse [Boolean] true iff we should reverse the sorted order of
|
303
|
+
# our output
|
304
|
+
def list_friends(location_name:, tagged:, verbose:, sort:, reverse:)
|
301
305
|
fs = @friends
|
302
306
|
|
303
307
|
# Filter by location if a name is passed.
|
@@ -313,18 +317,11 @@ module Friends
|
|
313
317
|
end
|
314
318
|
end
|
315
319
|
|
316
|
-
(
|
320
|
+
list_things(type: :friend, arr: fs, verbose: verbose, sort: sort, reverse: reverse)
|
317
321
|
end
|
318
322
|
|
319
|
-
|
320
|
-
|
321
|
-
list_favorite_things(:friend)
|
322
|
-
end
|
323
|
-
|
324
|
-
# List your favorite friends.
|
325
|
-
def list_favorite_locations
|
326
|
-
list_favorite_things(:location)
|
327
|
-
end
|
323
|
+
NA_STR = "N/A"
|
324
|
+
private_constant :NA_STR
|
328
325
|
|
329
326
|
# See `list_events` for all of the parameters we can pass.
|
330
327
|
def list_activities(**args)
|
@@ -337,8 +334,14 @@ module Friends
|
|
337
334
|
end
|
338
335
|
|
339
336
|
# List all location names in the friends file.
|
340
|
-
|
341
|
-
|
337
|
+
# @param verbose [Boolean] true iff we should output location names with
|
338
|
+
# aliases; false for names only
|
339
|
+
# @param sort [String] one of:
|
340
|
+
# ["alphabetical", "n-activities", "recency"]
|
341
|
+
# @param reverse [Boolean] true iff we should reverse the sorted order of
|
342
|
+
# our output
|
343
|
+
def list_locations(verbose:, sort:, reverse:)
|
344
|
+
list_things(type: :location, verbose: verbose, sort: sort, reverse: reverse)
|
342
345
|
end
|
343
346
|
|
344
347
|
# @param from [Array] containing any of: ["activities", "friends", "notes"]
|
@@ -537,6 +540,49 @@ module Friends
|
|
537
540
|
|
538
541
|
private
|
539
542
|
|
543
|
+
# List either friends or activities
|
544
|
+
# @param arr [Array<Friend|Activity>] a filtered list to print
|
545
|
+
# @param verbose [Boolean] true iff we should output names with
|
546
|
+
# aliases/nicknames/etc.; false for names only
|
547
|
+
# @param sort [String] one of:
|
548
|
+
# ["alphabetical", "n-activities", "recency"]
|
549
|
+
# @param reverse [Boolean] true iff we should reverse the sorted order of
|
550
|
+
# our output
|
551
|
+
def list_things(type:, arr: instance_variable_get("@#{type}s"), verbose:, sort:, reverse:)
|
552
|
+
case sort
|
553
|
+
when "alphabetical"
|
554
|
+
arr = stable_sort(arr) # In case the input file was not already sorted.
|
555
|
+
when "n-activities"
|
556
|
+
arr = stable_sort_by(arr) { |thing| -thing.n_activities }
|
557
|
+
when "recency"
|
558
|
+
today = Date.today
|
559
|
+
|
560
|
+
most_recent_activity_by_thing = @activities.each_with_object({}) do |activity, output|
|
561
|
+
activity.send("#{type}_names").each do |thing_name|
|
562
|
+
output[thing_name] = (today - activity.date).to_i unless output.key?(thing_name)
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
arr = stable_sort_by(arr) do |thing|
|
567
|
+
most_recent_activity_by_thing[thing.name] || -Float::INFINITY
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
(reverse ? arr.reverse : arr).each do |thing|
|
572
|
+
case sort
|
573
|
+
when "n-activities"
|
574
|
+
prefix = "#{Paint[thing.n_activities, :bold, :red]} "\
|
575
|
+
"activit#{thing.n_activities == 1 ? 'y' : 'ies'}: "
|
576
|
+
when "recency"
|
577
|
+
n_days = most_recent_activity_by_thing[thing.name] || NA_STR
|
578
|
+
prefix = "#{Paint[n_days, :bold, :red]} "\
|
579
|
+
"day#{'s' unless n_days == 1} ago: "
|
580
|
+
end
|
581
|
+
|
582
|
+
@output << "#{prefix}#{verbose ? thing.to_s : thing.name}"
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
540
586
|
# @param from [Array] containing any of: ["activities", "friends", "notes"]
|
541
587
|
# If not empty, limits the tags returned to only those from either
|
542
588
|
# activities, notes, or friends.
|
@@ -586,6 +632,13 @@ module Friends
|
|
586
632
|
arr.sort_by.with_index { |x, idx| [x, idx] }
|
587
633
|
end
|
588
634
|
|
635
|
+
# @param arr [Array] an unsorted array
|
636
|
+
# @param &block [block] used to return a value for each element's sort position
|
637
|
+
# @return [Array] a stably-sorted array
|
638
|
+
def stable_sort_by(arr)
|
639
|
+
arr.sort_by.with_index { |x, idx| [yield(x), idx] }
|
640
|
+
end
|
641
|
+
|
589
642
|
# Filter activities by friend, location and tag
|
590
643
|
# @param events [Array<Event>] the base events to list, either @activities or @notes
|
591
644
|
# @param with [Array<String>] the names of friends to filter by, or empty for
|
@@ -628,49 +681,6 @@ module Friends
|
|
628
681
|
events
|
629
682
|
end
|
630
683
|
|
631
|
-
# @param type [Symbol] one of: [:friend, :location]
|
632
|
-
# @raise [ArgumentError] if type is not one of: [:friend, :location]
|
633
|
-
def list_favorite_things(type)
|
634
|
-
unless [:friend, :location].include? type
|
635
|
-
raise ArgumentError, "Type must be either :friend or :location"
|
636
|
-
end
|
637
|
-
|
638
|
-
# Sort the results, with the most favorite thing first.
|
639
|
-
results = instance_variable_get("@#{type}s").sort_by do |thing|
|
640
|
-
-thing.n_activities
|
641
|
-
end
|
642
|
-
|
643
|
-
@output << "Your favorite #{type}s:"
|
644
|
-
|
645
|
-
max_str_size = results.map(&:name).map(&:size).max
|
646
|
-
|
647
|
-
grouped_results = results.group_by(&:n_activities)
|
648
|
-
|
649
|
-
rank = 1
|
650
|
-
first = true
|
651
|
-
data = grouped_results.each.with_object([]) do |(n_activities, things), arr|
|
652
|
-
things.each do |thing|
|
653
|
-
name = thing.name.ljust(max_str_size)
|
654
|
-
if first
|
655
|
-
label = n_activities == 1 ? " activity" : " activities"
|
656
|
-
first = false
|
657
|
-
end
|
658
|
-
str = "#{name} (#{n_activities}#{label})"
|
659
|
-
|
660
|
-
arr << [rank, str]
|
661
|
-
end
|
662
|
-
rank += things.size
|
663
|
-
end
|
664
|
-
|
665
|
-
# We need to use `data.last.first` instead of `rank` to determine the size
|
666
|
-
# of the numbering prefix because `rank` will simply be the size of all
|
667
|
-
# elements, which may be too large if the last element in the list is a tie.
|
668
|
-
num_str_size = data.last.first.to_s.size + 1 unless data.empty?
|
669
|
-
data.each do |ranking, str|
|
670
|
-
@output << "#{"#{ranking}.".ljust(num_str_size)} #{str}"
|
671
|
-
end
|
672
|
-
end
|
673
|
-
|
674
684
|
# Sets the n_activities field on each thing.
|
675
685
|
# @param type [Symbol] one of: [:friend, :location]
|
676
686
|
# @raise [ArgumentError] if `type` is not one of: [:friend, :location]
|
data/lib/friends/version.rb
CHANGED
@@ -26,26 +26,26 @@ clean_describe "list friends" do
|
|
26
26
|
# only reads from the (usually-sorted) file.
|
27
27
|
let(:content) { SCRAMBLED_CONTENT }
|
28
28
|
|
29
|
-
it "lists friends in
|
29
|
+
it "lists friends in alphabetical order" do
|
30
30
|
stdout_only <<-OUTPUT
|
31
31
|
George Washington Carver
|
32
|
-
Marie Curie
|
33
32
|
Grace Hopper
|
34
|
-
|
33
|
+
Marie Curie
|
35
34
|
Norman Borlaug
|
35
|
+
Stanislav Petrov
|
36
36
|
OUTPUT
|
37
37
|
end
|
38
38
|
|
39
39
|
describe "--verbose" do
|
40
40
|
subject { run_cmd("list friends --verbose") }
|
41
41
|
|
42
|
-
it "lists friends in
|
42
|
+
it "lists friends in sorted order with details" do
|
43
43
|
stdout_only <<-OUTPUT
|
44
44
|
George Washington Carver
|
45
|
-
Marie Curie [Atlantis] @science
|
46
45
|
Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] @navy @science
|
47
|
-
|
46
|
+
Marie Curie [Atlantis] @science
|
48
47
|
Norman Borlaug (a.k.a. Norm) @science @science:outdoors @science:outdoors:agronomy
|
48
|
+
Stanislav Petrov (a.k.a. Stan) @doesnt-trust-computers @doesnt-trust-computers:military-uses
|
49
49
|
OUTPUT
|
50
50
|
end
|
51
51
|
end
|
@@ -73,8 +73,8 @@ Norman Borlaug (a.k.a. Norm) @science @science:outdoors @science:outdoors:agrono
|
|
73
73
|
|
74
74
|
it "matches tag case-insensitively" do
|
75
75
|
stdout_only <<-OUTPUT
|
76
|
-
Marie Curie
|
77
76
|
Grace Hopper
|
77
|
+
Marie Curie
|
78
78
|
Norman Borlaug
|
79
79
|
OUTPUT
|
80
80
|
end
|
@@ -114,5 +114,99 @@ Stanislav Petrov
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
117
|
+
|
118
|
+
describe "--sort" do
|
119
|
+
subject { run_cmd("list friends --sort #{sort} #{reverse}") }
|
120
|
+
|
121
|
+
let(:reverse) { nil }
|
122
|
+
|
123
|
+
# Use scrambled content to differentiate between output that is sorted and output that
|
124
|
+
# only reads from the (usually-sorted) file.
|
125
|
+
let(:content) { SCRAMBLED_CONTENT }
|
126
|
+
|
127
|
+
describe "alphabetical" do
|
128
|
+
let(:sort) { "alphabetical" }
|
129
|
+
|
130
|
+
it "lists friends in sorted order" do
|
131
|
+
stdout_only <<-OUTPUT
|
132
|
+
George Washington Carver
|
133
|
+
Grace Hopper
|
134
|
+
Marie Curie
|
135
|
+
Norman Borlaug
|
136
|
+
Stanislav Petrov
|
137
|
+
OUTPUT
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "--reverse" do
|
141
|
+
let(:reverse) { "--reverse" }
|
142
|
+
|
143
|
+
it "lists friends in reverse order" do
|
144
|
+
stdout_only <<-OUTPUT
|
145
|
+
Stanislav Petrov
|
146
|
+
Norman Borlaug
|
147
|
+
Marie Curie
|
148
|
+
Grace Hopper
|
149
|
+
George Washington Carver
|
150
|
+
OUTPUT
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "n-activities" do
|
156
|
+
let(:sort) { "n-activities" }
|
157
|
+
|
158
|
+
it "lists friends in sorted order" do
|
159
|
+
stdout_only <<-OUTPUT
|
160
|
+
3 activities: George Washington Carver
|
161
|
+
2 activities: Grace Hopper
|
162
|
+
1 activity: Marie Curie
|
163
|
+
1 activity: Norman Borlaug
|
164
|
+
0 activities: Stanislav Petrov
|
165
|
+
OUTPUT
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "--reverse" do
|
169
|
+
let(:reverse) { "--reverse" }
|
170
|
+
|
171
|
+
it "lists friends in reverse order" do
|
172
|
+
stdout_only <<-OUTPUT
|
173
|
+
0 activities: Stanislav Petrov
|
174
|
+
1 activity: Norman Borlaug
|
175
|
+
1 activity: Marie Curie
|
176
|
+
2 activities: Grace Hopper
|
177
|
+
3 activities: George Washington Carver
|
178
|
+
OUTPUT
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "recency" do
|
184
|
+
let(:sort) { "recency" }
|
185
|
+
|
186
|
+
it "lists friends in sorted order" do
|
187
|
+
stdout_only_regexes [
|
188
|
+
"N/A days ago: Stanislav Petrov",
|
189
|
+
/\d+ days ago: George Washington Carver/,
|
190
|
+
/\d+ days ago: Norman Borlaug/,
|
191
|
+
/\d+ days ago: Grace Hopper/,
|
192
|
+
/\d+ days ago: Marie Curie/
|
193
|
+
]
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "--reverse" do
|
197
|
+
let(:reverse) { "--reverse" }
|
198
|
+
|
199
|
+
it "lists friends in reverse order" do
|
200
|
+
stdout_only_regexes [
|
201
|
+
/\d+ days ago: Marie Curie/,
|
202
|
+
/\d+ days ago: Grace Hopper/,
|
203
|
+
/\d+ days ago: Norman Borlaug/,
|
204
|
+
/\d+ days ago: George Washington Carver/,
|
205
|
+
"N/A days ago: Stanislav Petrov"
|
206
|
+
]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
117
211
|
end
|
118
212
|
end
|
@@ -26,24 +26,112 @@ clean_describe "list locations" do
|
|
26
26
|
# only reads from the (usually-sorted) file.
|
27
27
|
let(:content) { SCRAMBLED_CONTENT }
|
28
28
|
|
29
|
-
it "lists locations in
|
29
|
+
it "lists locations in alphabetical" do
|
30
30
|
stdout_only <<-OUTPUT
|
31
|
-
Paris
|
32
31
|
Atlantis
|
33
32
|
Martha's Vineyard
|
34
33
|
New York City
|
34
|
+
Paris
|
35
35
|
OUTPUT
|
36
36
|
end
|
37
37
|
|
38
|
+
describe "--sort" do
|
39
|
+
subject { run_cmd("list locations --sort #{sort} #{reverse}") }
|
40
|
+
|
41
|
+
let(:reverse) { nil }
|
42
|
+
|
43
|
+
# Use scrambled content to differentiate between output that is sorted and output that
|
44
|
+
# only reads from the (usually-sorted) file.
|
45
|
+
let(:content) { SCRAMBLED_CONTENT }
|
46
|
+
|
47
|
+
describe "alphabetical" do
|
48
|
+
let(:sort) { "alphabetical" }
|
49
|
+
|
50
|
+
it "lists locations in sorted order" do
|
51
|
+
stdout_only <<-OUTPUT
|
52
|
+
Atlantis
|
53
|
+
Martha's Vineyard
|
54
|
+
New York City
|
55
|
+
Paris
|
56
|
+
OUTPUT
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "--reverse" do
|
60
|
+
let(:reverse) { "--reverse" }
|
61
|
+
|
62
|
+
it "lists locations in reverse order" do
|
63
|
+
stdout_only <<-OUTPUT
|
64
|
+
Paris
|
65
|
+
New York City
|
66
|
+
Martha's Vineyard
|
67
|
+
Atlantis
|
68
|
+
OUTPUT
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "n-activities" do
|
74
|
+
let(:sort) { "n-activities" }
|
75
|
+
|
76
|
+
it "lists locations in sorted order" do
|
77
|
+
stdout_only <<-OUTPUT
|
78
|
+
1 activity: Paris
|
79
|
+
1 activity: Atlantis
|
80
|
+
1 activity: Martha's Vineyard
|
81
|
+
0 activities: New York City
|
82
|
+
OUTPUT
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "--reverse" do
|
86
|
+
let(:reverse) { "--reverse" }
|
87
|
+
|
88
|
+
it "lists locations in reverse order" do
|
89
|
+
stdout_only <<-OUTPUT
|
90
|
+
0 activities: New York City
|
91
|
+
1 activity: Martha's Vineyard
|
92
|
+
1 activity: Atlantis
|
93
|
+
1 activity: Paris
|
94
|
+
OUTPUT
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "recency" do
|
100
|
+
let(:sort) { "recency" }
|
101
|
+
|
102
|
+
it "lists locations in sorted order" do
|
103
|
+
stdout_only_regexes [
|
104
|
+
"N/A days ago: New York City",
|
105
|
+
/\d+ days ago: Atlantis/,
|
106
|
+
/\d+ days ago: Martha's Vineyard/,
|
107
|
+
/\d+ days ago: Paris/
|
108
|
+
]
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "--reverse" do
|
112
|
+
let(:reverse) { "--reverse" }
|
113
|
+
|
114
|
+
it "lists locations in reverse order" do
|
115
|
+
stdout_only_regexes [
|
116
|
+
/\d+ days ago: Paris/,
|
117
|
+
/\d+ days ago: Martha's Vineyard/,
|
118
|
+
/\d+ days ago: Atlantis/,
|
119
|
+
"N/A days ago: New York City"
|
120
|
+
]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
38
126
|
describe "--verbose" do
|
39
127
|
subject { run_cmd("list locations --verbose") }
|
40
128
|
|
41
|
-
it "lists locations in
|
129
|
+
it "lists locations in alphabetical order with details" do
|
42
130
|
stdout_only <<-OUTPUT
|
43
|
-
Paris
|
44
131
|
Atlantis
|
45
132
|
Martha's Vineyard
|
46
133
|
New York City (a.k.a. NYC a.k.a. NY)
|
134
|
+
Paris
|
47
135
|
OUTPUT
|
48
136
|
end
|
49
137
|
end
|
data/test/default_file_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require "./test/helper"
|
|
4
4
|
|
5
5
|
# Since this touches the ~/friends.md file instead of a temp
|
6
6
|
# one, we only want to run it on our CI servers.
|
7
|
-
if ENV["
|
7
|
+
if ENV["CI"] == "true"
|
8
8
|
describe "default filename behavior" do
|
9
9
|
let(:filename) { File.expand_path("~/friends.md") }
|
10
10
|
|
data/test/helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
if ENV["
|
3
|
+
if ENV["CI"] == "true" && ENV["CODE_COVERAGE"] == "true"
|
4
4
|
require "simplecov"
|
5
5
|
require "codecov"
|
6
6
|
SimpleCov.formatter = SimpleCov::Formatter::Codecov
|
@@ -121,6 +121,17 @@ def stderr_only(expected)
|
|
121
121
|
value(subject[:status]).must_be :>, 0
|
122
122
|
end
|
123
123
|
|
124
|
+
def stdout_only_regexes(regexes)
|
125
|
+
puts subject[:stderr] unless subject[:stderr] == ""
|
126
|
+
lines = subject[:stdout].split("\n")
|
127
|
+
regexes.each_with_index do |regex, index|
|
128
|
+
value(lines[index]).must_match regex
|
129
|
+
end
|
130
|
+
assert_nil(lines[regexes.size])
|
131
|
+
value(subject[:stderr]).must_equal ""
|
132
|
+
value(subject[:status]).must_equal 0
|
133
|
+
end
|
134
|
+
|
124
135
|
def file_equals(expected)
|
125
136
|
subject
|
126
137
|
value(File.read(filename)).must_equal expected
|
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.55'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Evelyn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chronic
|
@@ -120,10 +120,10 @@ files:
|
|
120
120
|
- ".github/FUNDING.yml"
|
121
121
|
- ".github/ISSUE_TEMPLATE.md"
|
122
122
|
- ".github/PULL_REQUEST_TEMPLATE.md"
|
123
|
+
- ".github/workflows/main.yml"
|
123
124
|
- ".gitignore"
|
124
125
|
- ".overcommit.yml"
|
125
126
|
- ".rubocop.yml"
|
126
|
-
- ".travis.yml"
|
127
127
|
- CHANGELOG.md
|
128
128
|
- CODE_OF_CONDUCT.md
|
129
129
|
- Gemfile
|
@@ -174,8 +174,6 @@ files:
|
|
174
174
|
- test/commands/graph_spec.rb
|
175
175
|
- test/commands/help_spec.rb
|
176
176
|
- test/commands/list/activities_spec.rb
|
177
|
-
- test/commands/list/favorite/friends_spec.rb
|
178
|
-
- test/commands/list/favorite/locations_spec.rb
|
179
177
|
- test/commands/list/friends_spec.rb
|
180
178
|
- test/commands/list/locations_spec.rb
|
181
179
|
- test/commands/list/notes_spec.rb
|
@@ -232,8 +230,6 @@ test_files:
|
|
232
230
|
- test/commands/graph_spec.rb
|
233
231
|
- test/commands/help_spec.rb
|
234
232
|
- test/commands/list/activities_spec.rb
|
235
|
-
- test/commands/list/favorite/friends_spec.rb
|
236
|
-
- test/commands/list/favorite/locations_spec.rb
|
237
233
|
- test/commands/list/friends_spec.rb
|
238
234
|
- test/commands/list/locations_spec.rb
|
239
235
|
- test/commands/list/notes_spec.rb
|
data/.travis.yml
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
cache: bundler
|
3
|
-
rvm:
|
4
|
-
- 2.3
|
5
|
-
- 2.4
|
6
|
-
- 2.5
|
7
|
-
- 2.6 # 2.7 is tested below
|
8
|
-
gemfile: Gemfile.old # The latest Ruby version uses Gemfile below
|
9
|
-
script:
|
10
|
-
- bundle exec rake test
|
11
|
-
matrix:
|
12
|
-
include:
|
13
|
-
- rvm: 2.7
|
14
|
-
gemfile: Gemfile
|
15
|
-
script:
|
16
|
-
- bundle exec rake test
|
17
|
-
- bundle exec rubocop
|
18
|
-
env:
|
19
|
-
- CODE_COVERAGE=true
|
20
|
-
branches:
|
21
|
-
only:
|
22
|
-
- main # Always run on the main branch
|
23
|
-
notifications:
|
24
|
-
email: false
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "./test/helper"
|
4
|
-
|
5
|
-
clean_describe "list favorite friends" do
|
6
|
-
subject { run_cmd("list favorite friends") }
|
7
|
-
|
8
|
-
describe "when file does not exist" do
|
9
|
-
let(:content) { nil }
|
10
|
-
|
11
|
-
it "prints a no-data message" do
|
12
|
-
stdout_only "Your favorite friends:"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
describe "when file is empty" do
|
17
|
-
let(:content) { "" }
|
18
|
-
|
19
|
-
it "prints a no-data message" do
|
20
|
-
stdout_only "Your favorite friends:"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe "when file has content" do
|
25
|
-
let(:content) do
|
26
|
-
<<-FILE
|
27
|
-
### Activities:
|
28
|
-
- 2017-01-01: Did some math with **Grace Hopper**.
|
29
|
-
- 2015-11-01: **Grace Hopper** and I went to _Martha's Vineyard_. George had to cancel at the last minute.
|
30
|
-
- 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**. @food
|
31
|
-
- 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. @partying
|
32
|
-
- 2014-11-15: Talked to **George Washington Carver** on the phone for an hour.
|
33
|
-
|
34
|
-
### Friends:
|
35
|
-
- George Washington Carver
|
36
|
-
- Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] @navy @science
|
37
|
-
- Marie Curie [Atlantis] @science
|
38
|
-
FILE
|
39
|
-
end
|
40
|
-
|
41
|
-
it "lists friends in order of decreasing activity" do
|
42
|
-
stdout_only <<-OUTPUT
|
43
|
-
Your favorite friends:
|
44
|
-
1. Grace Hopper (3 activities)
|
45
|
-
2. George Washington Carver (2)
|
46
|
-
3. Marie Curie (1)
|
47
|
-
OUTPUT
|
48
|
-
end
|
49
|
-
|
50
|
-
describe "when friends are tied for the same number of activities" do
|
51
|
-
let(:content) do
|
52
|
-
<<-FILE
|
53
|
-
### Activities:
|
54
|
-
- 2017-01-01: Did something with **Friend A**.
|
55
|
-
- 2017-01-01: Did something with **Friend A**.
|
56
|
-
- 2017-01-01: Did something with **Friend B**.
|
57
|
-
- 2017-01-01: Did something with **Friend B**.
|
58
|
-
- 2017-01-01: Did something with **Friend C**.
|
59
|
-
- 2017-01-01: Did something with **Friend D**.
|
60
|
-
- 2017-01-01: Did something with **Friend E**.
|
61
|
-
- 2017-01-01: Did something with **Friend F**.
|
62
|
-
- 2017-01-01: Did something with **Friend G**.
|
63
|
-
- 2017-01-01: Did something with **Friend H**.
|
64
|
-
- 2017-01-01: Did something with **Friend I**.
|
65
|
-
- 2017-01-01: Did something with **Friend J**.
|
66
|
-
|
67
|
-
### Friends:
|
68
|
-
- Friend A
|
69
|
-
- Friend B
|
70
|
-
- Friend C
|
71
|
-
- Friend D
|
72
|
-
- Friend E
|
73
|
-
- Friend F
|
74
|
-
- Friend G
|
75
|
-
- Friend H
|
76
|
-
- Friend I
|
77
|
-
- Friend J
|
78
|
-
FILE
|
79
|
-
end
|
80
|
-
|
81
|
-
it "uses tied ranks" do
|
82
|
-
value(subject[:stderr]).must_equal ""
|
83
|
-
value(subject[:status]).must_equal 0
|
84
|
-
|
85
|
-
lines = subject[:stdout].split("\n")
|
86
|
-
value(lines[1]).must_match(/1\. Friend (A|B)/)
|
87
|
-
value(lines[2]).must_match(/1\. Friend (A|B)/)
|
88
|
-
value(lines[3]).must_include "3. Friend"
|
89
|
-
end
|
90
|
-
|
91
|
-
it "only uses the word 'activities' for the first item, even when a tie" do
|
92
|
-
value(subject[:stderr]).must_equal ""
|
93
|
-
value(subject[:status]).must_equal 0
|
94
|
-
|
95
|
-
lines = subject[:stdout].split("\n")
|
96
|
-
value(lines[1]).must_include "activities"
|
97
|
-
value(lines[2]).wont_include "activities"
|
98
|
-
end
|
99
|
-
|
100
|
-
it "indents based on the highest rank number, not the number of friends" do
|
101
|
-
value(subject[:stderr]).must_equal ""
|
102
|
-
value(subject[:status]).must_equal 0
|
103
|
-
|
104
|
-
# Since there are 10 friends, a naive implementation would pad our output
|
105
|
-
# assuming the (numerically) highest rank is "10." but since the highest
|
106
|
-
# rank is a tie, we never display a double-digit rank, so we don't need to
|
107
|
-
# pad our output for double digits.
|
108
|
-
lines = subject[:stdout].split("\n")
|
109
|
-
value(lines.last).must_include "3. Friend"
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
@@ -1,149 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "./test/helper"
|
4
|
-
|
5
|
-
clean_describe "list favorite locations" do
|
6
|
-
subject { run_cmd("list favorite locations") }
|
7
|
-
|
8
|
-
describe "when file does not exist" do
|
9
|
-
let(:content) { nil }
|
10
|
-
|
11
|
-
it "prints a no-data message" do
|
12
|
-
stdout_only "Your favorite locations:"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
describe "when file is empty" do
|
17
|
-
let(:content) { "" }
|
18
|
-
|
19
|
-
it "prints a no-data message" do
|
20
|
-
stdout_only "Your favorite locations:"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe "when file has content" do
|
25
|
-
let(:content) do
|
26
|
-
<<-FILE
|
27
|
-
### Activities:
|
28
|
-
- 2017-01-01: **Grace Hopper** and I went to _Martha's Vineyard_ for breakfast.
|
29
|
-
- 2015-11-01: **Grace Hopper** and I went to _Martha's Vineyard_. George had to cancel at the last minute.
|
30
|
-
- 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**. @food
|
31
|
-
- 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. @partying
|
32
|
-
- 2014-11-15: Talked to **George Washington Carver** on the phone for an hour.
|
33
|
-
|
34
|
-
### Friends:
|
35
|
-
- George Washington Carver
|
36
|
-
- Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] @navy @science
|
37
|
-
- Marie Curie [Atlantis] @science
|
38
|
-
|
39
|
-
### Locations:
|
40
|
-
- Atlantis
|
41
|
-
- Martha's Vineyard
|
42
|
-
- Paris
|
43
|
-
FILE
|
44
|
-
end
|
45
|
-
|
46
|
-
it "lists locations in order of decreasing activity" do
|
47
|
-
stdout_only <<-OUTPUT
|
48
|
-
Your favorite locations:
|
49
|
-
1. Martha's Vineyard (2 activities)
|
50
|
-
2. Paris (1)
|
51
|
-
3. Atlantis (0)
|
52
|
-
OUTPUT
|
53
|
-
end
|
54
|
-
|
55
|
-
describe "when locations are tied for the same number of activities" do
|
56
|
-
let(:content) do
|
57
|
-
<<-FILE
|
58
|
-
### Activities:
|
59
|
-
- 2017-01-01: Did something in _Location A_.
|
60
|
-
- 2017-01-01: Did something in _Location A_.
|
61
|
-
- 2017-01-01: Did something in _Location B_.
|
62
|
-
- 2017-01-01: Did something in _Location B_.
|
63
|
-
- 2017-01-01: Did something in _Location C_.
|
64
|
-
- 2017-01-01: Did something in _Location D_.
|
65
|
-
- 2017-01-01: Did something in _Location E_.
|
66
|
-
- 2017-01-01: Did something in _Location F_.
|
67
|
-
- 2017-01-01: Did something in _Location G_.
|
68
|
-
- 2017-01-01: Did something in _Location H_.
|
69
|
-
- 2017-01-01: Did something in _Location I_.
|
70
|
-
- 2017-01-01: Did something in _Location J_.
|
71
|
-
|
72
|
-
### Locations:
|
73
|
-
- Location A
|
74
|
-
- Location B
|
75
|
-
- Location C
|
76
|
-
- Location D
|
77
|
-
- Location E
|
78
|
-
- Location F
|
79
|
-
- Location G
|
80
|
-
- Location H
|
81
|
-
- Location I
|
82
|
-
- Location J
|
83
|
-
FILE
|
84
|
-
end
|
85
|
-
|
86
|
-
it "uses tied ranks" do
|
87
|
-
value(subject[:stderr]).must_equal ""
|
88
|
-
value(subject[:status]).must_equal 0
|
89
|
-
|
90
|
-
lines = subject[:stdout].split("\n")
|
91
|
-
value(lines[1]).must_match(/1\. Location (A|B)/)
|
92
|
-
value(lines[2]).must_match(/1\. Location (A|B)/)
|
93
|
-
value(lines[3]).must_include "3. Location"
|
94
|
-
end
|
95
|
-
|
96
|
-
it "only uses the word 'activities' for the first item, even when a tie" do
|
97
|
-
value(subject[:stderr]).must_equal ""
|
98
|
-
value(subject[:status]).must_equal 0
|
99
|
-
|
100
|
-
lines = subject[:stdout].split("\n")
|
101
|
-
value(lines[1]).must_include "activities"
|
102
|
-
value(lines[2]).wont_include "activities"
|
103
|
-
end
|
104
|
-
|
105
|
-
it "indents based on the highest rank number, not the number of locations" do
|
106
|
-
value(subject[:stderr]).must_equal ""
|
107
|
-
value(subject[:status]).must_equal 0
|
108
|
-
|
109
|
-
# Since there are 10 friends, a naive implementation would pad our output
|
110
|
-
# assuming the (numerically) highest rank is "10." but since the highest
|
111
|
-
# rank is a tie, we never display a double-digit rank, so we don't need to
|
112
|
-
# pad our output for double digits.
|
113
|
-
lines = subject[:stdout].split("\n")
|
114
|
-
value(lines.last).must_include "3. Location"
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
describe "when implied locations are set" do
|
119
|
-
let(:content) do
|
120
|
-
<<-FILE
|
121
|
-
### Activities:
|
122
|
-
- 2015-01-30: Went to a museum with **George Washington Carver**.
|
123
|
-
- 2015-01-29: Moved to _Paris_.
|
124
|
-
- 2015-01-01: Got lunch with **Grace Hopper** and **George Washington Carver**. @food
|
125
|
-
- 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. @partying @food
|
126
|
-
- 2014-12-30: Went to _Atlantis_.
|
127
|
-
- 2014-12-29: Talked to **George Washington Carver** on the phone for an hour.
|
128
|
-
|
129
|
-
### Friends:
|
130
|
-
- George Washington Carver
|
131
|
-
- Marie Curie [Atlantis] @science
|
132
|
-
- Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] @navy @science
|
133
|
-
|
134
|
-
### Locations:
|
135
|
-
- Atlantis
|
136
|
-
- Paris
|
137
|
-
FILE
|
138
|
-
end
|
139
|
-
|
140
|
-
it "lists locations in order of decreasing activity" do
|
141
|
-
stdout_only <<-OUTPUT
|
142
|
-
Your favorite locations:
|
143
|
-
1. Paris (3 activities)
|
144
|
-
2. Atlantis (2)
|
145
|
-
OUTPUT
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|