friends 0.16 → 0.17
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 +4 -4
- data/CHANGELOG.md +23 -0
- data/CONTRIBUTING.md +2 -1
- data/README.md +59 -2
- data/bin/friends +74 -18
- data/friends.md +9 -4
- data/lib/friends/activity.rb +101 -42
- data/lib/friends/friend.rb +15 -39
- data/lib/friends/introvert.rb +229 -71
- data/lib/friends/location.rb +49 -0
- data/lib/friends/regex_builder.rb +38 -0
- data/lib/friends/version.rb +1 -1
- data/test/activity_spec.rb +89 -18
- data/test/friend_spec.rb +2 -13
- data/test/introvert_spec.rb +330 -56
- data/test/location_spec.rb +58 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67aca011d40b8ec1f5f92dda50a441dff1363523
|
4
|
+
data.tar.gz: fdd5c7f7459f4bf28773d4e6c1ef3ab4d462f35e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d05eff21075277e8bc244c69c4bf506c52b74955ef841d65a0fd23cd883d48af4f0d652ccde694ec453e4460355f542251c891f97ad6fe0986f49b08952e273
|
7
|
+
data.tar.gz: 8df26c543ce7c25dc6a1c1474914fd170ca973019b27834df33c5003b1900e07f5112a02c96117dd8b4eb7c17fb1f1c3af9e9c8a5e722ce2ee06b9ca883e2e36
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v0.17](https://github.com/JacobEvelyn/friends/tree/v0.17) (2016-03-28)
|
4
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.16...v0.17)
|
5
|
+
|
6
|
+
**Implemented enhancements:**
|
7
|
+
|
8
|
+
- Add --in flag to `suggest` [\#106](https://github.com/JacobEvelyn/friends/issues/106)
|
9
|
+
- Allow locations to be renamed [\#105](https://github.com/JacobEvelyn/friends/issues/105)
|
10
|
+
- Add --in location flag to `list activities` [\#100](https://github.com/JacobEvelyn/friends/issues/100)
|
11
|
+
- Add --in location flag to `list friends` [\#99](https://github.com/JacobEvelyn/friends/issues/99)
|
12
|
+
- Add location matching to activity descriptions [\#97](https://github.com/JacobEvelyn/friends/issues/97)
|
13
|
+
- Add `list locations` command [\#96](https://github.com/JacobEvelyn/friends/issues/96)
|
14
|
+
- Add `add location` command [\#95](https://github.com/JacobEvelyn/friends/issues/95)
|
15
|
+
- Add backwards-compatible \#\#\# Locations: heading to friends.md file. [\#94](https://github.com/JacobEvelyn/friends/issues/94)
|
16
|
+
- Update documentation for `graph` and `stats` commands [\#93](https://github.com/JacobEvelyn/friends/issues/93)
|
17
|
+
- Add location features [\#107](https://github.com/JacobEvelyn/friends/pull/107) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
18
|
+
- Fix documentation formatting and typos [\#104](https://github.com/JacobEvelyn/friends/pull/104) ([andypearson](https://github.com/andypearson))
|
19
|
+
- Add backwards-compatible `add location` and `list locations` commands [\#101](https://github.com/JacobEvelyn/friends/pull/101) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
20
|
+
|
21
|
+
**Merged pull requests:**
|
22
|
+
|
23
|
+
- Add location matching/highlighting to activities [\#103](https://github.com/JacobEvelyn/friends/pull/103) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
24
|
+
- Add documentation for `graph` and `stats` commands [\#102](https://github.com/JacobEvelyn/friends/pull/102) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
25
|
+
|
3
26
|
## [v0.16](https://github.com/JacobEvelyn/friends/tree/v0.16) (2016-03-23)
|
4
27
|
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.15...v0.16)
|
5
28
|
|
data/CONTRIBUTING.md
CHANGED
@@ -17,7 +17,8 @@ that's great as well!
|
|
17
17
|
(`git checkout -b my-new-feature`).
|
18
18
|
6. Make your changes. Add or modify tests if necessary!
|
19
19
|
(Run tests with `rake test`.) Do your best to conform to
|
20
|
-
existing style and commenting patterns.
|
20
|
+
existing style and commenting patterns. You can run the local version of the
|
21
|
+
`friends` script with `bundle exec bin/friends`.
|
21
22
|
7. Update the `README.md` as necessary to include your changes.
|
22
23
|
8. Commit your changes
|
23
24
|
(`git commit -am "Add some feature"`). You should see
|
data/README.md
CHANGED
@@ -87,11 +87,21 @@ And they can be removed as well:
|
|
87
87
|
$ friends remove nickname "Grace Hopper" "The Admiral"
|
88
88
|
Nickname removed: "Grace Hopper (a.k.a. Amazing Grace)"
|
89
89
|
```
|
90
|
+
##### Set a friend's location:
|
91
|
+
```
|
92
|
+
$ friends set location Marie Paris
|
93
|
+
Marie Curie's location set to: Paris
|
94
|
+
```
|
90
95
|
##### Change a friend's name:
|
91
96
|
```
|
92
97
|
$ friends rename friend "Grace Hopper" "Grace Brewster Murray Hopper"
|
93
98
|
Name changed: "Grace Brewster Murray Hopper (a.k.a. Amazing Grace)"
|
94
99
|
```
|
100
|
+
##### Change the name of a location:
|
101
|
+
```
|
102
|
+
$ friends rename location Paris "Paris, France"
|
103
|
+
Location renamed: "Paris, France"
|
104
|
+
```
|
95
105
|
##### Suggest a friend to do something with:
|
96
106
|
```
|
97
107
|
$ friends suggest
|
@@ -99,11 +109,26 @@ Distant friend: Marie Curie
|
|
99
109
|
Moderate friend: Grace Hopper
|
100
110
|
Close friend: George Washington Carver
|
101
111
|
```
|
112
|
+
Or only suggest friends in a specific location:
|
113
|
+
```
|
114
|
+
$ friends suggest --in Paris
|
115
|
+
Distant friend: Marie Curie
|
116
|
+
```
|
117
|
+
##### Add a location for your friends and activities:
|
118
|
+
```
|
119
|
+
$ friends add location Atlantis
|
120
|
+
Location added: "Atlantis"
|
121
|
+
```
|
122
|
+
##### And locations will be matched as well:
|
123
|
+
```
|
124
|
+
$ friends add activity "Went swimming near atlantis with George."
|
125
|
+
Activity added: "2016-01-06: Went swimming near Atlantis with George Washington Carver."
|
126
|
+
```
|
102
127
|
##### List the activities you've recorded:
|
103
128
|
```
|
104
129
|
$ friends list activities
|
105
130
|
2015-01-04: Got lunch with Grace Hopper and George Washington Carver.
|
106
|
-
2014-12-31: Celebrated the new year with Marie Curie.
|
131
|
+
2014-12-31: Celebrated the new year with Marie Curie in New York City.
|
107
132
|
2014-11-15: Talked to George Washington Carver on the phone for an hour.
|
108
133
|
```
|
109
134
|
Or only list the activities you did with a certain friend:
|
@@ -112,6 +137,11 @@ $ friends list activities --with "George"
|
|
112
137
|
2015-01-04: Got lunch with Grace Hopper and George Washington Carver.
|
113
138
|
2014-11-15: Talked to George Washington Carver on the phone for an hour.
|
114
139
|
|
140
|
+
```
|
141
|
+
Or filter your activities by location:
|
142
|
+
```
|
143
|
+
$ friends list activities --in "New York"
|
144
|
+
2014-12-31: Celebrated the new year with Marie Curie in New York City.
|
115
145
|
```
|
116
146
|
##### Find your favorite friends:
|
117
147
|
```
|
@@ -130,12 +160,27 @@ Your favorite friends:
|
|
130
160
|
```
|
131
161
|
##### Graph (in color!) your relationship with a friend over time:
|
132
162
|
```
|
133
|
-
$ friends graph
|
163
|
+
$ friends graph George
|
134
164
|
Nov 2014 |█
|
135
165
|
Dec 2014 |
|
136
166
|
Jan 2015 |█████
|
137
167
|
Feb 2015 |███
|
138
168
|
```
|
169
|
+
##### Or just graph all of your activities:
|
170
|
+
```
|
171
|
+
$ friends graph
|
172
|
+
Nov 2014 |███
|
173
|
+
Dec 2014 |██
|
174
|
+
Jan 2015 |███████
|
175
|
+
Feb 2015 |█████
|
176
|
+
```
|
177
|
+
##### Or just check out your lifetime stats:
|
178
|
+
```
|
179
|
+
$ friends stats
|
180
|
+
Total activities: 4
|
181
|
+
Total friends: 3
|
182
|
+
Total time elapsed: 5 days
|
183
|
+
```
|
139
184
|
##### List all of your friends:
|
140
185
|
```
|
141
186
|
$ friends list friends
|
@@ -143,6 +188,18 @@ George Washington Carver
|
|
143
188
|
Grace Hopper
|
144
189
|
Marie Curie
|
145
190
|
```
|
191
|
+
Or list only the friends in a specific location:
|
192
|
+
```
|
193
|
+
$ friends list friends --in Paris
|
194
|
+
Marie Curie
|
195
|
+
```
|
196
|
+
##### List the locations you've added:
|
197
|
+
```
|
198
|
+
$ friends list locations
|
199
|
+
Atlantis
|
200
|
+
New York City
|
201
|
+
Paris
|
202
|
+
```
|
146
203
|
##### Update to the latest version:
|
147
204
|
```
|
148
205
|
$ friends update
|
data/bin/friends
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
# Todo:
|
4
|
-
# - Split out serialization into separate repository.
|
5
|
-
# - Add auto check for updates.
|
6
|
-
# - Allow easy editing of most recent entry.
|
7
|
-
|
8
3
|
require "gli"
|
9
4
|
require "paint"
|
10
5
|
require "readline"
|
@@ -61,12 +56,16 @@ command :update do |update|
|
|
61
56
|
end
|
62
57
|
end
|
63
58
|
|
64
|
-
desc "Lists friends
|
59
|
+
desc "Lists friends, activities, and locations"
|
65
60
|
command :list do |list|
|
66
61
|
list.desc "List all friends"
|
67
62
|
list.command :friends do |list_friends|
|
68
|
-
list_friends.
|
69
|
-
|
63
|
+
list_friends.flag [:in],
|
64
|
+
arg_name: "LOCATION",
|
65
|
+
desc: "List only friends in the given location"
|
66
|
+
|
67
|
+
list_friends.action do |_, options|
|
68
|
+
puts @introvert.list_friends(location_name: options[:in])
|
70
69
|
end
|
71
70
|
end
|
72
71
|
|
@@ -105,14 +104,29 @@ command :list do |list|
|
|
105
104
|
arg_name: "NAME",
|
106
105
|
desc: "List only activities involving the given friend"
|
107
106
|
|
107
|
+
list_activities.flag [:in],
|
108
|
+
arg_name: "LOCATION",
|
109
|
+
desc: "List only activities in the given location"
|
110
|
+
|
108
111
|
list_activities.action do |_, options|
|
109
112
|
limit = options[:limit].to_i
|
110
|
-
puts @introvert.list_activities(
|
113
|
+
puts @introvert.list_activities(
|
114
|
+
limit: limit,
|
115
|
+
with: options[:with],
|
116
|
+
location_name: options[:in]
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
list.desc "List all locations"
|
122
|
+
list.command :locations do |list_locations|
|
123
|
+
list_locations.action do
|
124
|
+
puts @introvert.list_locations
|
111
125
|
end
|
112
126
|
end
|
113
127
|
end
|
114
128
|
|
115
|
-
desc "Adds a friend or activity"
|
129
|
+
desc "Adds a friend (or nickname), activity, or location"
|
116
130
|
command :add do |add|
|
117
131
|
add.desc "Adds a friend"
|
118
132
|
add.arg_name "NAME"
|
@@ -133,7 +147,7 @@ command :add do |add|
|
|
133
147
|
# If there's no description, prompt the user for one.
|
134
148
|
if activity.description.nil? || activity.description.empty?
|
135
149
|
activity.description = Readline.readline(activity.display_text)
|
136
|
-
activity.
|
150
|
+
activity.highlight_description(introvert: @introvert)
|
137
151
|
end
|
138
152
|
|
139
153
|
@message = "Activity added: \"#{activity.display_text}\""
|
@@ -141,6 +155,16 @@ command :add do |add|
|
|
141
155
|
end
|
142
156
|
end
|
143
157
|
|
158
|
+
add.desc "Adds a location"
|
159
|
+
add.arg_name "LOCATION"
|
160
|
+
add.command :location do |add_location|
|
161
|
+
add_location.action do |_, _, args|
|
162
|
+
location = @introvert.add_location(name: args.first)
|
163
|
+
@message = "Location added: \"#{location.name}\""
|
164
|
+
@dirty = true # Mark the file for cleaning.
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
144
168
|
add.desc "Adds a nickname to a friend"
|
145
169
|
add.arg_name "NAME NICKNAME"
|
146
170
|
add.command :nickname do |add_nickname|
|
@@ -152,6 +176,19 @@ command :add do |add|
|
|
152
176
|
end
|
153
177
|
end
|
154
178
|
|
179
|
+
desc "Set data about friends"
|
180
|
+
command :set do |set|
|
181
|
+
set.desc "Set a friend's location"
|
182
|
+
set.arg_name "NAME LOCATION"
|
183
|
+
set.command :location do |set_location|
|
184
|
+
set_location.action do |_, _, args|
|
185
|
+
friend = @introvert.set_location(name: args.first, location_name: args[1])
|
186
|
+
@message = "#{friend.name}'s location set to: #{friend.location_name}"
|
187
|
+
@dirty = true # Mark the file for cleaning.
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
155
192
|
desc "Remove a nickname"
|
156
193
|
command :remove do |remove|
|
157
194
|
remove.desc "Removes a nickname from a friend"
|
@@ -165,8 +202,8 @@ command :remove do |remove|
|
|
165
202
|
end
|
166
203
|
end
|
167
204
|
|
168
|
-
desc "Graph a friend's relationship over time"
|
169
|
-
arg_name "NAME"
|
205
|
+
desc "Graph all activities or a friend's relationship over time"
|
206
|
+
arg_name "NAME (default: none)"
|
170
207
|
command :graph do |graph|
|
171
208
|
graph.action do |_, _, args|
|
172
209
|
# This math is taken from Minitest's Pride plugin (the PrideLOL class).
|
@@ -192,8 +229,12 @@ end
|
|
192
229
|
|
193
230
|
desc "Suggest friends to do activities with"
|
194
231
|
command :suggest do |suggest|
|
195
|
-
suggest.
|
196
|
-
|
232
|
+
suggest.flag [:in],
|
233
|
+
arg_name: "LOCATION",
|
234
|
+
desc: "Suggest only friends in the given location"
|
235
|
+
|
236
|
+
suggest.action do |_, options|
|
237
|
+
suggestions = @introvert.suggest(location_name: options[:in])
|
197
238
|
|
198
239
|
puts "Distant friend: "\
|
199
240
|
"#{Paint[suggestions[:distant].sample || 'None found', :bold, :magenta]}"
|
@@ -222,18 +263,33 @@ command :stats do |stats|
|
|
222
263
|
end
|
223
264
|
end
|
224
265
|
|
225
|
-
desc "Renames a friend"
|
266
|
+
desc "Renames a friend or location"
|
226
267
|
command :rename do |rename|
|
227
268
|
rename.desc "Renames a friend"
|
228
269
|
rename.arg_name "NAME NEW_NAME"
|
229
270
|
rename.command :friend do |rename_friend|
|
230
271
|
rename_friend.action do |_, _, args|
|
231
|
-
friend = @introvert.rename_friend(
|
232
|
-
|
272
|
+
friend = @introvert.rename_friend(
|
273
|
+
old_name: args.first,
|
274
|
+
new_name: args[1]
|
275
|
+
)
|
233
276
|
@message = "Name changed: \"#{friend}\""
|
234
277
|
@dirty = true # Mark the file for cleaning.
|
235
278
|
end
|
236
279
|
end
|
280
|
+
|
281
|
+
rename.desc "Renames a location"
|
282
|
+
rename.arg_name "NAME NEW_NAME"
|
283
|
+
rename.command :location do |rename_location|
|
284
|
+
rename_location.action do |_, _, args|
|
285
|
+
location = @introvert.rename_location(
|
286
|
+
old_name: args.first,
|
287
|
+
new_name: args[1]
|
288
|
+
)
|
289
|
+
@message = "Location renamed: \"#{location.name}\""
|
290
|
+
@dirty = true # Mark the file for cleaning.
|
291
|
+
end
|
292
|
+
end
|
237
293
|
end
|
238
294
|
|
239
295
|
# Before each command, clean up all arguments and create the global Introvert.
|
data/friends.md
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
### Activities:
|
2
|
-
- 2015-11-01: **Grace Hopper** and I went to
|
2
|
+
- 2015-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute.
|
3
3
|
- 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**.
|
4
|
-
- 2014-12-31: Celebrated the new year with **Marie Curie**.
|
4
|
+
- 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**.
|
5
5
|
- 2014-11-15: Talked to **George Washington Carver** on the phone for an hour.
|
6
6
|
|
7
7
|
### Friends:
|
8
8
|
- George Washington Carver
|
9
|
-
- Grace Hopper (a.k.a. The Admiral)
|
10
|
-
- Marie Curie
|
9
|
+
- Grace Hopper (a.k.a. The Admiral) [Paris]
|
10
|
+
- Marie Curie [Atlantis]
|
11
|
+
|
12
|
+
### Locations:
|
13
|
+
- Atlantis
|
14
|
+
- Marie's Diner
|
15
|
+
- Paris
|
data/lib/friends/activity.rb
CHANGED
@@ -34,7 +34,9 @@ module Friends
|
|
34
34
|
# Partition lets us parse "Today" and "Today: I awoke." identically.
|
35
35
|
date_s, _, description = str.partition(DATE_PARTITION)
|
36
36
|
|
37
|
+
# rubocop:disable Lint/AssignmentInCondition
|
37
38
|
if time = Chronic.parse(date_s)
|
39
|
+
# rubocop:enable Lint/AssignmentInCondition
|
38
40
|
@date = time.to_date
|
39
41
|
@description = description
|
40
42
|
else
|
@@ -52,10 +54,18 @@ module Friends
|
|
52
54
|
date_s = Paint[date, :bold]
|
53
55
|
description_s = description.to_s
|
54
56
|
# rubocop:disable Lint/AssignmentInCondition
|
55
|
-
while match = description_s.match(
|
57
|
+
while match = description_s.match(/\*\*([^\*]+)\*\*/)
|
56
58
|
# rubocop:enable Lint/AssignmentInCondition
|
57
59
|
description_s = "#{match.pre_match}"\
|
58
|
-
"#{Paint[match[
|
60
|
+
"#{Paint[match[1], :bold, :magenta]}"\
|
61
|
+
"#{match.post_match}"
|
62
|
+
end
|
63
|
+
|
64
|
+
# rubocop:disable Lint/AssignmentInCondition
|
65
|
+
while match = description_s.match(/_([^_]+)_/)
|
66
|
+
# rubocop:enable Lint/AssignmentInCondition
|
67
|
+
description_s = "#{match.pre_match}"\
|
68
|
+
"#{Paint[match[1], :bold, :yellow]}"\
|
59
69
|
"#{match.post_match}"
|
60
70
|
end
|
61
71
|
"#{date_s}: #{description_s}"
|
@@ -66,11 +76,78 @@ module Friends
|
|
66
76
|
"#{SERIALIZATION_PREFIX}#{date}: #{description}"
|
67
77
|
end
|
68
78
|
|
79
|
+
# Modify the description to turn inputted friend names
|
80
|
+
# (e.g. "Jacob" or "Jacob Evelyn") into full asterisk'd names
|
81
|
+
# (e.g. "**Jacob Evelyn**") and inputted location names (e.g. "Atlantis")
|
82
|
+
# into full underscore'd names (e.g. "_Atlantis_").
|
83
|
+
# @param introvert [Introvert] used to access internal data structures to
|
84
|
+
# perform object matching
|
85
|
+
def highlight_description(introvert:)
|
86
|
+
highlight_locations(introvert: introvert)
|
87
|
+
highlight_friends(introvert: introvert)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Updates a friend's old_name to their new_name
|
91
|
+
# @param [String] old_name
|
92
|
+
# @param [String] new_name
|
93
|
+
# @return [String] if name found in description
|
94
|
+
# @return [nil] if no change was made
|
95
|
+
def update_friend_name(old_name:, new_name:)
|
96
|
+
@description.gsub!(
|
97
|
+
Regexp.new("(?<=\\*\\*)#{old_name}(?=\\*\\*)"),
|
98
|
+
new_name
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Updates a location's old_name to their new_name
|
103
|
+
# @param [String] old_name
|
104
|
+
# @param [String] new_name
|
105
|
+
# @return [String] if name found in description
|
106
|
+
# @return [nil] if no change was made
|
107
|
+
def update_location_name(old_name:, new_name:)
|
108
|
+
@description.gsub!(Regexp.new("(?<=_)#{old_name}(?=_)"), new_name)
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param location [Location] the location to test
|
112
|
+
# @return [Boolean] true iff this activity includes the given location
|
113
|
+
def includes_location?(location:)
|
114
|
+
@description.scan(/(?<=_)[^_]+(?=_)/).include? location.name
|
115
|
+
end
|
116
|
+
|
117
|
+
# @param friend [Friend] the friend to test
|
118
|
+
# @return [Boolean] true iff this activity includes the given friend
|
119
|
+
def includes_friend?(friend:)
|
120
|
+
friend_names.include? friend.name
|
121
|
+
end
|
122
|
+
|
123
|
+
# Find the names of all friends in this description.
|
124
|
+
# @return [Array] list of all friend names in the description
|
125
|
+
def friend_names
|
126
|
+
@description.scan(/(?<=\*\*)\w[^\*]*(?=\*\*)/).uniq
|
127
|
+
end
|
128
|
+
memoize :friend_names
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# Modify the description to turn inputted location names (e.g. "Atlantis")
|
133
|
+
# into full underscore'd names (e.g. "_Atlantis_").
|
134
|
+
# @param introvert [Introvert] used to access internal data structures to
|
135
|
+
# perform location matching
|
136
|
+
def highlight_locations(introvert:)
|
137
|
+
introvert.regex_location_map.each do |regex, location|
|
138
|
+
# If we find a match, replace all instances of the matching text with
|
139
|
+
# the location's name. We use single-underscores to indicate locations.
|
140
|
+
description_matches(regex: regex, replace: true, indicator: "_") do
|
141
|
+
location.name
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
69
146
|
# Modify the description to turn inputted friend names
|
70
147
|
# (e.g. "Jacob" or "Jacob Evelyn") into full asterisk'd names
|
71
148
|
# (e.g. "**Jacob Evelyn**")
|
72
|
-
# @param introvert [Introvert] used to access
|
73
|
-
#
|
149
|
+
# @param introvert [Introvert] used to access internal data structures to
|
150
|
+
# perform friend matching
|
74
151
|
# NOTE: When a friend name matches more than one friend, this method chooses
|
75
152
|
# a friend based on a best-guess algorithm that looks at which friends do
|
76
153
|
# activities together and which friends are stronger than others. For
|
@@ -89,7 +166,7 @@ module Friends
|
|
89
166
|
definite_map.each do |regex, friend_list|
|
90
167
|
# If we find a match, add the friend to the matched list and replace all
|
91
168
|
# instances of the matching text with the friend's name.
|
92
|
-
description_matches(regex: regex, replace: true) do
|
169
|
+
description_matches(regex: regex, replace: true, indicator: "**") do
|
93
170
|
friend = friend_list.first # There's only one friend in the list.
|
94
171
|
matched_friends << friend
|
95
172
|
friend.name
|
@@ -101,7 +178,7 @@ module Friends
|
|
101
178
|
# Now, we look at regex matches that are ambiguous.
|
102
179
|
ambiguous_map.each do |regex, friend_list|
|
103
180
|
# If we find a match, add the friend to the possible-match list.
|
104
|
-
description_matches(regex: regex, replace: false) do
|
181
|
+
description_matches(regex: regex, replace: false, indicator: "**") do
|
105
182
|
possible_matched_friends << friend_list
|
106
183
|
end
|
107
184
|
end
|
@@ -117,7 +194,7 @@ module Friends
|
|
117
194
|
ambiguous_map.each do |regex, friend_list|
|
118
195
|
# If we find a match, take the most likely and replace all instances of
|
119
196
|
# the matching text with that friend's name.
|
120
|
-
description_matches(regex: regex, replace: true) do
|
197
|
+
description_matches(regex: regex, replace: true, indicator: "**") do
|
121
198
|
friend_list.sort_by do |friend|
|
122
199
|
[-friend.likelihood_score, -friend.n_activities]
|
123
200
|
end.first.name
|
@@ -129,45 +206,19 @@ module Friends
|
|
129
206
|
@description = @description.delete("\\")
|
130
207
|
end
|
131
208
|
|
132
|
-
# Updates a friend's old_name to their new_name
|
133
|
-
# @param [String] old_name
|
134
|
-
# @param [String] new_name
|
135
|
-
# @return [String] if name found in description
|
136
|
-
# @return [nil] if no change was made
|
137
|
-
def update_name(old_name:, new_name:)
|
138
|
-
description.gsub!(
|
139
|
-
Regexp.new("(?<=\\*\\*)#{old_name}(?=\\*\\*)"),
|
140
|
-
new_name)
|
141
|
-
end
|
142
|
-
|
143
|
-
# @param friend [Friend] the friend to test
|
144
|
-
# @return [Boolean] true iff this activity includes the given friend
|
145
|
-
def includes_friend?(friend:)
|
146
|
-
friend_names.include? friend.name
|
147
|
-
end
|
148
|
-
|
149
|
-
# Find the names of all friends in this description.
|
150
|
-
# @return [Array] list of all friend names in the description
|
151
|
-
def friend_names
|
152
|
-
description.scan(/(?<=\*\*)\w[^\*]*(?=\*\*)/).uniq
|
153
|
-
end
|
154
|
-
memoize :friend_names
|
155
|
-
|
156
|
-
private
|
157
|
-
|
158
209
|
# This method accepts a block, and tests a regex on the @description
|
159
210
|
# instance variable.
|
160
211
|
# - If the regex does not match, the block is not executed.
|
161
212
|
# - If the regex matches, the block is executed exactly once, and:
|
162
213
|
# - If `replace` is true, all of the regex's matches are replaced by the
|
163
214
|
# return value of the block, EXCEPT when the matched text is between a
|
164
|
-
# set of double-asterisks ("**")
|
165
|
-
# another friend's matched name.
|
215
|
+
# set of double-asterisks ("**") or single-underscores ("_") indicating
|
216
|
+
# it is already part of another location or friend's matched name.
|
166
217
|
# - If `replace` is not true, we do not modify @description.
|
167
218
|
# @param regex [Regexp] the regex to test against @description
|
168
219
|
# @param replace [Boolean] true iff we should replace regex matches with the
|
169
220
|
# yielded block's result in @description
|
170
|
-
def description_matches(regex:, replace:)
|
221
|
+
def description_matches(regex:, replace:, indicator:)
|
171
222
|
# rubocop:disable Lint/AssignmentInCondition
|
172
223
|
return unless match = @description.match(regex) # Abort if no match.
|
173
224
|
# rubocop:enable Lint/AssignmentInCondition
|
@@ -176,14 +227,22 @@ module Friends
|
|
176
227
|
|
177
228
|
position = 0 # Prevent infinite looping by tracking last match position.
|
178
229
|
loop do
|
179
|
-
# Only make a replacement if we're not between a set of "**"s.
|
230
|
+
# Only make a replacement if we're not between a set of "**"s or "_"s.
|
180
231
|
if match.pre_match.scan("**").size % 2 == 0 &&
|
181
|
-
match.post_match.scan("**").size % 2 == 0
|
182
|
-
|
232
|
+
match.post_match.scan("**").size % 2 == 0 &&
|
233
|
+
match.pre_match.scan("_").size % 2 == 0 &&
|
234
|
+
match.post_match.scan("_").size % 2 == 0
|
235
|
+
@description = [
|
236
|
+
match.pre_match,
|
237
|
+
indicator,
|
238
|
+
str,
|
239
|
+
indicator,
|
240
|
+
match.post_match
|
241
|
+
].join
|
183
242
|
else
|
184
|
-
# If we're between double-asterisks we're
|
185
|
-
# we don't make a substitution. We update
|
186
|
-
# looping.
|
243
|
+
# If we're between double-asterisks or single-underscores we're
|
244
|
+
# already part of a name, so we don't make a substitution. We update
|
245
|
+
# `position` to avoid infinite looping.
|
187
246
|
position = match.end(0)
|
188
247
|
end
|
189
248
|
|