friends 0.16 → 0.17
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|