friends 0.54 → 0.55
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/friends.svg)](https://badge.fury.io/rb/friends)
|
2
2
|
[![Code Coverage](https://codecov.io/gh/JacobEvelyn/friends/branch/main/graph/badge.svg)](https://codecov.io/gh/JacobEvelyn/friends)
|
3
|
-
[![
|
3
|
+
[![Tests](https://github.com/JacobEvelyn/friends/workflows/Main/badge.svg)](https://github.com/JacobEvelyn/friends/actions?query=workflow%3AMain)
|
4
4
|
[![Readme Score](http://readme-score-api.herokuapp.com/score.svg?url=JacobEvelyn/friends&bust=1)](http://clayallsopp.github.io/readme-score?url=JacobEvelyn/friends)
|
5
5
|
[![Inline docs](http://inch-ci.org/github/JacobEvelyn/friends.png)](http://inch-ci.org/github/JacobEvelyn/friends)
|
6
6
|
[![Gem](https://img.shields.io/gem/dt/friends.svg)](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
|