friends 0.28 → 0.29

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