friends 0.48 → 0.53

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="