friends 0.28 → 0.29

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -4
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +2 -0
  5. data/CHANGELOG.md +13 -0
  6. data/README.md +35 -5
  7. data/Rakefile +1 -1
  8. data/bin/friends +7 -379
  9. data/friends.gemspec +3 -1
  10. data/lib/friends/activity.rb +4 -4
  11. data/lib/friends/commands/add.rb +64 -0
  12. data/lib/friends/commands/clean.rb +9 -0
  13. data/lib/friends/commands/edit.rb +12 -0
  14. data/lib/friends/commands/graph.rb +56 -0
  15. data/lib/friends/commands/list.rb +143 -0
  16. data/lib/friends/commands/remove.rb +27 -0
  17. data/lib/friends/commands/rename.rb +30 -0
  18. data/lib/friends/commands/set.rb +14 -0
  19. data/lib/friends/commands/stats.rb +11 -0
  20. data/lib/friends/commands/suggest.rb +20 -0
  21. data/lib/friends/commands/update.rb +29 -0
  22. data/lib/friends/graph.rb +7 -7
  23. data/lib/friends/introvert.rb +23 -18
  24. data/lib/friends/version.rb +1 -1
  25. data/test/commands/add/activity_spec.rb +379 -0
  26. data/test/commands/add/friend_spec.rb +30 -0
  27. data/test/commands/add/location_spec.rb +30 -0
  28. data/test/commands/add/nickname_spec.rb +50 -0
  29. data/test/commands/add/tag_spec.rb +65 -0
  30. data/test/commands/clean_spec.rb +39 -0
  31. data/test/commands/graph_spec.rb +147 -0
  32. data/test/commands/help_spec.rb +45 -0
  33. data/test/commands/list/activities_spec.rb +136 -0
  34. data/test/commands/list/favorite/friends_spec.rb +77 -0
  35. data/test/commands/list/favorite/locations_spec.rb +82 -0
  36. data/test/commands/list/friends_spec.rb +76 -0
  37. data/test/commands/list/locations_spec.rb +35 -0
  38. data/test/commands/list/tags_spec.rb +58 -0
  39. data/test/commands/remove/nickname_spec.rb +63 -0
  40. data/test/commands/remove/tag_spec.rb +64 -0
  41. data/test/commands/rename/friend_spec.rb +55 -0
  42. data/test/commands/rename/location_spec.rb +43 -0
  43. data/test/commands/set/location_spec.rb +54 -0
  44. data/test/commands/stats_spec.rb +41 -0
  45. data/test/commands/suggest_spec.rb +86 -0
  46. data/test/commands/update_spec.rb +13 -0
  47. data/test/helper.rb +114 -0
  48. metadata +89 -15
  49. data/test/activity_spec.rb +0 -597
  50. data/test/friend_spec.rb +0 -241
  51. data/test/graph_spec.rb +0 -92
  52. data/test/introvert_spec.rb +0 -969
  53. data/test/location_spec.rb +0 -60
