friends 0.48 → 0.53
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CONTRIBUTING.md +1 -1
- data/.rubocop.yml +20 -5
- data/.travis.yml +6 -6
- data/CHANGELOG.md +75 -2
- data/Gemfile +1 -1
- data/README.md +6 -6
- data/RELEASING.md +1 -1
- data/bin/friends +3 -7
- data/friends.gemspec +3 -5
- data/lib/friends/commands/add.rb +2 -2
- data/lib/friends/commands/edit.rb +1 -1
- data/lib/friends/commands/update.rb +2 -2
- data/lib/friends/event.rb +2 -2
- data/lib/friends/friend.rb +20 -13
- data/lib/friends/graph.rb +1 -1
- data/lib/friends/introvert.rb +9 -7
- data/lib/friends/location.rb +1 -1
- data/lib/friends/post_install_message.rb +1 -2
- data/lib/friends/regex_builder.rb +8 -8
- data/lib/friends/sem_ver_comparator.rb +20 -0
- data/lib/friends/serializable.rb +1 -1
- data/lib/friends/version.rb +1 -1
- data/test/add_event_helper.rb +56 -0
- data/test/commands/add/nickname_spec.rb +9 -0
- data/test/commands/add/tag_spec.rb +9 -0
- data/test/commands/edit_spec.rb +31 -0
- data/test/commands/remove/tag_spec.rb +9 -0
- data/test/editor +2 -0
- data/test/helper.rb +3 -3
- metadata +6 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1be607e71eb696b1ebf8c6e46b1b561f153288ecede0c429fc93facd82a4b392
|
4
|
+
data.tar.gz: 2ea6de005359014425d32386306c0654eaa8349be0ebf19c957da3582a8c33f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e946dd33cd26ee1fdbe288cc2a2be9f0eb3eaae6bede1e47096c8b42d2af5da753b7d151b29fd75aa3aac5958d717a7141cac7e108e949961cd000e6af5f3451
|
7
|
+
data.tar.gz: 3c8e1a84fec855f17d0177de3cadd52a31bdfe51255bf77dd70dd9188be59871eec15d734e6f0b269183e330e15734653679f821d322407c6a87ca8d3f1a3b8f
|
data/.github/CONTRIBUTING.md
CHANGED
@@ -55,7 +55,7 @@ promise I don't bite. 😊
|
|
55
55
|
|
56
56
|
## Code of Conduct
|
57
57
|
|
58
|
-
Note that this project follows a [Code of Conduct](https://github.com/JacobEvelyn/friends/blob/
|
58
|
+
Note that this project follows a [Code of Conduct](https://github.com/JacobEvelyn/friends/blob/main/CODE_OF_CONDUCT.md).
|
59
59
|
If you're a polite, reasonable person you won't have any issues!
|
60
60
|
|
61
61
|
## Financial contributions
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
-
TargetRubyVersion: 2.
|
2
|
+
TargetRubyVersion: 2.3
|
3
3
|
|
4
4
|
Gemspec/RequiredRubyVersion:
|
5
5
|
Enabled: false
|
@@ -10,9 +10,18 @@ Layout/ClosingHeredocIndentation:
|
|
10
10
|
Layout/DotPosition:
|
11
11
|
EnforcedStyle: trailing
|
12
12
|
|
13
|
-
Layout/
|
13
|
+
Layout/HeredocIndentation:
|
14
14
|
Enabled: false
|
15
15
|
|
16
|
+
Layout/LineLength:
|
17
|
+
Max: 100
|
18
|
+
|
19
|
+
Lint/RaiseException:
|
20
|
+
Enabled: true
|
21
|
+
|
22
|
+
Lint/StructNewOverride:
|
23
|
+
Enabled: true
|
24
|
+
|
16
25
|
Metrics/AbcSize:
|
17
26
|
Enabled: false
|
18
27
|
|
@@ -28,9 +37,6 @@ Metrics/ClassLength:
|
|
28
37
|
Metrics/CyclomaticComplexity:
|
29
38
|
Enabled: false
|
30
39
|
|
31
|
-
Metrics/LineLength:
|
32
|
-
Max: 100
|
33
|
-
|
34
40
|
Metrics/MethodLength:
|
35
41
|
Enabled: false
|
36
42
|
|
@@ -46,6 +52,15 @@ Naming/AccessorMethodName:
|
|
46
52
|
Style/Documentation:
|
47
53
|
Enabled: false
|
48
54
|
|
55
|
+
Style/HashEachMethods:
|
56
|
+
Enabled: true
|
57
|
+
|
58
|
+
Style/HashTransformKeys:
|
59
|
+
Enabled: true
|
60
|
+
|
61
|
+
Style/HashTransformValues:
|
62
|
+
Enabled: true
|
63
|
+
|
49
64
|
Style/MultilineBlockChain:
|
50
65
|
Enabled: false
|
51
66
|
|
data/.travis.yml
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
language: ruby
|
2
|
+
cache: bundler
|
2
3
|
rvm:
|
3
|
-
- 2.1
|
4
|
-
- 2.2
|
5
4
|
- 2.3
|
6
5
|
- 2.4
|
7
|
-
- 2.5
|
6
|
+
- 2.5
|
7
|
+
- 2.6 # 2.7 is tested below
|
8
8
|
gemfile: Gemfile.old # The latest Ruby version uses Gemfile below
|
9
9
|
script:
|
10
10
|
- bundle exec rake test
|
11
11
|
matrix:
|
12
12
|
include:
|
13
|
-
- rvm: 2.
|
13
|
+
- rvm: 2.7
|
14
14
|
gemfile: Gemfile
|
15
15
|
script:
|
16
16
|
- bundle exec rake test
|
17
|
-
-
|
17
|
+
- bundle exec rubocop
|
18
18
|
env:
|
19
19
|
- CODE_COVERAGE=true
|
20
20
|
branches:
|
21
21
|
only:
|
22
|
-
-
|
22
|
+
- main # Always run on the main branch
|
23
23
|
notifications:
|
24
24
|
email: false
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,79 @@
|
|
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.53](https://github.com/JacobEvelyn/friends/tree/v0.53) (2020-07-06)
|
8
|
+
|
9
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.52...v0.53)
|
10
|
+
|
11
|
+
**Fixed bugs:**
|
12
|
+
|
13
|
+
- undefined method error occurred when “add tag" or “add nickname” executed with no arguments [\#265](https://github.com/JacobEvelyn/friends/issues/265)
|
14
|
+
|
15
|
+
**Merged pull requests:**
|
16
|
+
|
17
|
+
- Fix for the case of add tag or add nickname args are nill [\#266](https://github.com/JacobEvelyn/friends/pull/266) ([m-t-a-n-a-k-a](https://github.com/m-t-a-n-a-k-a))
|
18
|
+
|
19
|
+
## [v0.52](https://github.com/JacobEvelyn/friends/tree/v0.52) (2020-06-03)
|
20
|
+
|
21
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.51...v0.52)
|
22
|
+
|
23
|
+
**Fixed bugs:**
|
24
|
+
|
25
|
+
- undefined method error occurred when "remove tag" is executed with no arguments [\#262](https://github.com/JacobEvelyn/friends/issues/262)
|
26
|
+
|
27
|
+
**Closed issues:**
|
28
|
+
|
29
|
+
- Try using bundler caching in Travis [\#260](https://github.com/JacobEvelyn/friends/issues/260)
|
30
|
+
|
31
|
+
**Merged pull requests:**
|
32
|
+
|
33
|
+
- Use correct RuboCop version in Travis [\#264](https://github.com/JacobEvelyn/friends/pull/264) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
34
|
+
- Fix convert\_to\_tag for the case of str is nil [\#263](https://github.com/JacobEvelyn/friends/pull/263) ([m-t-a-n-a-k-a](https://github.com/m-t-a-n-a-k-a))
|
35
|
+
- Cache bundler directory in Travis [\#261](https://github.com/JacobEvelyn/friends/pull/261) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
36
|
+
- Update rubocop requirement from 0.67 to 0.81.0 [\#259](https://github.com/JacobEvelyn/friends/pull/259) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
37
|
+
|
38
|
+
## [v0.51](https://github.com/JacobEvelyn/friends/tree/v0.51) (2020-04-05)
|
39
|
+
|
40
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.50...v0.51)
|
41
|
+
|
42
|
+
**Implemented enhancements:**
|
43
|
+
|
44
|
+
- Drop Semverse dependency [\#256](https://github.com/JacobEvelyn/friends/issues/256)
|
45
|
+
|
46
|
+
**Closed issues:**
|
47
|
+
|
48
|
+
- Drop support for Ruby \<2.3 [\#257](https://github.com/JacobEvelyn/friends/issues/257)
|
49
|
+
|
50
|
+
**Merged pull requests:**
|
51
|
+
|
52
|
+
- Remove Semverse dependency, and require Ruby 2.3+ [\#258](https://github.com/JacobEvelyn/friends/pull/258) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
53
|
+
|
54
|
+
## [v0.50](https://github.com/JacobEvelyn/friends/tree/v0.50) (2020-04-03)
|
55
|
+
|
56
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.49...v0.50)
|
57
|
+
|
58
|
+
**Implemented enhancements:**
|
59
|
+
|
60
|
+
- Add support for Ruby 2.7 [\#254](https://github.com/JacobEvelyn/friends/issues/254)
|
61
|
+
|
62
|
+
**Merged pull requests:**
|
63
|
+
|
64
|
+
- Add Travis tests for Ruby 2.7 [\#255](https://github.com/JacobEvelyn/friends/pull/255) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
65
|
+
|
66
|
+
## [v0.49](https://github.com/JacobEvelyn/friends/tree/v0.49) (2020-04-02)
|
67
|
+
|
68
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.48...v0.49)
|
69
|
+
|
70
|
+
**Fixed bugs:**
|
71
|
+
|
72
|
+
- Multi-word editors no longer work with `friends edit` [\#251](https://github.com/JacobEvelyn/friends/issues/251)
|
73
|
+
- Punctuation swallowed after friend name with last initial [\#235](https://github.com/JacobEvelyn/friends/issues/235)
|
74
|
+
|
75
|
+
**Merged pull requests:**
|
76
|
+
|
77
|
+
- Improve name matching to not swallow punctuation [\#253](https://github.com/JacobEvelyn/friends/pull/253) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
78
|
+
- Fix `friends edit` for multi-word EDITORs [\#252](https://github.com/JacobEvelyn/friends/pull/252) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
79
|
+
|
7
80
|
## [v0.48](https://github.com/JacobEvelyn/friends/tree/v0.48) (2020-03-27)
|
8
81
|
|
9
82
|
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.47...v0.48)
|
@@ -31,7 +104,7 @@ show you appreciate its continued development.
|
|
31
104
|
|
32
105
|
**Fixed bugs:**
|
33
106
|
|
34
|
-
- Tests are failing in `
|
107
|
+
- Tests are failing in `main` [\#238](https://github.com/JacobEvelyn/friends/issues/238)
|
35
108
|
|
36
109
|
**Merged pull requests:**
|
37
110
|
|
@@ -584,7 +657,7 @@ show you appreciate its continued development.
|
|
584
657
|
**Merged pull requests:**
|
585
658
|
|
586
659
|
- Highlight multiple occurrences [\#70](https://github.com/JacobEvelyn/friends/pull/70) ([GuruKhalsa](https://github.com/GuruKhalsa))
|
587
|
-
- Fix Travis badge \(
|
660
|
+
- Fix Travis badge \(main only\) [\#67](https://github.com/JacobEvelyn/friends/pull/67) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
588
661
|
- Move activity prompt to bin/friends [\#64](https://github.com/JacobEvelyn/friends/pull/64) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
589
662
|
- Adds the --debug flag for printing backtraces on error [\#63](https://github.com/JacobEvelyn/friends/pull/63) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
590
663
|
- Move file writes to end of command actions [\#61](https://github.com/JacobEvelyn/friends/pull/61) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
data/Gemfile
CHANGED
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
|
-
[![Code Coverage](https://codecov.io/gh/JacobEvelyn/friends/branch/
|
3
|
-
[![Build Status](https://travis-ci.com/JacobEvelyn/friends.svg?branch=
|
2
|
+
[![Code Coverage](https://codecov.io/gh/JacobEvelyn/friends/branch/main/graph/badge.svg)](https://codecov.io/gh/JacobEvelyn/friends)
|
3
|
+
[![Build Status](https://travis-ci.com/JacobEvelyn/friends.svg?branch=main)](https://travis-ci.com/JacobEvelyn/friends)
|
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)
|
@@ -181,7 +181,7 @@ The `friends.md` Markdown file that stores all of your data contains:
|
|
181
181
|
```
|
182
182
|
|
183
183
|
See the example
|
184
|
-
[`friends.md`](https://github.com/JacobEvelyn/friends/blob/
|
184
|
+
[`friends.md`](https://github.com/JacobEvelyn/friends/blob/main/friends.md)
|
185
185
|
file for more information.
|
186
186
|
|
187
187
|
### Global flags
|
@@ -956,7 +956,7 @@ If you have an idea,
|
|
956
956
|
[make a GitHub issue](https://github.com/JacobEvelyn/friends/issues/new)!
|
957
957
|
Suggestions are very very welcome, and usually are implemented very
|
958
958
|
quickly. And if you'd like to do the implementing yourself, see the
|
959
|
-
[contributing guide](https://github.com/JacobEvelyn/friends/blob/
|
959
|
+
[contributing guide](https://github.com/JacobEvelyn/friends/blob/main/.github/CONTRIBUTING.md).
|
960
960
|
|
961
961
|
A big big thanks to all of this project's lovely
|
962
962
|
[contributors](https://github.com/JacobEvelyn/friends/graphs/contributors):
|
@@ -988,10 +988,10 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
|
|
988
988
|
|
989
989
|
## Code of Conduct
|
990
990
|
|
991
|
-
Note that this project follows a [Code of Conduct](https://github.com/JacobEvelyn/friends/blob/
|
991
|
+
Note that this project follows a [Code of Conduct](https://github.com/JacobEvelyn/friends/blob/main/CODE_OF_CONDUCT.md).
|
992
992
|
If you're a polite, reasonable person you won't have any issues!
|
993
993
|
|
994
994
|
## License
|
995
995
|
|
996
996
|
Friends is released under the
|
997
|
-
[MIT License](https://github.com/JacobEvelyn/friends/blob/
|
997
|
+
[MIT License](https://github.com/JacobEvelyn/friends/blob/main/LICENSE.txt).
|
data/RELEASING.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
These are steps for the maintainer to take to release a new version of this gem.
|
4
4
|
|
5
|
-
1. On the `
|
5
|
+
1. On the `main` branch, update the `VERSION` constant in
|
6
6
|
`lib/friends/version.rb`.
|
7
7
|
2. Commit the change (`git add -A && git commit -m 'Bump to vX.X'`).
|
8
8
|
3. Add a tag (`git tag -am "vX.X" vX.X`).
|
data/bin/friends
CHANGED
@@ -12,7 +12,6 @@ end
|
|
12
12
|
require "gli"
|
13
13
|
require "paint"
|
14
14
|
require "readline"
|
15
|
-
require "semverse"
|
16
15
|
|
17
16
|
require "friends/introvert"
|
18
17
|
require "friends/version"
|
@@ -35,7 +34,7 @@ class Tag
|
|
35
34
|
# conversions for arguments.
|
36
35
|
# See: https://github.com/davetron5000/gli/issues/241
|
37
36
|
def self.convert_to_tag(str)
|
38
|
-
str = str.strip
|
37
|
+
str = str.to_s.strip
|
39
38
|
!str.empty? && str[0] == "@" ? str : "@#{str}"
|
40
39
|
end
|
41
40
|
end
|
@@ -48,10 +47,7 @@ class Stripped; end
|
|
48
47
|
accept(Stripped, &:strip)
|
49
48
|
|
50
49
|
class InputDate; end
|
51
|
-
accept(InputDate)
|
52
|
-
time = Chronic.parse(value)
|
53
|
-
time && time.to_date
|
54
|
-
end
|
50
|
+
accept(InputDate) { |value| Chronic.parse(value)&.to_date }
|
55
51
|
|
56
52
|
switch [:quiet],
|
57
53
|
negatable: false,
|
@@ -102,7 +98,7 @@ post do
|
|
102
98
|
rescue Errno::ENOENT
|
103
99
|
# If the pager is not installed, just print the output.
|
104
100
|
puts @introvert.output
|
105
|
-
rescue Errno::EPIPE
|
101
|
+
rescue Errno::EPIPE
|
106
102
|
# Occurs when quitting the pager.
|
107
103
|
end
|
108
104
|
end
|
data/friends.gemspec
CHANGED
@@ -23,17 +23,15 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
|
26
|
-
# We need Ruby 2.
|
27
|
-
|
28
|
-
spec.required_ruby_version = ">= 2.1"
|
26
|
+
# We need Ruby 2.3's safe navigation operator.
|
27
|
+
spec.required_ruby_version = ">= 2.3"
|
29
28
|
|
30
29
|
spec.add_dependency "chronic", "~> 0.10"
|
31
30
|
spec.add_dependency "gli", "~> 2.14"
|
32
31
|
spec.add_dependency "paint", "~> 2.0"
|
33
|
-
spec.add_dependency "semverse", ">= 2", "< 4"
|
34
32
|
spec.add_dependency "tty-pager", "~> 0.11"
|
35
33
|
|
36
34
|
spec.add_development_dependency "minitest", "~> 5.5"
|
37
35
|
spec.add_development_dependency "minitest-proveit", "~> 1.0"
|
38
|
-
spec.add_development_dependency "rake", "~>
|
36
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
39
37
|
end
|
data/lib/friends/commands/add.rb
CHANGED
@@ -35,7 +35,7 @@ command :add do |add|
|
|
35
35
|
add.arg_name "NAME NICKNAME"
|
36
36
|
add.command :nickname do |add_nickname|
|
37
37
|
add_nickname.action do |_, _, args|
|
38
|
-
@introvert.add_nickname(name: args.first.strip, nickname: args[1].strip)
|
38
|
+
@introvert.add_nickname(name: args.first.to_s.strip, nickname: args[1].to_s.strip)
|
39
39
|
@dirty = true # Mark the file for cleaning.
|
40
40
|
end
|
41
41
|
end
|
@@ -46,7 +46,7 @@ command :add do |add|
|
|
46
46
|
add_tag.action do |_, _, args|
|
47
47
|
@introvert.add_tag(
|
48
48
|
name: args[0..-2].join(" "),
|
49
|
-
tag: Tag.convert_to_tag(args.last.strip)
|
49
|
+
tag: Tag.convert_to_tag(args.last.to_s.strip)
|
50
50
|
)
|
51
51
|
@dirty = true # Mark the file for cleaning.
|
52
52
|
end
|
@@ -9,7 +9,7 @@ command :edit do |edit|
|
|
9
9
|
puts "Opening \"#{filename}\" with \"#{editor}\"" unless global_options[:quiet]
|
10
10
|
|
11
11
|
# Mark the file for cleaning once the editor was closed correctly.
|
12
|
-
if Kernel.system(editor
|
12
|
+
if Kernel.system("#{editor} #{filename}")
|
13
13
|
@introvert = Friends::Introvert.new(filename: filename)
|
14
14
|
@clean_command = true
|
15
15
|
@dirty = true
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "friends/post_install_message"
|
4
|
+
require "friends/sem_ver_comparator"
|
4
5
|
|
5
6
|
desc "Updates the `friends` program"
|
6
7
|
command :update do |update|
|
@@ -9,8 +10,7 @@ command :update do |update|
|
|
9
10
|
if match = `gem search friends`.match(/^friends\s\(([^\)]+)\)$/)
|
10
11
|
# rubocop:enable Lint/AssignmentInCondition
|
11
12
|
remote_version = match[1]
|
12
|
-
if
|
13
|
-
Semverse::Version.coerce(Friends::VERSION)
|
13
|
+
if Friends::SemVerComparator.greater?(remote_version, Friends::VERSION)
|
14
14
|
`gem update friends && gem cleanup friends`
|
15
15
|
|
16
16
|
unless global_options[:quiet]
|
data/lib/friends/event.rb
CHANGED
@@ -14,8 +14,8 @@ module Friends
|
|
14
14
|
class Event
|
15
15
|
extend Serializable
|
16
16
|
|
17
|
-
SERIALIZATION_PREFIX = "- "
|
18
|
-
DATE_PARTITION = ": "
|
17
|
+
SERIALIZATION_PREFIX = "- "
|
18
|
+
DATE_PARTITION = ": "
|
19
19
|
|
20
20
|
# @return [Regexp] the regex for capturing groups in deserialization
|
21
21
|
def self.deserialization_regex
|
data/lib/friends/friend.rb
CHANGED
@@ -10,15 +10,13 @@ module Friends
|
|
10
10
|
class Friend
|
11
11
|
extend Serializable
|
12
12
|
|
13
|
-
SERIALIZATION_PREFIX = "- "
|
14
|
-
NICKNAME_PREFIX = "a.k.a. "
|
13
|
+
SERIALIZATION_PREFIX = "- "
|
14
|
+
NICKNAME_PREFIX = "a.k.a. "
|
15
15
|
|
16
16
|
# @return [Regexp] the regex for capturing groups in deserialization
|
17
17
|
def self.deserialization_regex
|
18
18
|
# Note: this regex must be on one line because whitespace is important
|
19
|
-
# rubocop:disable
|
20
|
-
/(#{SERIALIZATION_PREFIX})?(?<name>[^\(\[@]*[^\(\[@\s])(\s+\(#{NICKNAME_PREFIX}(?<nickname_str>.+)\))?(\s+\[(?<location_name>[^\]]+)\])?(\s+(?<tags_str>(#{TAG_REGEX}\s*)+))?/
|
21
|
-
# rubocop:enable Metrics/LineLength
|
19
|
+
/(#{SERIALIZATION_PREFIX})?(?<name>[^\(\[@]*[^\(\[@\s])(\s+\(#{NICKNAME_PREFIX}(?<nickname_str>.+)\))?(\s+\[(?<location_name>[^\]]+)\])?(\s+(?<tags_str>(#{TAG_REGEX}\s*)+))?/ # rubocop:disable Layout/LineLength
|
22
20
|
end
|
23
21
|
|
24
22
|
# @return [Regexp] the string of what we expected during deserialization
|
@@ -34,11 +32,9 @@ module Friends
|
|
34
32
|
tags_str: nil
|
35
33
|
)
|
36
34
|
@name = name
|
37
|
-
@nicknames = nickname_str
|
38
|
-
nickname_str.split(" #{NICKNAME_PREFIX}") ||
|
39
|
-
[]
|
35
|
+
@nicknames = nickname_str&.split(" #{NICKNAME_PREFIX}") || []
|
40
36
|
@location_name = location_name
|
41
|
-
@tags = tags_str
|
37
|
+
@tags = tags_str&.split(/\s+/) || []
|
42
38
|
end
|
43
39
|
|
44
40
|
attr_accessor :name
|
@@ -134,12 +130,23 @@ module Friends
|
|
134
130
|
chunks, # Match a full name with the highest priority.
|
135
131
|
*@nicknames.map { |n| [n] },
|
136
132
|
|
137
|
-
# Match a first name followed by a last name initial, period
|
138
|
-
#
|
139
|
-
#
|
133
|
+
# Match a first name followed by a last name initial, period (that via
|
134
|
+
# lookahead is *NOT* a part of an ellipsis), and then (via lookahead)
|
135
|
+
# either:
|
136
|
+
# - other punctuation that would indicate we want to swallow the period
|
137
|
+
# (note that we do not include closing parentheses in this list because
|
138
|
+
# they could be part of an offset sentence), OR
|
139
|
+
# - anything, so long as the first alphabetical character afterwards is
|
140
|
+
# lowercase.
|
141
|
+
# This matches the "Jake E." part of something like "Jake E. and I went
|
142
|
+
# skiing." or "Jake E., Marie Curie, and I studied science." This
|
140
143
|
# allows us to correctly count the period as part of the name when it's
|
141
144
|
# in the middle of a sentence.
|
142
|
-
(
|
145
|
+
(
|
146
|
+
if chunks.size > 1
|
147
|
+
[chunks.first, "#{chunks.last[0]}\\.(?!\\.\\.)(?=([,!?;:—]+|(?-i)[^A-Z]+[a-z]))"]
|
148
|
+
end
|
149
|
+
),
|
143
150
|
|
144
151
|
# If the above doesn't match, we check for just the first name and then
|
145
152
|
# a last name initial. This matches the "Jake E" part of something like
|
data/lib/friends/graph.rb
CHANGED
data/lib/friends/introvert.rb
CHANGED
@@ -16,10 +16,10 @@ require "friends/friends_error"
|
|
16
16
|
|
17
17
|
module Friends
|
18
18
|
class Introvert
|
19
|
-
ACTIVITIES_HEADER = "### Activities:"
|
20
|
-
NOTES_HEADER = "### Notes:"
|
21
|
-
FRIENDS_HEADER = "### Friends:"
|
22
|
-
LOCATIONS_HEADER = "### Locations:"
|
19
|
+
ACTIVITIES_HEADER = "### Activities:"
|
20
|
+
NOTES_HEADER = "### Notes:"
|
21
|
+
FRIENDS_HEADER = "### Friends:"
|
22
|
+
LOCATIONS_HEADER = "### Locations:"
|
23
23
|
|
24
24
|
# @param filename [String] the name of the friends Markdown file
|
25
25
|
def initialize(filename:)
|
@@ -203,6 +203,7 @@ module Friends
|
|
203
203
|
# @param nickname [String] the nickname to add to the friend
|
204
204
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
205
205
|
def add_nickname(name:, nickname:)
|
206
|
+
raise FriendsError, "Expected \"[Friend Name]\" \"[Nickname]\"" if name.empty?
|
206
207
|
raise FriendsError, "Nickname cannot be blank" if nickname.empty?
|
207
208
|
|
208
209
|
friend = thing_with_name_in(:friend, name)
|
@@ -216,6 +217,7 @@ module Friends
|
|
216
217
|
# @param tag [String] the tag to add to the friend, of the form: "@tag"
|
217
218
|
# @raise [FriendsError] if 0 or 2+ friends match the given name
|
218
219
|
def add_tag(name:, tag:)
|
220
|
+
raise FriendsError, "Expected \"[Friend Name]\" \"[Tag]\"" if name.empty?
|
219
221
|
raise FriendsError, "Tag cannot be blank" if tag == "@"
|
220
222
|
|
221
223
|
friend = thing_with_name_in(:friend, name)
|
@@ -725,8 +727,8 @@ module Friends
|
|
725
727
|
|
726
728
|
begin
|
727
729
|
instance_variable_get("@#{stage.id}") << stage.klass.deserialize(line)
|
728
|
-
rescue StandardError =>
|
729
|
-
bad_line(
|
730
|
+
rescue StandardError => e
|
731
|
+
bad_line(e, line_num)
|
730
732
|
end
|
731
733
|
|
732
734
|
state
|
@@ -808,7 +810,7 @@ module Friends
|
|
808
810
|
a.default_location && a.default_location != activity.default_location
|
809
811
|
end
|
810
812
|
|
811
|
-
str += " to #{Paint[
|
813
|
+
str += " to #{Paint[later_activity&.date || 'present', :bold]}"
|
812
814
|
end
|
813
815
|
|
814
816
|
str += " already" if earlier_activity_with_default_location != activity
|
data/lib/friends/location.rb
CHANGED
@@ -3,6 +3,5 @@
|
|
3
3
|
module Friends
|
4
4
|
POST_INSTALL_MESSAGE = "\nfriends is a volunteer project. If you find it useful, please "\
|
5
5
|
"consider making a small donation:\n\n\t"\
|
6
|
-
"https://github.com/JacobEvelyn/friends#contributing-its-encouraged\n\n"
|
7
|
-
freeze
|
6
|
+
"https://github.com/JacobEvelyn/friends#contributing-its-encouraged\n\n"
|
8
7
|
end
|
@@ -6,26 +6,26 @@ module Friends
|
|
6
6
|
class RegexBuilder
|
7
7
|
# We don't want to match strings that are "escaped" with a leading
|
8
8
|
# backslash.
|
9
|
-
NO_LEADING_BACKSLASH = "(?<!\\\\)"
|
9
|
+
NO_LEADING_BACKSLASH = "(?<!\\\\)"
|
10
10
|
|
11
11
|
# We don't want to match strings that are directly touching double asterisks
|
12
12
|
# as these are treated as sacred by our program.
|
13
|
-
NO_LEADING_ASTERISKS = "(?<!\\*\\*)"
|
14
|
-
NO_TRAILING_ASTERISKS = "(?!\\*\\*)"
|
13
|
+
NO_LEADING_ASTERISKS = "(?<!\\*\\*)"
|
14
|
+
NO_TRAILING_ASTERISKS = "(?!\\*\\*)"
|
15
15
|
|
16
16
|
# We don't want to match strings that are directly touching underscores
|
17
17
|
# as these are treated as sacred by our program.
|
18
|
-
NO_LEADING_UNDERSCORES = "(?<!_)"
|
19
|
-
NO_TRAILING_UNDERSCORES = "(?!_)"
|
18
|
+
NO_LEADING_UNDERSCORES = "(?<!_)"
|
19
|
+
NO_TRAILING_UNDERSCORES = "(?!_)"
|
20
20
|
|
21
21
|
# We don't want to match strings that are part of other words.
|
22
|
-
NO_LEADING_ALPHABETICALS = "(?<![A-z])"
|
23
|
-
NO_TRAILING_ALPHABETICALS = "(?![A-z])"
|
22
|
+
NO_LEADING_ALPHABETICALS = "(?<![A-z])"
|
23
|
+
NO_TRAILING_ALPHABETICALS = "(?![A-z])"
|
24
24
|
|
25
25
|
# We ignore case within the regex as opposed to globally to allow consumers
|
26
26
|
# of this API the ability to pass in strings that turn off this modifier
|
27
27
|
# with the "(?-i)" string.
|
28
|
-
IGNORE_CASE = "(?i)"
|
28
|
+
IGNORE_CASE = "(?i)"
|
29
29
|
|
30
30
|
def self.regex(str)
|
31
31
|
Regexp.new(
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Friends
|
4
|
+
module SemVerComparator
|
5
|
+
SEPARATOR = "."
|
6
|
+
NUMBER_REGEX = /\d+/.freeze
|
7
|
+
|
8
|
+
def self.greater?(version_a, version_b)
|
9
|
+
version_a.split(SEPARATOR).zip(version_b.split(SEPARATOR)) do |a, b|
|
10
|
+
a_num = a&.[](NUMBER_REGEX)&.to_i
|
11
|
+
b_num = b&.[](NUMBER_REGEX)&.to_i
|
12
|
+
return false if a_num.nil?
|
13
|
+
return true if b_num.nil? || a_num > b_num
|
14
|
+
return false if a_num < b_num
|
15
|
+
end
|
16
|
+
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/friends/serializable.rb
CHANGED
data/lib/friends/version.rb
CHANGED
data/test/add_event_helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
def date_parsing_specs(test_stdout: true)
|
2
4
|
describe "date parsing" do
|
3
5
|
let(:description) { "Test." }
|
@@ -117,6 +119,60 @@ def description_parsing_specs(test_stdout: true)
|
|
117
119
|
it { stdout_only "#{capitalized_event} added: \"#{date}: Met Grace Hopper at 12.\"" }
|
118
120
|
end
|
119
121
|
end
|
122
|
+
|
123
|
+
describe "when followed by a period and a comma" do
|
124
|
+
let(:description) { "Met grace h., and others, at 12." }
|
125
|
+
|
126
|
+
it { line_added "- #{date}: Met **Grace Hopper**, and others, at 12." }
|
127
|
+
if test_stdout
|
128
|
+
it { stdout_only "#{capitalized_event} added: \"#{date}: Met Grace Hopper, and others, at 12.\"" } # rubocop:disable Layout/LineLength
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "when followed by a period, a comma, and a proper noun" do
|
133
|
+
let(:description) { "Met grace h., King James, and others at 12." }
|
134
|
+
|
135
|
+
it { line_added "- #{date}: Met **Grace Hopper**, King James, and others at 12." }
|
136
|
+
if test_stdout
|
137
|
+
it { stdout_only "#{capitalized_event} added: \"#{date}: Met Grace Hopper, King James, and others at 12.\"" } # rubocop:disable Layout/LineLength
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "when followed by a period and a complex series of sentence-ending punctuation" do
|
142
|
+
let(:description) { "Met someone—grace h.?! At 12." }
|
143
|
+
|
144
|
+
it { line_added "- #{date}: Met someone—**Grace Hopper**?! At 12." }
|
145
|
+
if test_stdout
|
146
|
+
it { stdout_only "#{capitalized_event} added: \"#{date}: Met someone—Grace Hopper?! At 12.\"" } # rubocop:disable Layout/LineLength
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "when followed by a period and a complex series of mid-sentence punctuation" do
|
151
|
+
let(:description) { "Met someone {grace h.}—at 12." }
|
152
|
+
|
153
|
+
it { line_added "- #{date}: Met someone {**Grace Hopper**}—at 12." }
|
154
|
+
if test_stdout
|
155
|
+
it { stdout_only "#{capitalized_event} added: \"#{date}: Met someone {Grace Hopper}—at 12.\"" } # rubocop:disable Layout/LineLength
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "when followed by a period as part of a sentence-ending ellipsis" do
|
160
|
+
let(:description) { "Met grace h... Great!" }
|
161
|
+
|
162
|
+
it { line_added "- #{date}: Met **Grace Hopper**... Great!" }
|
163
|
+
if test_stdout
|
164
|
+
it { stdout_only "#{capitalized_event} added: \"#{date}: Met Grace Hopper... Great!\"" }
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "when followed by a period as part of a mid-sentence ellipsis" do
|
169
|
+
let(:description) { "Met grace h... at 12." }
|
170
|
+
|
171
|
+
it { line_added "- #{date}: Met **Grace Hopper**... at 12." }
|
172
|
+
if test_stdout
|
173
|
+
it { stdout_only "#{capitalized_event} added: \"#{date}: Met Grace Hopper... at 12.\"" }
|
174
|
+
end
|
175
|
+
end
|
120
176
|
end
|
121
177
|
|
122
178
|
describe "when description includes a friend's nickname (case insensitive)" do
|
@@ -7,6 +7,15 @@ clean_describe "add nickname" do
|
|
7
7
|
|
8
8
|
let(:content) { CONTENT }
|
9
9
|
|
10
|
+
describe "when friend name and nickname are blank" do
|
11
|
+
let(:friend_name) { nil }
|
12
|
+
let(:nickname) { nil }
|
13
|
+
|
14
|
+
it "prints an error message" do
|
15
|
+
stderr_only 'Error: Expected "[Friend Name]" "[Nickname]"'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
10
19
|
describe "when friend name has no matches" do
|
11
20
|
let(:friend_name) { "Garbage" }
|
12
21
|
let(:nickname) { "Georgie" }
|
@@ -7,6 +7,15 @@ clean_describe "add tag" do
|
|
7
7
|
|
8
8
|
let(:content) { CONTENT }
|
9
9
|
|
10
|
+
describe "when friend name and tag are blank" do
|
11
|
+
let(:friend_name) { nil }
|
12
|
+
let(:tag) { nil }
|
13
|
+
|
14
|
+
it "prints an error message" do
|
15
|
+
stderr_only 'Error: Expected "[Friend Name]" "[Tag]"'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
10
19
|
describe "when friend name has no matches" do
|
11
20
|
let(:friend_name) { "Garbage" }
|
12
21
|
let(:tag) { "school" }
|
data/test/commands/edit_spec.rb
CHANGED
@@ -36,6 +36,37 @@ clean_describe "edit" do
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
describe "when editor is multiple words" do
|
40
|
+
let(:editor) { "'cat -u'" }
|
41
|
+
|
42
|
+
describe "when output is quieted" do
|
43
|
+
let(:quiet) { "--quiet" }
|
44
|
+
|
45
|
+
it 'opens the file in the "editor"' do
|
46
|
+
stdout_only content
|
47
|
+
end
|
48
|
+
|
49
|
+
it "cleans the file" do
|
50
|
+
file_equals CONTENT # File is cleaned (no longer scrambled).
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "when output is not quieted" do
|
55
|
+
let(:quiet) { nil }
|
56
|
+
|
57
|
+
# Since our "editor" is just `cat -u`, our STDOUT output will include both the opening
|
58
|
+
# message and the contents of the file.
|
59
|
+
it 'prints a message and opens the file in the "editor"' do
|
60
|
+
stdout_only "Opening \"#{filename}\" with \"#{editor.tr("'", '')}\""\
|
61
|
+
"\n#{content}File cleaned: \"#{filename}\""
|
62
|
+
end
|
63
|
+
|
64
|
+
it "cleans the file" do
|
65
|
+
file_equals CONTENT # File is cleaned (no longer scrambled).
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
39
70
|
describe "when editor does not exit successfully" do
|
40
71
|
let(:editor) { "'exit 1 #'" }
|
41
72
|
|
@@ -7,6 +7,15 @@ clean_describe "remove tag" do
|
|
7
7
|
|
8
8
|
let(:content) { CONTENT }
|
9
9
|
|
10
|
+
describe "when friend name and tag are blank" do
|
11
|
+
let(:friend_name) { nil }
|
12
|
+
let(:tag) { nil }
|
13
|
+
|
14
|
+
it "prints an error message" do
|
15
|
+
stderr_only 'Error: No friend found for ""'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
10
19
|
describe "when friend name has no matches" do
|
11
20
|
let(:friend_name) { "Garbage" }
|
12
21
|
let(:tag) { "science" }
|
data/test/editor
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
3
5
|
# This is an example "editor" used to test the `friends edit` command.
|
4
6
|
# It inserts a new activity into the second line of the file, allowing
|
5
7
|
# us to test that friends and locations added in an `edit` command are
|
data/test/helper.rb
CHANGED
@@ -24,7 +24,7 @@ require "securerandom"
|
|
24
24
|
|
25
25
|
require "friends"
|
26
26
|
|
27
|
-
CONTENT = <<-FILE
|
27
|
+
CONTENT = <<-FILE
|
28
28
|
### Activities:
|
29
29
|
- 2018-02-06: @science:indoors:agronomy-with-hydroponics: **Norman Borlaug** and **George Washington Carver** scored a tour of _Atlantis_' hydroponics gardens through wetplants@example.org and they took me along.
|
30
30
|
- 2015-11-01: **Grace Hopper** and I went to _Martha's Vineyard_. George had to cancel at the last minute.
|
@@ -52,7 +52,7 @@ CONTENT = <<-FILE.freeze
|
|
52
52
|
FILE
|
53
53
|
|
54
54
|
# This is CONTENT but with activities, friends, and locations unsorted.
|
55
|
-
SCRAMBLED_CONTENT = <<-FILE
|
55
|
+
SCRAMBLED_CONTENT = <<-FILE
|
56
56
|
### Activities:
|
57
57
|
- 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**. @food
|
58
58
|
- 2015-11-01: **Grace Hopper** and I went to _Martha's Vineyard_. George had to cancel at the last minute.
|
@@ -135,7 +135,7 @@ def line_added(expected)
|
|
135
135
|
n_initial_lines = File.read(filename).split("\n").size
|
136
136
|
subject
|
137
137
|
lines = File.read(filename).split("\n")
|
138
|
-
value(lines
|
138
|
+
value(lines).must_include expected # Output includes our line
|
139
139
|
value(lines.size).must_equal(n_initial_lines + 1) # Line was added, not changed.
|
140
140
|
end
|
141
141
|
|
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.53'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Evelyn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chronic
|
@@ -52,26 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: semverse
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '2'
|
62
|
-
- - "<"
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version: '4'
|
65
|
-
type: :runtime
|
66
|
-
prerelease: false
|
67
|
-
version_requirements: !ruby/object:Gem::Requirement
|
68
|
-
requirements:
|
69
|
-
- - ">="
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
version: '2'
|
72
|
-
- - "<"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '4'
|
75
55
|
- !ruby/object:Gem::Dependency
|
76
56
|
name: tty-pager
|
77
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,14 +100,14 @@ dependencies:
|
|
120
100
|
requirements:
|
121
101
|
- - "~>"
|
122
102
|
- !ruby/object:Gem::Version
|
123
|
-
version: '
|
103
|
+
version: '13.0'
|
124
104
|
type: :development
|
125
105
|
prerelease: false
|
126
106
|
version_requirements: !ruby/object:Gem::Requirement
|
127
107
|
requirements:
|
128
108
|
- - "~>"
|
129
109
|
- !ruby/object:Gem::Version
|
130
|
-
version: '
|
110
|
+
version: '13.0'
|
131
111
|
description: Spend time with the people you care about. Introvert-tested. Extrovert-approved.
|
132
112
|
email:
|
133
113
|
- jacobevelyn@gmail.com
|
@@ -177,6 +157,7 @@ files:
|
|
177
157
|
- lib/friends/note.rb
|
178
158
|
- lib/friends/post_install_message.rb
|
179
159
|
- lib/friends/regex_builder.rb
|
160
|
+
- lib/friends/sem_ver_comparator.rb
|
180
161
|
- lib/friends/serializable.rb
|
181
162
|
- lib/friends/tag_regex.rb
|
182
163
|
- lib/friends/version.rb
|
@@ -224,7 +205,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
224
205
|
requirements:
|
225
206
|
- - ">="
|
226
207
|
- !ruby/object:Gem::Version
|
227
|
-
version: '2.
|
208
|
+
version: '2.3'
|
228
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
229
210
|
requirements:
|
230
211
|
- - ">="
|