friends 0.48 → 0.53

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0447c3979f63bd070ad83451203382518d615a4128f1180aa673cc2d5bdaa6b
4
- data.tar.gz: aff492bcb17479c7ec16e5025bf2ad7f0b59355ef54363ff8c6714c84a87a644
3
+ metadata.gz: 1be607e71eb696b1ebf8c6e46b1b561f153288ecede0c429fc93facd82a4b392
4
+ data.tar.gz: 2ea6de005359014425d32386306c0654eaa8349be0ebf19c957da3582a8c33f2
5
5
  SHA512:
6
- metadata.gz: 9121bba689b7b61af0ccc63f93e08e950885ac6d0d47eaf19a28bd826b65a8213e3dad5ee5546416540a03497b1bae0e21dfc8ee1f1243554b8b4700e39ecbba
7
- data.tar.gz: ede549f847d3d962b69fd77c15a4c5a1c031cc3d6cc7c92aac29ea7da9fff928573681e3d0f1451611f00dd703ad338be4ee950e197c05ecbf11002a05ef7b34
6
+ metadata.gz: e946dd33cd26ee1fdbe288cc2a2be9f0eb3eaae6bede1e47096c8b42d2af5da753b7d151b29fd75aa3aac5958d717a7141cac7e108e949961cd000e6af5f3451
7
+ data.tar.gz: 3c8e1a84fec855f17d0177de3cadd52a31bdfe51255bf77dd70dd9188be59871eec15d734e6f0b269183e330e15734653679f821d322407c6a87ca8d3f1a3b8f
@@ -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/master/CODE_OF_CONDUCT.md).
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
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.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/IndentHeredoc:
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
 
@@ -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 # 2.6 is tested below
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.6
13
+ - rvm: 2.7
14
14
  gemfile: Gemfile
15
15
  script:
16
16
  - bundle exec rake test
17
- - gem install --no-document rubocop -v 0.67 && rubocop
17
+ - bundle exec rubocop
18
18
  env:
19
19
  - CODE_COVERAGE=true
20
20
  branches:
21
21
  only:
22
- - master # Always run on the master branch
22
+ - main # Always run on the main branch
23
23
  notifications:
24
24
  email: false
@@ -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 `master` [\#238](https://github.com/JacobEvelyn/friends/issues/238)
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 \(master only\) [\#67](https://github.com/JacobEvelyn/friends/pull/67) ([JacobEvelyn](https://github.com/JacobEvelyn))
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
@@ -5,7 +5,7 @@ source "https://rubygems.org"
5
5
  gemspec
6
6
 
7
7
  group :development, :test do
8
- gem "rubocop", "0.67"
8
+ gem "rubocop", "0.81.0"
9
9
  end
10
10
 
11
11
  group :test do
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/master/graph/badge.svg)](https://codecov.io/gh/JacobEvelyn/friends)
3
- [![Build Status](https://travis-ci.com/JacobEvelyn/friends.svg?branch=master)](https://travis-ci.com/JacobEvelyn/friends)
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/master/friends.md)
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/master/.github/CONTRIBUTING.md).
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/master/CODE_OF_CONDUCT.md).
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/master/LICENSE.txt).
997
+ [MIT License](https://github.com/JacobEvelyn/friends/blob/main/LICENSE.txt).
@@ -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 `master` branch, update the `VERSION` constant in
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`).
@@ -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) do |value|
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 # rubocop:disable Lint/HandleExceptions
101
+ rescue Errno::EPIPE
106
102
  # Occurs when quitting the pager.
107
103
  end
108
104
  end
@@ -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.1's default-less keyword arguments and default UTF-8
27
- # encoding.
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", "~> 12.3"
36
+ spec.add_development_dependency "rake", "~> 13.0"
39
37
  end
@@ -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, filename)
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 Semverse::Version.coerce(remote_version) >
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]
@@ -14,8 +14,8 @@ module Friends
14
14
  class Event
15
15
  extend Serializable
16
16
 
17
- SERIALIZATION_PREFIX = "- ".freeze
18
- DATE_PARTITION = ": ".freeze
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
@@ -10,15 +10,13 @@ module Friends
10
10
  class Friend
11
11
  extend Serializable
12
12
 
13
- SERIALIZATION_PREFIX = "- ".freeze
14
- NICKNAME_PREFIX = "a.k.a. ".freeze
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 Metrics/LineLength
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 && tags_str.split(/\s+/) || []
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, and then
138
- # (via lookahead) spacing followed by a lowercase letter. This matches
139
- # the "Jake E." part of something like "Jake E. and I went skiing." This
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
- ([chunks.first, "#{chunks.last[0]}\.(?=#{splitter}(?-i)[a-z])"] if chunks.size > 1),
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
@@ -4,7 +4,7 @@
4
4
 
5
5
  module Friends
6
6
  class Graph
7
- DATE_FORMAT = "%b %Y".freeze
7
+ DATE_FORMAT = "%b %Y"
8
8
  SCALED_SIZE = 20
9
9
 
10
10
  # @param filtered_activities [Array<Friends::Activity>] a list of activities to highlight in
@@ -16,10 +16,10 @@ require "friends/friends_error"
16
16
 
17
17
  module Friends
18
18
  class Introvert
19
- ACTIVITIES_HEADER = "### Activities:".freeze
20
- NOTES_HEADER = "### Notes:".freeze
21
- FRIENDS_HEADER = "### Friends:".freeze
22
- LOCATIONS_HEADER = "### Locations:".freeze
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 => ex
729
- bad_line(ex, line_num)
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[(later_activity.date if later_activity) || 'present', :bold]}"
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
@@ -9,7 +9,7 @@ module Friends
9
9
  class Location
10
10
  extend Serializable
11
11
 
12
- SERIALIZATION_PREFIX = "- ".freeze
12
+ SERIALIZATION_PREFIX = "- "
13
13
 
14
14
  # @return [Regexp] the regex for capturing groups in deserialization
15
15
  def self.deserialization_regex
@@ -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 = "(?<!\\\\)".freeze
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 = "(?<!\\*\\*)".freeze
14
- NO_TRAILING_ASTERISKS = "(?!\\*\\*)".freeze
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 = "(?<!_)".freeze
19
- NO_TRAILING_UNDERSCORES = "(?!_)".freeze
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])".freeze
23
- NO_TRAILING_ALPHABETICALS = "(?![A-z])".freeze
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)".freeze
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
@@ -26,7 +26,7 @@ module Serializable
26
26
  reduce(:merge).
27
27
  reject { |_, v| v.nil? }
28
28
 
29
- new(args)
29
+ new(**args)
30
30
  end
31
31
 
32
32
  class SerializationError < StandardError
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Friends
4
- VERSION = "0.48".freeze
4
+ VERSION = "0.53"
5
5
  end
@@ -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" }
@@ -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" }
@@ -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
@@ -24,7 +24,7 @@ require "securerandom"
24
24
 
25
25
  require "friends"
26
26
 
27
- CONTENT = <<-FILE.freeze
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.freeze
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.index(expected)).must_be_kind_of Numeric # Not nil, so we know `expected` was found.
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.48'
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-03-27 00:00:00.000000000 Z
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: '12.3'
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: '12.3'
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.1'
208
+ version: '2.3'
228
209
  required_rubygems_version: !ruby/object:Gem::Requirement
229
210
  requirements:
230
211
  - - ">="