friends 0.37 → 0.38

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
  SHA1:
3
- metadata.gz: 149a16ce90d50f7fc56732aab02d9c27c3304cf3
4
- data.tar.gz: 3c45801a69f2ba00a13d2e08cdb1c25e3425eba7
3
+ metadata.gz: be5bfb57559485e598831f2e273f27fc57eca790
4
+ data.tar.gz: afbd6cd665503573e8cff620073e22617a18f810
5
5
  SHA512:
6
- metadata.gz: 2de1ef2a453e3a2a0e757888abe17824ea80b7212002f0cdc9240c4ecbef4c2f5e4e564ed923af6c10425206b2920975f5344c0b247a3974c0da33b608e0b780
7
- data.tar.gz: 768405774cdf4c0561f16ccf57e3611b37b5397f1e1ca195dd9837a8d001422eab17ef7ced9da59c6968becd12413f56f0b6e28ffea263f30bed36989ce33854
6
+ metadata.gz: 5da144c036b93a8e05bed9cc00e37167a168d6f64031c864827a6b348e7f96902a193c886ae0b3af8bd758aebefce576bcb5b931241bf0f91b3e5b539a6b3b91
7
+ data.tar.gz: 8d25dbdde17277d93a73fe7e9ed19472e7e29abfcd647bf7a2545346f3cabdd164bd03b88d3a4bfe8153d82522f9c0b0d267aadc923e314c892a7bece849a7f7
data/.rubocop.yml CHANGED
@@ -1,5 +1,11 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.1
2
+ TargetRubyVersion: 2.2
3
+
4
+ Gemspec/RequiredRubyVersion:
5
+ Enabled: false
6
+
7
+ Layout/ClosingHeredocIndentation:
8
+ Enabled: false
3
9
 
4
10
  Layout/DotPosition:
5
11
  EnforcedStyle: trailing
@@ -50,4 +56,4 @@ Style/StringLiterals:
50
56
  EnforcedStyle: double_quotes
51
57
 
52
58
  Style/SymbolArray:
53
- EnforcedStyle: brackets
59
+ EnforcedStyle: brackets
data/CHANGELOG.md CHANGED
@@ -14,6 +14,20 @@ making a small donation (🙏) to show you appreciate its continued development.
14
14
 
15
15
  👆 Donate with these buttons! 👆
16
16
 