data/test/friend_spec.rb DELETED
@@ -1,241 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "./test/helper"
4
-
5
- describe Friends::Friend do
6
- let(:friend_name) { "Jacob Evelyn" }
7
- let(:friend) { Friends::Friend.new(name: friend_name) }
8
-
9
- describe ".deserialize" do
10
- subject { Friends::Friend.deserialize(serialized_str) }
11
-
12
- describe "when string is well-formed" do
13
- let(:serialized_str) do
14
- "#{Friends::Friend::SERIALIZATION_PREFIX}#{friend_name}"
15
- end
16
-
17
- it "creates a friend with the correct name" do
18
- subject.name.must_equal friend_name
19
- end
20
- end
21
-
22
- describe "when string is malformed" do
23
- let(:serialized_str) { "" }
24
-
25
- it { proc { subject }.must_raise Serializable::SerializationError }
26
- end
27
-
28
- describe "when string has one nickname" do
29
- let(:serialized_str) { "Guybrush Threepwood (a.k.a. Brush)" }
30
- it { subject.name.must_equal "Guybrush Threepwood" }
31
- it { subject.instance_variable_get(:@nicknames).must_equal ["Brush"] }
32
- end
33
-
34
- describe "when string has multiple nicknames" do
35
- let(:serialized_str) { "Guybrush Threepwood (a.k.a. Brush a.k.a. Guy)" }
36
- it { subject.name.must_equal "Guybrush Threepwood" }
37
- it do
38
- subject.instance_variable_get(:@nicknames).must_equal ["Brush", "Guy"]
39
- end
40
- end
41
-
42
- describe "when string has a location" do
43
- let(:serialized_str) { "Guybrush Threepwood [Plunder Island]" }
44
- it { subject.name.must_equal "Guybrush Threepwood" }
45
- it { subject.location_name.must_equal "Plunder Island" }
46
- end
47
-
48
- describe "when string has one tag" do
49
- let(:serialized_str) { "Guybrush Threepwood @pirate" }
50
- it { subject.name.must_equal "Guybrush Threepwood" }
51
- it { subject.tags.must_equal ["@pirate"] }
52
- end
53
-
54
- describe "when string has multiple tags" do
55
- let(:serialized_str) { "Guybrush Threepwood @pirate @swashbuckler" }
56
- it { subject.name.must_equal "Guybrush Threepwood" }
57
- it { subject.tags.must_equal ["@pirate", "@swashbuckler"] }
58
- end
59
-
60
- describe "when string has nicknames and tags" do
61
- let(:serialized_str) do
62
- "Guybrush Threepwood (a.k.a. Brush a.k.a. Guy) @pirate @swashbuckler"
63
- end
64
- it { subject.name.must_equal "Guybrush Threepwood" }
65
- it do
66
- subject.instance_variable_get(:@nicknames).must_equal ["Brush", "Guy"]
67
- end
68
- it { subject.tags.must_equal ["@pirate", "@swashbuckler"] }
69
- end
70
-
71
- describe "when string has nicknames and a location" do
72
- let(:serialized_str) do
73
- "Guybrush Threepwood (a.k.a. Brush a.k.a. Guy) [Plunder Island]"
74
- end
75
- it { subject.name.must_equal "Guybrush Threepwood" }
76
- it do
77
- subject.instance_variable_get(:@nicknames).must_equal ["Brush", "Guy"]
78
- end
79
- it { subject.location_name.must_equal "Plunder Island" }
80
- end
81
-
82
- describe "when string has a location and tags" do
83
- let(:serialized_str) do
84
- "Guybrush Threepwood [Plunder Island] @pirate @swashbuckler"
85
- end
86
- it { subject.name.must_equal "Guybrush Threepwood" }
87
- it { subject.location_name.must_equal "Plunder Island" }
88
- it { subject.tags.must_equal ["@pirate", "@swashbuckler"] }
89
- end
90
-
91
- describe "when string has nicknames, a location, and tags" do
92
- let(:serialized_str) do
93
- "Guybrush Threepwood (a.k.a. Brush a.k.a. Guy) [Plunder Island] "\
94
- "@pirate @swashbuckler"
95
- end
96
- it { subject.name.must_equal "Guybrush Threepwood" }
97
- it do
98
- subject.instance_variable_get(:@nicknames).must_equal ["Brush", "Guy"]
99
- end
100
- it { subject.location_name.must_equal "Plunder Island" }
101
- it { subject.tags.must_equal ["@pirate", "@swashbuckler"] }
102
- end
103
- end
104
-
105
- describe "#new" do
106
- subject { friend }
107
-
108
- it { subject.name.must_equal friend_name }
109
- end
110
-
111
- describe "#serialize" do
112
- subject { friend.serialize }
113
-
114
- it do
115
- subject.must_equal(
116
- "#{Friends::Friend::SERIALIZATION_PREFIX}#{friend_name}"
117
- )
118
- end
119
- end
120
-
121
- describe "#add_nickname" do
122
- subject { friend.add_nickname("The Dude") }
123
-
124
- it "adds the nickname" do
125
- subject
126
- friend.instance_variable_get(:@nicknames).must_include("The Dude")
127
- end
128
-
129
- it "does not keep duplicates" do
130
- # Add the same nickname twice. Do not use `subject` because it's memoized.
131
- friend.add_nickname("The Dude")
132
- friend.add_nickname("The Dude")
133
-
134
- friend.instance_variable_get(:@nicknames).must_equal ["The Dude"]
135
- end
136
- end
137
-
138
- describe "#remove_nickname" do
139
- subject { friend.remove_nickname("Jake") }
140
-
141
- describe "when the nickname is present" do
142
- let(:friend) do
143
- Friends::Friend.new(name: friend_name, nickname_str: "Jake")
144
- end
145
-
146
- it "removes the nickname" do
147
- friend.instance_variable_get(:@nicknames).must_equal ["Jake"]
148
- subject
149
- friend.instance_variable_get(:@nicknames).must_equal []
150
- end
151
- end
152
-
153
- describe "when the nickname is not present" do
154
- it "raises an error if the nickname is not found" do
155
- proc { subject }.must_raise Friends::FriendsError
156
- end
157
- end
158
- end
159
-
160
- describe "#add_tag" do
161
- subject { friend.add_tag("@college") }
162
-
163
- it "adds the nickname" do
164
- subject
165
- friend.tags.must_include("@college")
166
- end
167
-
168
- it "does not keep duplicates" do
169
- # Add the same nickname twice. Do not use `subject` because it's memoized.
170
- friend.add_tag("@college")
171
- friend.add_tag("@college")
172
-
173
- friend.tags.must_equal ["@college"]
174
- end
175
- end
176
-
177
- describe "@remove_tag" do
178
- subject { friend.remove_tag("@school") }
179
-
180
- describe "when the tag is present" do
181
- let(:friend) do
182
- Friends::Friend.new(name: friend_name, tags_str: "@school @work")
183
- end
184
-
185
- it "removes the nickname" do
186
- friend.instance_variable_get(:@tags).must_equal ["@school", "@work"]
187
- subject
188
- friend.instance_variable_get(:@tags).must_equal ["@work"]
189
- end
190
- end
191
-
192
- describe "when the nickname is not present" do
193
- it "raises an error if the nickname is not found" do
194
- proc { subject }.must_raise Friends::FriendsError
195
- end
196
- end
197
- end
198
-
199
- describe "#n_activities" do
200
- subject { friend.n_activities }
201
-
202
- it "defaults to zero" do
203
- subject.must_equal 0
204
- end
205
-
206
- it "is writable" do
207
- friend.n_activities += 1
208
- subject.must_equal 1
209
- end
210
- end
211
-
212
- describe "#likelihood_score" do
213
- subject { friend.likelihood_score }
214
-
215
- it "defaults to zero" do
216
- subject.must_equal 0
217
- end
218
-
219
- it "is writable" do
220
- friend.likelihood_score += 1
221
- subject.must_equal 1
222
- end
223
- end
224
-
225
- describe "#regexes_for_name" do
226
- subject { friend.regexes_for_name }
227
-
228
- it "generates appropriate regexes" do
229
- subject.any? { |r| r =~ friend_name }.must_equal true
230
- subject.any? { |r| r =~ friend_name.partition(" ").first }.must_equal true
231
- end
232
- end
233
-
234
- describe "#<=>" do
235
- it "sorts alphabetically" do
236
- aaron = Friends::Friend.new(name: "Aaron")
237
- zeke = Friends::Friend.new(name: "Zeke")
238
- [zeke, aaron].sort.must_equal [aaron, zeke]
239
- end
240
- end
241
- end
data/test/graph_spec.rb DELETED
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
- require "./test/helper"
3
-
4
- describe Friends::Graph do
5
- subject do
6
- Friends::Graph.new(
7
- start_date: start_date,
8
- end_date: end_date,
9
- activities: activities
10
- ).to_h
11
- end
12
-
13
- let(:start_date) { Date.new(2016, 1, 1) }
14
- let(:end_date) { Date.new(2016, 2, 1) }
15
- let(:activities) do
16
- [
17
- Friends::Activity.new(
18
- str: "2016-02-01: Relaxing."
19
- ),
20
-
21
- Friends::Activity.new(
22
- str: "2016-01-01: Running."
23
- )
24
- ]
25
- end
26
-
27
- it "graphs activities by month" do
28
- subject.must_equal(
29
- "Jan 2016" => 1,
30
- "Feb 2016" => 1
31
- )
32
- end
33
-
34
- describe "there is a gap between activities" do
35
- let(:end_date) { Date.new(2016, 3, 1) }
36
-
37
- let(:activities) do
38
- [
39
- Friends::Activity.new(
40
- str: "2016-03-01: Relaxing."
41
- ),
42
-
43
- Friends::Activity.new(
44
- str: "2016-01-01: Running."
45
- )
46
- ]
47
- end
48
-
49
- it "includes the month with no activities" do
50
- subject.must_equal(
51
- "Jan 2016" => 1,
52
- "Feb 2016" => 0,
53
- "Mar 2016" => 1
54
- )
55
- end
56
- end
57
-
58
- describe "graph starts before the first activity" do
59
- let(:start_date) { Date.new(2015, 12, 1) }
60
-
61
- it "graphs activities by month" do
62
- subject.must_equal(
63
- "Dec 2015" => 0,
64
- "Jan 2016" => 1,
65
- "Feb 2016" => 1
66
- )
67
- end
68
- end
69
-
70
- describe "graph ends after the last activity" do
71
- let(:end_date) { Date.new(2016, 3, 1) }
72
-
73
- it "graphs activities by month" do
74
- subject.must_equal(
75
- "Jan 2016" => 1,
76
- "Feb 2016" => 1,
77
- "Mar 2016" => 0
78
- )
79
- end
80
- end
81
-
82
- describe "there are no activities" do
83
- let(:activities) { [] }
84
-
85
- it "graphs activities by month" do
86
- subject.must_equal(
87
- "Jan 2016" => 0,
88
- "Feb 2016" => 0
89
- )
90
- end
91
- end
92
- end
@@ -1,969 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "./test/helper"
4
-
5
- describe Friends::Introvert do
6
- # Add readers to make internal state easier to test.
7
- module Friends
8
- class Introvert
9
- attr_reader :filename, :activities, :friends
10
- end
11
- end
12
-
13
- # Add helpers to set internal states for friends/locations/activities.
14
- def stub_friends(val)
15
- old_val = introvert.instance_variable_get(:@friends)
16
- introvert.instance_variable_set(:@friends, val)
17
- introvert.send(:set_n_activities!, :friend)
18
- yield
19
- introvert.instance_variable_set(:@friends, old_val)
20
- end
21
-
22
- def stub_activities(val)
23
- old_val = introvert.instance_variable_get(:@activities)
24
- introvert.instance_variable_set(:@activities, val)
25
- introvert.send(:set_n_activities!, :friend)
26
- introvert.send(:set_n_activities!, :location)
27
- yield
28
- introvert.instance_variable_set(:@activities, old_val)
29
- end
30
-
31
- def stub_locations(val)
32
- old_val = introvert.instance_variable_get(:@locations)
33
- introvert.instance_variable_set(:@locations, val)
34
- introvert.send(:set_n_activities!, :location)
35
- yield
36
- introvert.instance_variable_set(:@locations, old_val)
37
- end
38
-
39
- let(:filename) { "test/tmp/friends.md" }
40
- let(:args) { { filename: filename } }
41
- let(:introvert) { Friends::Introvert.new(args) }
42
- let(:friend_names) { ["George Washington Carver", "Betsy Ross"] }
43
- let(:friends) do
44
- friend_names.map do |name|
45
- Friends::Friend.new(name: name, tags_str: "@test")
46
- end
47
- end
48
- let(:activities) do
49
- [
50
- Friends::Activity.new(
51
- str: "Lunch w/ **#{friend_names.first}** and **#{friend_names.last}**."
52
- ),
53
- Friends::Activity.new(
54
- str: "Yesterday: Called **#{friend_names.last}**."
55
- )
56
- ]
57
- end
58
- let(:locations) do
59
- [
60
- Friends::Location.new(name: "The Eiffel Tower"),
61
- Friends::Location.new(name: "Atlantis")
62
- ]
63
- end
64
-
65
- describe "#new" do
66
- it "accepts all arguments" do
67
- introvert # Build a new introvert.
68
-
69
- # Check passed values.
70
- introvert.filename.must_equal filename
71
- end
72
-
73
- it "has sane defaults" do
74
- args.clear # Pass no arguments to the initializer.
75
- introvert # Build a new introvert.
76
-
77
- # Check default values.
78
- introvert.filename.must_equal Friends::Introvert::DEFAULT_FILENAME
79
- end
80
- end
81
-
82
- describe "#clean" do
83
- subject { introvert.clean }
84
-
85
- # Delete the file that is created each time.
86
- after { File.delete(filename) if File.exist?(filename) }
87
-
88
- it "writes cleaned file" do
89
- sorted_friends = friends.sort
90
- unsorted_friends = sorted_friends.reverse
91
- sorted_activities = activities.sort
92
- unsorted_activities = sorted_activities.reverse
93
- sorted_locations = locations.sort
94
- unsorted_locations = sorted_locations.reverse
95
-
96
- serialized_friends = sorted_friends.map(&:serialize).join("\n")
97
- serialized_activities = sorted_activities.map(&:serialize).join("\n")
98
- serialized_locations = sorted_locations.map(&:serialize).join("\n")
99
-
100
- expected_output =
101
- "#{Friends::Introvert::ACTIVITIES_HEADER}\n"\
102
- "#{serialized_activities}\n\n"\
103
- "#{Friends::Introvert::FRIENDS_HEADER}\n"\
104
- "#{serialized_friends}\n\n"\
105
- "#{Friends::Introvert::LOCATIONS_HEADER}\n"\
106
- "#{serialized_locations}\n"
107
-
108
- # Read the input as unsorted, and make sure we get sorted output.
109
- stub_friends(unsorted_friends) do
110
- stub_activities(unsorted_activities) do
111
- stub_locations(unsorted_locations) do
112
- subject
113
- File.read(filename).must_equal expected_output
114
- end
115
- end
116
- end
117
- end
118
-
119
- it { subject.must_equal filename }
120
- end
121
-
122
- describe "#list_friends" do
123
- subject do
124
- introvert.list_friends(
125
- location_name: location_name,
126
- tagged: tagged,
127
- verbose: verbose
128
- )
129
- end
130
-
131
- describe "when a location name has been passed" do
132
- let(:location_name) { "Atlantis" }
133
- let(:tagged) { nil }
134
- let(:verbose) { false }
135
- let(:friends) do
136
- [
137
- Friends::Friend.new(name: "Mark Watney", location_name: "Mars"),
138
- Friends::Friend.new(name: "Aquaman", location_name: "Atlantis"),
139
- Friends::Friend.new(name: "Shark-Boy", location_name: "Atlantis"),
140
- Friends::Friend.new(name: "Ms. Nowhere")
141
- ]
142
- end
143
-
144
- it "lists the names of friends in that location" do
145
- stub_friends(friends) do
146
- stub_locations(locations) do
147
- subject.must_equal ["Aquaman", "Shark-Boy"]
148
- end
149
- end
150
- end
151
- end
152
-
153
- describe "when a tag has been passed" do
154
- let(:location_name) { nil }
155
- let(:tagged) { "@superhero" }
156
- let(:verbose) { false }
157
- let(:friends) do
158
- [
159
- Friends::Friend.new(name: "Mark Watney"),
160
- Friends::Friend.new(name: "Aquaman", tags_str: "@superhero"),
161
- Friends::Friend.new(name: "Shark-Boy", tags_str: "@superhero"),
162
- Friends::Friend.new(name: "Ms. Nowhere")
163
- ]
164
- end
165
-
166
- it "lists the names of friends in that location" do
167
- stub_friends(friends) do
168
- stub_locations(locations) do
169
- subject.must_equal ["Aquaman", "Shark-Boy"]
170
- end
171
- end
172
- end
173
- end
174
-
175
- describe "when not verbose" do
176
- let(:verbose) { false }
177
- let(:location_name) { nil }
178
- let(:tagged) { nil }
179
- it "lists the names of friends" do
180
- stub_friends(friends) do
181
- subject.must_equal friend_names
182
- end
183
- end
184
- end
185
-
186
- describe "when verbose" do
187
- let(:verbose) { true }
188
- let(:location_name) { nil }
189
- let(:tagged) { nil }
190
- it "lists the names and details of friends" do
191
- stub_friends(friends) do
192
- # Just check that there's a difference between the verbose and
193
- # non-verbose versions of friends (otherwise our test is useless).
194
- subject.wont_equal friend_names
195
-
196
- subject.must_equal friends.map(&:to_s)
197
- end
198
- end
199
- end
200
- end
201
-
202
- describe "#add_friend" do
203
- let(:new_friend_name) { "Jacob Evelyn" }
204
- subject { introvert.add_friend(name: new_friend_name) }
205
-
206
- describe "when there is no existing friend with that name" do
207
- it "adds the given friend" do
208
- stub_friends(friends) do
209
- subject
210
- introvert.
211
- instance_variable_get(:@friends).
212
- map(&:name).
213
- must_include new_friend_name
214
- end
215
- end
216
-
217
- it "returns the friend added" do
218
- stub_friends(friends) do
219
- subject.name.must_equal new_friend_name
220
- end
221
- end
222
- end
223
-
224
- describe "when there is an existing friend with that name" do
225
- let(:new_friend_name) { friend_names.first }
226
-
227
- it "raises an error" do
228
- stub_friends(friends) do
229
- proc { subject }.must_raise Friends::FriendsError
230
- end
231
- end
232
- end
233
- end
234
-
235
- describe "#add_location" do
236
- let(:new_location_name) { "Peru" }
237
- subject { introvert.add_location(name: new_location_name) }
238
-
239
- describe "when there is no existing location with that name" do
240
- it "adds the given location" do
241
- stub_locations(locations) do
242
- subject
243
- introvert.list_locations.must_include new_location_name
244
- end
245
- end
246
-
247
- it "returns the location added" do
248
- stub_locations(locations) do
249
- subject.name.must_equal new_location_name
250
- end
251
- end
252
- end
253
-
254
- describe "when there is an existing location with that name" do
255
- let(:new_location_name) { locations.first.name }
256
-
257
- it "raises an error" do
258
- stub_locations(locations) do
259
- proc { subject }.must_raise Friends::FriendsError
260
- end
261
- end
262
- end
263
- end
264
-
265
- describe "#list_locations" do
266
- subject { introvert.list_locations }
267
-
268
- it "lists all locations" do
269
- stub_locations(locations) do
270
- subject.must_equal locations.map(&:name)
271
- end
272
- end
273
- end
274
-
275
- describe "#list_tags" do
276
- subject { introvert.list_tags(from: from) }
277
-
278
- let(:activities) do
279
- [
280
- Friends::Activity.new(str: "Lunch in the park. @picnic @food"),
281
- Friends::Activity.new(str: "Fancy dinner. @food @swanky")
282
- ]
283
- end
284
-
285
- let(:friends) do
286
- [
287
- Friends::Friend.new(name: "Grace Hopper", tags_str: "@coder @navy"),
288
- Friends::Friend.new(name: "James Bond", tags_str: "@cool @swanky")
289
- ]
290
- end
291
-
292
- describe "when from flag is nil" do
293
- let(:from) { nil }
294
- it "lists all tags in sorted order" do
295
- stub_activities(activities) do
296
- stub_friends(friends) do
297
- subject.must_equal [
298
- "@coder",
299
- "@cool",
300
- "@food",
301
- "@navy",
302
- "@picnic",
303
- "@swanky"
304
- ]
305
- end
306
- end
307
- end
308
- end
309
-
310
- describe 'when from flag is "activities"' do
311
- let(:from) { "activities" }
312
- it "lists all activity tags in sorted order" do
313
- stub_activities(activities) do
314
- stub_friends(friends) do
315
- subject.must_equal ["@food", "@picnic", "@swanky"]
316
- end
317
- end
318
- end
319
- end
320
-
321
- describe 'when from flag is "friends"' do
322
- let(:from) { "friends" }
323
- it "lists all friend tags in sorted order" do
324
- stub_activities(activities) do
325
- stub_friends(friends) do
326
- subject.must_equal ["@coder", "@cool", "@navy", "@swanky"]
327
- end
328
- end
329
- end
330
- end
331
- end
332
-
333
- describe "#list_activities" do
334
- subject do
335
- introvert.list_activities(
336
- limit: limit,
337
- with: with,
338
- location_name: location_name,
339
- tagged: tagged
340
- )
341
- end
342
- let(:limit) { nil }
343
- let(:with) { nil }
344
- let(:location_name) { nil }
345
- let(:tagged) { nil }
346
-
347
- describe "when the limit is lower than the number of activities" do
348
- let(:limit) { 1 }
349
-
350
- it "lists that number of activities" do
351
- stub_activities(activities) do
352
- subject.size.must_equal limit
353
- end
354
- end
355
- end
356
-
357
- describe "when the limit is equal to the number of activities" do
358
- let(:limit) { activities.size }
359
-
360
- it "lists all activities" do
361
- stub_activities(activities) do
362
- subject.size.must_equal activities.size
363
- end
364
- end
365
- end
366
-
367
- describe "when the limit is greater than the number of activities" do
368
- let(:limit) { activities.size + 5 }
369
-
370
- it "lists all activities" do
371
- stub_activities(activities) do
372
- subject.size.must_equal activities.size
373
- end
374
- end
375
- end
376
-
377
- describe "when the limit is nil" do
378
- let(:limit) { nil }
379
-
380
- it "lists all activities" do
381
- stub_activities(activities) do
382
- subject.size.must_equal activities.size
383
- end
384
- end
385
- end
386
-
387
- describe "when not filtering by a friend" do
388
- let(:with) { nil }
389
-
390
- it "lists the activities" do
391
- stub_activities(activities) do
392
- subject.must_equal activities.map(&:to_s)
393
- end
394
- end
395
- end
396
-
397
- describe "when filtering by part of a friend's name" do
398
- let(:with) { "george" }
399
-
400
- describe "when there is more than one friend match" do
401
- let(:friend_names) { ["George Washington Carver", "George Harrison"] }
402
-
403
- it "raises an error" do
404
- stub_friends(friends) do
405
- stub_activities(activities) do
406
- proc { subject }.must_raise Friends::FriendsError
407
- end
408
- end
409
- end
410
- end
411
-
412
- describe "when there are no friend matches" do
413
- let(:friend_names) { ["Joe"] }
414
-
415
- it "raises an error" do
416
- stub_friends(friends) do
417
- stub_activities(activities) do
418
- proc { subject }.must_raise Friends::FriendsError
419
- end
420
- end
421
- end
422
- end
423
-
424
- describe "when there is exactly one friend match" do
425
- it "filters the activities by that friend" do
426
- stub_friends(friends) do
427
- stub_activities(activities) do
428
- # Only one activity has that friend.
429
- subject.must_equal activities[0..0].map(&:to_s)
430
- end
431
- end
432
- end
433
- end
434
- end
435
-
436
- describe "when not filtering by a location" do
437
- let(:location_name) { nil }
438
-
439
- it "lists the activities" do
440
- stub_activities(activities) do
441
- subject.must_equal activities.map(&:to_s)
442
- end
443
- end
444
- end
445
-
446
- describe "when filtering by part of a location name" do
447
- let(:location_name) { "City" }
448
-
449
- describe "when there is more than one location match" do
450
- let(:locations) do
451
- [
452
- Friends::Location.new(name: "New York City"),
453
- Friends::Location.new(name: "Kansas City")
454
- ]
455
- end
456
-
457
- it "raises an error" do
458
- stub_friends(friends) do
459
- stub_locations(locations) do
460
- stub_activities(activities) do
461
- proc { subject }.must_raise Friends::FriendsError
462
- end
463
- end
464
- end
465
- end
466
- end
467
-
468
- describe "when there are no location matches" do
469
- let(:locations) { [Friends::Location.new(name: "Atantis")] }
470
-
471
- it "raises an error" do
472
- stub_friends(friends) do
473
- stub_locations(locations) do
474
- stub_activities(activities) do
475
- proc { subject }.must_raise Friends::FriendsError
476
- end
477
- end
478
- end
479
- end
480
- end
481
-
482
- describe "when there is exactly one location match" do
483
- let(:location_name) { "Atlantis" }
484
- let(:activities) do
485
- [
486
- Friends::Activity.new(str: "Swimming near _Atlantis_."),
487
- Friends::Activity.new(str: "Swimming somewhere else.")
488
- ]
489
- end
490
-
491
- it "filters the activities by that location" do
492
- stub_friends(friends) do
493
- stub_locations(locations) do
494
- stub_activities(activities) do
495
- # Only one activity has that friend.
496
- subject.must_equal activities[0..0].map(&:to_s)
497
- end
498
- end
499
- end
500
- end
501
- end
502
- end
503
-
504
- describe "when not filtering by a tag" do
505
- let(:tagged) { nil }
506
-
507
- it "lists the activities" do
508
- stub_activities(activities) do
509
- subject.must_equal activities.map(&:to_s)
510
- end
511
- end
512
- end
513
-
514
- describe "when filtering by a tag" do
515
- let(:activities) do
516
- [
517
- Friends::Activity.new(str: "Tennis after work. @exercise @tennis"),
518
- Friends::Activity.new(str: "Wimbledon! @tennis"),
519
- Friends::Activity.new(str: "Drinks after work. @beer")
520
- ]
521
- end
522
-
523
- describe "when the tag ('@tag') is not used at all" do
524
- let(:tagged) { "@garbage" }
525
- it "returns no results" do
526
- stub_activities(activities) do
527
- subject.must_equal []
528
- end
529
- end
530
- end
531
-
532
- describe "when the tag ('@tag') is used once" do
533
- let(:tagged) { "@beer" }
534
- it "returns the activity with that tag" do
535
- stub_activities(activities) do
536
- subject.must_equal [activities.last.to_s]
537
- end
538
- end
539
- end
540
-
541
- describe "when the tag ('@tag') is used multiple times" do
542
- let(:tagged) { "@tennis" }
543
- it "returns the activities with that tag" do
544
- stub_activities(activities) do
545
- subject.must_equal activities[0..1].map(&:to_s)
546
- end
547
- end
548
- end
549
- end
550
- end
551
-
552
- describe "#add_activity" do
553
- let(:activity_serialization) { "2014-01-01: Snorkeling with Betsy." }
554
- let(:activity_description) { "Snorkeling with **Betsy Ross**." }
555
- subject { introvert.add_activity(serialization: activity_serialization) }
556
-
557
- it "adds the given activity" do
558
- stub_friends(friends) do
559
- subject
560
- introvert.activities.first.description.must_equal activity_description
561
- end
562
- end
563
-
564
- it "adds the activity after others on the same day" do
565
- stub_friends(friends) do
566
- introvert.add_activity(serialization: "2014-01-01: Ate breakfast.")
567
- subject
568
- introvert.activities.first.description.must_equal activity_description
569
- end
570
- end
571
-
572
- it "returns the activity added" do
573
- stub_friends(friends) do
574
- subject.description.must_equal activity_description
575
- end
576
- end
577
- end
578
-
579
- describe "#rename_friend" do
580
- let(:new_name) { "David Bowie" }
581
- subject do
582
- introvert.rename_friend(old_name: friend_names.last, new_name: new_name)
583
- end
584
-
585
- it "replaces old name within activities to the new name" do
586
- stub_friends(friends) do
587
- stub_activities(activities) do
588
- subject
589
- introvert.activities.first.description.must_include new_name
590
- introvert.activities.last.description.must_include new_name
591
- end
592
- end
593
- end
594
- end
595
-
596
- describe "#rename_location" do
597
- subject do
598
- introvert.rename_location(old_name: old_name, new_name: new_name)
599
- end
600
- let(:old_name) { "Paris" }
601
- let(:new_name) { "Paris, France" }
602
-
603
- let(:activities) do
604
- [
605
- Friends::Activity.new(str: "Dining in _Paris_."),
606
- Friends::Activity.new(str: "Falling in love in _Paris_."),
607
- Friends::Activity.new(str: "Swimming near _Atlantis_.")
608
- ]
609
- end
610
- let(:locations) do
611
- [
612
- Friends::Location.new(name: "Paris"),
613
- Friends::Location.new(name: "Atlantis")
614
- ]
615
- end
616
-
617
- it "replaces old name within activities to the new name" do
618
- stub_locations(locations) do
619
- stub_activities(activities) do
620
- subject
621
- introvert.activities.map do |activity|
622
- activity.description.include? new_name
623
- end.must_equal [true, true, false]
624
- end
625
- end
626
- end
627
-
628
- describe "when there are friends at the location" do
629
- let(:friends) do
630
- [
631
- Friends::Friend.new(name: "Jacques Cousteau", location_name: "Paris"),
632
- Friends::Friend.new(name: "Marie Antoinette", location_name: "Paris"),
633
- Friends::Friend.new(name: "Julius Caesar", location_name: "Rome")
634
- ]
635
- end
636
-
637
- it "updates their locations" do
638
- stub_locations(locations) do
639
- stub_friends(friends) do
640
- subject
641
- introvert.friends.map do |friend|
642
- friend.location_name == new_name
643
- end.must_equal [true, true, false]
644
- end
645
- end
646
- end
647
- end
648
- end
649
-
650
- describe "#set_location" do
651
- subject do
652
- introvert.set_location(
653
- name: friend_names.first,
654
- location_name: locations.first.name
655
- )
656
- end
657
-
658
- it "returns the modified friend" do
659
- stub_friends(friends) do
660
- stub_locations(locations) do
661
- subject.must_equal friends.first
662
- end
663
- end
664
- end
665
- end
666
-
667
- describe "#add_nickname" do
668
- subject do
669
- introvert.add_nickname(name: friend_names.first, nickname: "The Dude")
670
- end
671
-
672
- it "returns the modified friend" do
673
- stub_friends(friends) do
674
- subject.must_equal friends.first
675
- end
676
- end
677
- end
678
-
679
- describe "#remove_nickname" do
680
- subject do
681
- introvert.remove_nickname(name: "Jeff", nickname: "The Dude")
682
- end
683
-
684
- it "returns the modified friend" do
685
- friend = Friends::Friend.new(name: "Jeff", nickname_str: "The Dude")
686
- stub_friends([friend]) do
687
- subject.must_equal friend
688
- end
689
- end
690
- end
691
-
692
- describe "#add_tag" do
693
- subject do
694
- introvert.add_tag(name: friend_names.first, tag: "@school")
695
- end
696
-
697
- it "returns the modified friend" do
698
- stub_friends(friends) do
699
- subject.must_equal friends.first
700
- end
701
- end
702
-
703
- describe "when more than one friend name matches" do
704
- let(:friend_names) { ["George Washington Carver", "George Washington"] }
705
-
706
- describe "when one friend name matches exactly" do
707
- it "returns the modified friend" do
708
- stub_friends(friends) do
709
- subject.must_equal friends.first
710
- end
711
- end
712
- end
713
-
714
- describe "when no friend name matches exactly" do
715
- it "raises an error" do
716
- proc do
717
- stub_friends(friends) do
718
- introvert.add_tag(name: "George", tag: "@school")
719
- end
720
- end.must_raise Friends::FriendsError
721
- end
722
- end
723
- end
724
- end
725
-
726
- describe "#remove_tag" do
727
- subject do
728
- introvert.remove_tag(name: "Jeff", tag: "@school")
729
- end
730
-
731
- it "returns the modified friend" do
732
- friend = Friends::Friend.new(name: "Jeff", tags_str: "@school")
733
- stub_friends([friend]) do
734
- subject.must_equal friend
735
- end
736
- end
737
- end
738
-
739
- describe "#list_favorite_friends" do
740
- subject { introvert.list_favorite_friends(limit: limit) }
741
-
742
- describe "when there are more friends than favorites requested" do
743
- let(:limit) { 1 }
744
-
745
- it "returns the number of favorites requested" do
746
- stub_friends(friends) do
747
- stub_activities(activities) do
748
- subject.must_equal ["Betsy Ross (2 activities)"]
749
- end
750
- end
751
- end
752
- end
753
- end
754
-
755
- describe "#list_favorite_locations" do
756
- subject { introvert.list_favorite_locations(limit: limit) }
757
-
758
- describe "when there are more locations than favorites requested" do
759
- let(:limit) { 1 }
760
-
761
- it "returns the number of favorites requested" do
762
- stub_locations(locations) do
763
- stub_activities(
764
- [Friends::Activity.new(str: "Swimming in _Atlantis_.")]
765
- ) do
766
- subject.must_equal ["Atlantis (1 activity)"]
767
- end
768
- end
769
- end
770
- end
771
- end
772
-
773
- describe "#suggest" do
774
- subject { introvert.suggest(location_name: location_name) }
775
-
776
- describe "when no location name is passed" do
777
- let(:location_name) { nil }
778
-
779
- it "returns distant, moderate, and close friends" do
780
- stub_friends(friends) do
781
- stub_activities(activities) do
782
- subject.must_equal(
783
- distant: ["George Washington Carver"],
784
- moderate: [],
785
- close: ["Betsy Ross"]
786
- )
787
- end
788
- end
789
- end
790
-
791
- it "doesn't choke when there are no friends" do
792
- stub_friends([]) do
793
- stub_activities([]) do
794
- subject.must_equal(
795
- distant: [],
796
- moderate: [],
797
- close: []
798
- )
799
- end
800
- end
801
- end
802
- end
803
-
804
- describe "when a location name is passed" do
805
- let(:location_name) { "USA" }
806
-
807
- it "returns distant, moderate, and close friends" do
808
- friends.first.location_name = location_name
809
- stub_friends(friends) do
810
- stub_activities(activities) do
811
- subject.must_equal(
812
- distant: ["George Washington Carver"],
813
- moderate: [],
814
- close: []
815
- )
816
- end
817
- end
818
- end
819
-
820
- it "doesn't choke when there are no friends" do
821
- stub_friends([]) do
822
- stub_activities([]) do
823
- subject.must_equal(
824
- distant: [],
825
- moderate: [],
826
- close: []
827
- )
828
- end
829
- end
830
- end
831
- end
832
- end
833
-
834
- describe "#graph" do
835
- subject do
836
- introvert.graph(
837
- with: with,
838
- location_name: location_name,
839
- tagged: tagged
840
- )
841
- end
842
-
843
- let(:with) { nil }
844
- let(:location_name) { nil }
845
- let(:tagged) { nil }
846
-
847
- let(:activities) do
848
- [
849
- # In a location
850
- Friends::Activity.new(
851
- str: "2016-01-01: \
852
- At _The Eiffel Tower_ with **George Washington Carver**."
853
- ),
854
-
855
- # Tagged
856
- Friends::Activity.new(
857
- str: "2016-01-01: Called **George Washington Carver**. @phone"
858
- ),
859
-
860
- # With a friend
861
- Friends::Activity.new(
862
- str: "2016-01-01: Hung out with **Betsy Ross**."
863
- )
864
- ]
865
- end
866
-
867
- it "graphs activities by month" do
868
- stub_activities(activities) do
869
- subject.must_equal("Jan 2016" => 3)
870
- end
871
- end
872
-
873
- describe "No activities" do
874
- it "returns an empty graph" do
875
- subject.must_equal({})
876
- end
877
- end
878
-
879
- describe "Filtering by friend" do
880
- let(:with) { "Betsy Ross" }
881
-
882
- it "graphs activities with a friend" do
883
- stub_friends(friends) do
884
- stub_activities(activities) do
885
- subject.must_equal("Jan 2016" => 1)
886
- end
887
- end
888
- end
889
-
890
- describe "when the friend does not exist" do
891
- let(:with) { "Oscar the Grouch" }
892
-
893
- it "raises an error" do
894
- stub_activities(activities) do
895
- proc { subject }.must_raise Friends::FriendsError
896
- end
897
- end
898
- end
899
- end
900
-
901
- describe "Filtering by location" do
902
- let(:location_name) { "The Eiffel Tower" }
903
-
904
- it "graphs activities in a location" do
905
- stub_locations(locations) do
906
- stub_activities(activities) do
907
- subject.must_equal("Jan 2016" => 1)
908
- end
909
- end
910
- end
911
-
912
- describe "when the location does not exist" do
913
- let(:location_name) { "Nowhere" }
914
-
915
- it "raises an error" do
916
- stub_activities(activities) do
917
- proc { subject }.must_raise Friends::FriendsError
918
- end
919
- end
920
- end
921
- end
922
-
923
- describe "Filtering by tag" do
924
- let(:tagged) { "@phone" }
925
-
926
- it "graphs tagged activities" do
927
- stub_activities(activities) do
928
- subject.must_equal("Jan 2016" => 1)
929
- end
930
- end
931
- end
932
- end
933
-
934
- describe "#total_friends" do
935
- it "returns 0 when there are no friends" do
936
- introvert.total_friends.must_equal 0
937
- end
938
-
939
- it "returns the total number of friends" do
940
- stub_friends(friends) do
941
- introvert.total_friends.must_equal friends.size
942
- end
943
- end
944
- end
945
-
946
- describe "#total_activities" do
947
- it "returns 0 when there are no activities" do
948
- introvert.total_activities.must_equal 0
949
- end
950
-
951
- it "returns the total number of activities" do
952
- stub_activities(activities) do
953
- introvert.total_activities.must_equal activities.size
954
- end
955
- end
956
- end
957
-
958
- describe "#elapsed_days" do
959
- it "return 0 elapsed days when there are no activities" do
960
- introvert.elapsed_days.must_equal 0
961
- end
962
-
963
- it "returns the number of days between the first and last activity" do
964
- stub_activities(activities) do
965
- introvert.elapsed_days.must_equal 1
966
- end
967
- end
968
- end
969
- end