17
+ ## [v0.38](https://github.com/JacobEvelyn/friends/tree/v0.38) (2018-07-24)
18
+ [Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.37...v0.38)
19
+
20
+ **Implemented enhancements:**
21
+
22
+ - Give matching priority to first name-only people [\#202](https://github.com/JacobEvelyn/friends/issues/202)
23
+ - Don't allow blank activities, friend names, tags, locations, or notes [\#198](https://github.com/JacobEvelyn/friends/issues/198)
24
+
25
+ **Merged pull requests:**
26
+
27
+ - Give name-matching priority to full-text matches [\#203](https://github.com/JacobEvelyn/friends/pull/203) ([JacobEvelyn](https://github.com/JacobEvelyn))
28
+ - Prevent the addition of blank events, names, and locations [\#200](https://github.com/JacobEvelyn/friends/pull/200) ([JacobEvelyn](https://github.com/JacobEvelyn))
29
+ - Fix Rubocop errors [\#196](https://github.com/JacobEvelyn/friends/pull/196) ([JacobEvelyn](https://github.com/JacobEvelyn))
30
+ - Update rake requirement to ~\> 12.3 [\#194](https://github.com/JacobEvelyn/friends/pull/194) ([dependabot[bot]](https://github.com/apps/dependabot))
17
31
 
18
32
  ## [v0.37](https://github.com/JacobEvelyn/friends/tree/v0.37) (2018-02-24)
19
33
  [Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.36...v0.37)
data/bin/friends CHANGED
@@ -4,7 +4,7 @@
4
4
  if ENV["TRAVIS"] == "true"
5
5
  require "simplecov"
6
6
  SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter
7
- SimpleCov.command_name Process.pid
7
+ SimpleCov.command_name Process.pid.to_s
8
8
  SimpleCov.start
9
9
  end
10
10
 
data/friends.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "friends/version"
6
6
 
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_development_dependency "coveralls", "~> 0.8"
33
33
  spec.add_development_dependency "minitest", "~> 5.5"
34
34
  spec.add_development_dependency "minitest-proveit", "~> 1.0"
35
- spec.add_development_dependency "rake", "~> 11.2"
35
+ spec.add_development_dependency "rake", "~> 12.3"
36
36
  spec.add_development_dependency "rubocop", "~> 0.49"
37
37
  spec.add_development_dependency "simplecov", "~> 0.14"
38
38
  end
@@ -6,7 +6,7 @@ command :add do |add|
6
6
  add.arg_name "NAME"
7
7
  add.command :friend do |add_friend|
8
8
  add_friend.action do |_, _, args|
9
- @introvert.add_friend(name: args.join(" "))
9
+ @introvert.add_friend(name: args.join(" ").strip)
10
10
  @dirty = true # Mark the file for cleaning.
11
11
  end
12
12
  end
@@ -16,7 +16,7 @@ command :add do |add|
16
16
  add.arg_name "DESCRIPTION"
17
17
  add.command event do |add_event|
18
18
  add_event.action do |_, _, args|
19
- @introvert.send("add_#{event}", serialization: args.join(" "))
19
+ @introvert.send("add_#{event}", serialization: args.join(" ").strip)
20
20
  @dirty = true # Mark the file for cleaning.
21
21
  end
22
22
  end
@@ -26,7 +26,7 @@ command :add do |add|
26
26
  add.arg_name "LOCATION"
27
27
  add.command :location do |add_location|
28
28
  add_location.action do |_, _, args|
29
- @introvert.add_location(name: args.join(" "))
29
+ @introvert.add_location(name: args.join(" ").strip)
30
30
  @dirty = true # Mark the file for cleaning.
31
31
  end
32
32
  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, nickname: args[1])
38
+ @introvert.add_nickname(name: args.first.strip, nickname: args[1].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)
49
+ tag: Tag.convert_to_tag(args.last.strip)
50
50
  )
51
51
  @dirty = true # Mark the file for cleaning.
52
52
  end
data/lib/friends/event.rb CHANGED
@@ -192,14 +192,16 @@ module Friends
192
192
  # more information see the comments below and the
193
193
  # introvert#set_likelihood_score! method.
194
194
  def highlight_friends(introvert:)
195
+ ## STEP 1
195
196
  # Split the regex friend map into two maps: one for names with only one
196
197
  # friend match and another for ambiguous names
197
198
  definite_map, ambiguous_map =
198
- introvert.regex_friend_map.partition { |_, arr| arr.size == 1 }
199
+ introvert.regex_friend_map.partition { |_, arr| arr.size == 1 }.map(&:to_h)
199
200
 
201
+ ## STEP 2
200
202
  matched_friends = []
201
203
 
202
- # First, we find all of the unambiguous matches, and make those
204
+ # We find all of the unambiguous matches, and make those
203
205
  # substitutions.
204
206
  definite_map.each do |regex, friend_list|
205
207
  # If we find a match, add the friend to the matched list and replace all
@@ -211,6 +213,36 @@ module Friends
211
213
  end
212
214
  end
213
215
 
216
+ ## STEP 3
217
+ # Now, we look through the ambiguous matches to find any where
218
+ # the matched text is the entire friend's name, and make those
219
+ # replacements too. ("Elizabeth" as a whole name should take
220
+ # precedence over "Elizabeth Cady Stanton" even if the latter
221
+ # is a better friend, because otherwise it's hard to just get
222
+ # your friend "Elizabeth" to match.)
223
+ full_name_regexes = []
224
+
225
+ ambiguous_map.each do |regex, friend_list|
226
+ smallest_name_friend = friend_list.min_by(&:name)
227
+ smallest_name = smallest_name_friend.name
228
+
229
+ # If one friend's name is contained within all of the other friend
230
+ # names within the regex group, we assume that that friend's name
231
+ # is the entire matched text (like "Elizabeth" in the above example).
232
+ next unless friend_list.all? { |friend| friend.name.include? smallest_name }
233
+
234
+ description_matches(regex: regex, replace: true, indicator: "**") do
235
+ matched_friends << smallest_name_friend
236
+ smallest_name
237
+ end
238
+
239
+ full_name_regexes << regex
240
+ end
241
+
242
+ # Delete all of regexes from STEP 3 substitutions.
243
+ full_name_regexes.each { |regex| ambiguous_map.delete(regex) }
244
+
245
+ ## STEP 4
214
246
  possible_matched_friends = []
215
247
 
216
248
  # Now, we look at regex matches that are ambiguous.
@@ -232,12 +264,13 @@ module Friends
232
264
  # If we find a match, take the most likely and replace all instances of
233
265
  # the matching text with that friend's name.
234
266
  description_matches(regex: regex, replace: true, indicator: "**") do
235
- friend_list.sort_by do |friend|
267
+ friend_list.min_by do |friend|
236
268
  [-friend.likelihood_score, -friend.n_activities]
237
- end.first.name
269
+ end.name
238
270
  end
239
271
  end
240
272
 
273
+ ## STEP 5
241
274
  # Lastly, we remove any backslashes, as these are used to escape friends'
242
275
  # names that we don't want to match.
243
276
  @description = @description.delete("\\")
data/lib/friends/graph.rb CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  module Friends
6
6
  class Graph
7
- DATE_FORMAT = "%b %Y".freeze # rubocop:disable Style/FormatStringToken
7
+ DATE_FORMAT = "%b %Y".freeze
8
8
 
9
9
  # @param activities [Array<Friends::Activity>] a list of activities to graph
10
10
  def initialize(filtered_activities:, all_activities:)
@@ -77,11 +77,11 @@ module Friends
77
77
  date.strftime(DATE_FORMAT)
78
78
  end
79
79
 
80
- # @param x [Integer] the x coordinate we want to color; x >= 0
80
+ # @param x_coord [Integer] the x coordinate we want to color; x >= 0
81
81
  # @return [Array<Integer>] the color we should use to paint
82
82
  # a point on the graph at the given x coordinate
83
- def color(x)
84
- COLORS[x % COLORS.size]
83
+ def color(x_coord)
84
+ COLORS[x_coord % COLORS.size]
85
85
  end
86
86
 
87
87
  # Originally generated by executing the code in Minitest's Pride plugin (the PrideLOL class),
@@ -91,7 +91,9 @@ module Friends
91
91
  Activity.deserialize(serialization).tap do |activity|
92
92
  # If there's no description, prompt the user for one.
93
93
  if activity.description.nil? || activity.description.empty?
94
- activity.description = Readline.readline(activity.to_s)
94
+ activity.description = Readline.readline(activity.to_s).to_s.strip
95
+
96
+ raise FriendsError, "Blank activity not added" if activity.description.empty?
95
97
  end
96
98
 
97
99
  activity.highlight_description(introvert: self)
@@ -108,7 +110,9 @@ module Friends
108
110
  Note.deserialize(serialization).tap do |note|
109
111
  # If there's no description, prompt the user for one.
110
112
  if note.description.nil? || note.description.empty?
111
- note.description = Readline.readline(note.to_s)
113
+ note.description = Readline.readline(note.to_s).to_s.strip
114
+
115
+ raise FriendsError, "Blank note not added" if note.description.empty?
112
116
  end
113
117
 
114
118
  note.highlight_description(introvert: self)
@@ -188,6 +192,8 @@ module Friends
188
192
  # @param nickname [String] the nickname to add to the friend
189
193
  # @raise [FriendsError] if 0 or 2+ friends match the given name
190
194
  def add_nickname(name:, nickname:)
195
+ raise FriendsError, "Nickname cannot be blank" if nickname.empty?
196
+
191
197
  friend = thing_with_name_in(:friend, name)
192
198
  friend.add_nickname(nickname)
193
199
 
@@ -199,6 +205,8 @@ module Friends
199
205
  # @param tag [String] the tag to add to the friend, of the form: "@tag"
200
206
  # @raise [FriendsError] if 0 or 2+ friends match the given name
201
207
  def add_tag(name:, tag:)
208
+ raise FriendsError, "Tag cannot be blank" if tag == "@"
209
+
202
210
  friend = thing_with_name_in(:friend, name)
203
211
  friend.add_tag(tag)
204
212
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Friends
4
- VERSION = "0.37".freeze
4
+ VERSION = "0.38".freeze
5
5
  end
@@ -51,6 +51,18 @@ def description_parsing_specs(test_stdout: true)
51
51
  describe "description parsing" do
52
52
  let(:date) { Date.today.strftime }
53
53
 
54
+ unless test_stdout
55
+ describe "when description is blank" do
56
+ let(:description) { " " }
57
+
58
+ it "prints an error message" do
59
+ subject[:stderr].must_equal(
60
+ ensure_trailing_newline_unless_empty("Error: Blank #{event} not added")
61
+ )
62
+ end
63
+ end
64
+ end
65
+
54
66
  describe "when description includes a friend's full name (case insensitive)" do
55
67
  let(:description) { "Lunch with grace hopper." }
56
68
 
@@ -275,6 +287,35 @@ FILE
275
287
  end
276
288
  end
277
289
 
290
+ describe "when description has a friend's full name which is another friend's first name" do
291
+ let(:description) { "Hung out with Elizabeth for most of the day." }
292
+
293
+ # Make sure "Elizabeth II" is a better friend than just "Elizabeth" to
294
+ # ensure our result isn't due to chance.
295
+ let(:content) do
296
+ <<-FILE
297
+ ### Activities:
298
+ - 2018-08-05: Royal picnic with **Elizabeth II**.
299
+
300
+ ### Notes:
301
+
302
+ ### Friends:
303
+ - Elizabeth
304
+ - Elizabeth II
305
+
306
+ ### Locations:
307
+ FILE
308
+ end
309
+
310
+ it { line_added "- #{date}: Hung out with **Elizabeth** for most of the day." }
311
+ if test_stdout
312
+ it do
313
+ stdout_only "#{capitalized_event} added: \"#{date}: "\
314
+ "Hung out with Elizabeth for most of the day.\""
315
+ end
316
+ end
317
+ end
318
+
278
319
  describe "when description has a name with multiple friend matches" do
279
320
  describe "when there is useful context from past activities" do
280
321
  let(:description) { "Met John + Elizabeth." }
@@ -372,6 +413,7 @@ FILE
372
413
  end
373
414
 
374
415
  def parsing_specs(event:)
416
+ let(:event) { event.to_s }
375
417
  let(:capitalized_event) { event.to_s.capitalize }
376
418
 
377
419
  describe "when given a date and a description in the command" do
@@ -7,6 +7,14 @@ clean_describe "add friend" do
7
7
 
8
8
  let(:content) { CONTENT }
9
9
 
10
+ describe "when friend name is blank" do
11
+ let(:friend_name) { "' '" }
12
+
13
+ it "prints an error message" do
14
+ stderr_only 'Error: Expected "[Friend Name]"'
15
+ end
16
+ end
17
+
10
18
  describe "when there is an existing friend with that name" do
11
19
  let(:friend_name) { "George Washington Carver" }
12
20
 
@@ -7,6 +7,14 @@ clean_describe "add location" do
7
7
 
8
8
  let(:content) { CONTENT }
9
9
 
10
+ describe "when location name is blank" do
11
+ let(:location_name) { "' '" }
12
+
13
+ it "prints an error message" do
14
+ stderr_only 'Error: Expected "[Location Name]"'
15
+ end
16
+ end
17
+
10
18
  describe "when there is an existing location with that name" do
11
19
  let(:location_name) { "Paris" }
12
20
 
@@ -29,22 +29,33 @@ clean_describe "add nickname" do
29
29
 
30
30
  describe "when friend name has one match" do
31
31
  let(:friend_name) { "George" }
32
- let(:nickname) { "Georgie" }
33
32
 
34
- it "adds nickname to friend" do
35
- line_changed "- George Washington Carver", "- George Washington Carver (a.k.a. Georgie)"
36
- end
33
+ describe "when nickname is blank" do
34
+ let(:nickname) { "' '" }
37
35
 
38
- it "updates parenthetical in file when friend has existing nicknames" do
39
- run_cmd("add nickname #{friend_name} 'Mr. Peanut'")
40
- line_changed(
41
- "- George Washington Carver (a.k.a. Mr. Peanut)",
42
- "- George Washington Carver (a.k.a. Mr. Peanut a.k.a. Georgie)"
43
- )
36
+ it "prints an error message" do
37
+ stderr_only "Error: Nickname cannot be blank"
38
+ end
44
39
  end
45
40
 
46
- it "prints an output message" do
47
- stdout_only 'Nickname added: "George Washington Carver (a.k.a. Georgie)"'
41
+ describe "when nickname is not blank" do
42
+ let(:nickname) { "Georgie" }
43
+
44
+ it "adds nickname to friend" do
45
+ line_changed "- George Washington Carver", "- George Washington Carver (a.k.a. Georgie)"
46
+ end
47
+
48
+ it "updates parenthetical in file when friend has existing nicknames" do
49
+ run_cmd("add nickname #{friend_name} 'Mr. Peanut'")
50
+ line_changed(
51
+ "- George Washington Carver (a.k.a. Mr. Peanut)",
52
+ "- George Washington Carver (a.k.a. Mr. Peanut a.k.a. Georgie)"
53
+ )
54
+ end
55
+
56
+ it "prints an output message" do
57
+ stdout_only 'Nickname added: "George Washington Carver (a.k.a. Georgie)"'
58
+ end
48
59
  end
49
60
  end
50
61
  end
@@ -29,21 +29,25 @@ clean_describe "add tag" do
29
29
 
30
30
  describe "when friend name has one match" do
31
31
  let(:friend_name) { "George" }
32
- let(:tag) { "school:tuskegee-institute" }
33
32
 
34
- it "adds tag to friend" do
35
- line_changed(
36
- "- George Washington Carver",
37
- "- George Washington Carver @school:tuskegee-institute"
38
- )
33
+ describe "when tag is blank" do
34
+ let(:tag) { "' '" }
35
+
36
+ it "prints an error message" do
37
+ stderr_only "Error: Tag cannot be blank"
38
+ end
39
39
  end
40
40
 
41
- it "prints an output message" do
42
- stdout_only 'Tag added to friend: "George Washington Carver @school:tuskegee-institute"'
41
+ describe "when tag is blank with an @" do
42
+ let(:tag) { "'@ '" }
43
+
44
+ it "prints an error message" do
45
+ stderr_only "Error: Tag cannot be blank"
46
+ end
43
47
  end
44
48
 
45
- describe "when tag is passed with @" do
46
- let(:tag) { "@school:tuskegee-institute" }
49
+ describe "when tag is not blank" do
50
+ let(:tag) { "school:tuskegee-institute" }
47
51
 
48
52
  it "adds tag to friend" do
49
53
  line_changed(
@@ -55,17 +59,32 @@ clean_describe "add tag" do
55
59
  it "prints an output message" do
56
60
  stdout_only 'Tag added to friend: "George Washington Carver @school:tuskegee-institute"'
57
61
  end
58
- end
59
62
 
60
- describe "when friend already has tags" do
61
- let(:friend_name) { "Marie" }
62
- let(:tag) { "school:ecole-normale-superieure" }
63
+ describe "when tag is passed with @" do
64
+ let(:tag) { "@school:tuskegee-institute" }
63
65
 
64
- it "allows for multiple tags" do
65
- line_changed(
66
- "- Marie Curie [Atlantis] @science",
67
- "- Marie Curie [Atlantis] @science @school:ecole-normale-superieure"
68
- )
66
+ it "adds tag to friend" do
67
+ line_changed(
68
+ "- George Washington Carver",
69
+ "- George Washington Carver @school:tuskegee-institute"
70
+ )
71
+ end
72
+
73
+ it "prints an output message" do
74
+ stdout_only 'Tag added to friend: "George Washington Carver @school:tuskegee-institute"'
75
+ end
76
+ end
77
+
78
+ describe "when friend already has tags" do
79
+ let(:friend_name) { "Marie" }
80
+ let(:tag) { "school:ecole-normale-superieure" }
81
+
82
+ it "allows for multiple tags" do
83
+ line_changed(
84
+ "- Marie Curie [Atlantis] @science",
85
+ "- Marie Curie [Atlantis] @science @school:ecole-normale-superieure"
86
+ )
87
+ end
69
88
  end
70
89
  end
71
90
  end
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.37'
4
+ version: '0.38'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Evelyn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-24 00:00:00.000000000 Z
11
+ date: 2018-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chronic
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '11.2'
131
+ version: '12.3'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '11.2'
138
+ version: '12.3'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rubocop
141
141
  requirement: !ruby/object:Gem::Requirement