friends 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +8 -0
- data/bin/friends +27 -16
- data/lib/friends/activity.rb +6 -0
- data/lib/friends/introvert.rb +54 -13
- data/lib/friends/version.rb +1 -1
- data/test/activity_spec.rb +20 -0
- data/test/introvert_spec.rb +74 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 788d17a21f2929e7936d68922b971ffcc907a8c4
|
4
|
+
data.tar.gz: f710eec4282c863b8e51d1d556478e3b23622aad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1de80c141a69d80ed9ce39f7629f0563a7bdf9efc48617036eaf410935b2b7f8b5bd4f08f60e5965462aea610f570882958024962e05fcb43e0e23509574116
|
7
|
+
data.tar.gz: 25b629ba4fd48f298c38ca17cb351f548001dd6320e0987a560dc29a6197605c15425f8bcd83ebe52a3cb18a871e79643b826ba4cab29e7c9ef92e8da96fed29
|
data/README.md
CHANGED
@@ -87,6 +87,14 @@ Your favorite friends:
|
|
87
87
|
1. George Washington Carver (2 activities)
|
88
88
|
2. Grace Hopper (1)
|
89
89
|
```
|
90
|
+
Graph your relationship with a friend over time:
|
91
|
+
```
|
92
|
+
$ friends graph "George"
|
93
|
+
Nov 2014 |█
|
94
|
+
Dec 2014 |
|
95
|
+
Jan 2015 |█████
|
96
|
+
Feb 2015 |███
|
97
|
+
```
|
90
98
|
|
91
99
|
### Global options:
|
92
100
|
|
data/bin/friends
CHANGED
@@ -29,16 +29,16 @@ switch [:clean],
|
|
29
29
|
desc: "Force a clean write of the friends file"
|
30
30
|
|
31
31
|
desc "Lists friends or activities"
|
32
|
-
command :list do |
|
33
|
-
|
34
|
-
|
32
|
+
command :list do |list|
|
33
|
+
list.desc "List all friends"
|
34
|
+
list.command :friends do |list_friends|
|
35
35
|
list_friends.action do
|
36
36
|
puts @introvert.list_friends
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
list.desc "List favorite friends"
|
41
|
+
list.command :favorites do |list_favorites|
|
42
42
|
list_favorites.flag [:limit],
|
43
43
|
arg_name: "NUMBER",
|
44
44
|
default_value: 10,
|
@@ -61,8 +61,8 @@ command :list do |c|
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
|
65
|
-
|
64
|
+
list.desc "Lists all activities"
|
65
|
+
list.command :activities do |list_activities|
|
66
66
|
list_activities.flag [:limit],
|
67
67
|
arg_name: "NUMBER",
|
68
68
|
default_value: 10,
|
@@ -80,19 +80,19 @@ command :list do |c|
|
|
80
80
|
end
|
81
81
|
|
82
82
|
desc "Adds a friend or activity"
|
83
|
-
command :add do |
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
command :add do |add|
|
84
|
+
add.desc "Adds a friend"
|
85
|
+
add.arg_name "NAME"
|
86
|
+
add.command :friend do |add_friend|
|
87
87
|
add_friend.action do |_, _, args|
|
88
88
|
friend = @introvert.add_friend(name: args.first)
|
89
89
|
@message = "Friend added: \"#{friend.name}\""
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
-
|
94
|
-
|
95
|
-
|
93
|
+
add.desc "Adds an activity"
|
94
|
+
add.arg_name "DESCRIPTION"
|
95
|
+
add.command :activity do |add_activity|
|
96
96
|
add_activity.action do |_, _, args|
|
97
97
|
activity = @introvert.add_activity(serialization: args.first)
|
98
98
|
@message = "Activity added: \"#{activity.display_text}\""
|
@@ -100,9 +100,20 @@ command :add do |c|
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
+
desc "Graph"
|
104
|
+
arg_name "NAME"
|
105
|
+
command :graph do |graph|
|
106
|
+
graph.action do |_, _, args|
|
107
|
+
data = @introvert.graph(name: args.first)
|
108
|
+
data.each do |month, count|
|
109
|
+
puts "#{month} |#{'█' * count}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
103
114
|
desc "Cleans your friends.md file"
|
104
|
-
command :clean do |
|
105
|
-
|
115
|
+
command :clean do |clean|
|
116
|
+
clean.action do
|
106
117
|
filename = @introvert.clean
|
107
118
|
@message = "File cleaned: \"#{filename}\""
|
108
119
|
end
|
data/lib/friends/activity.rb
CHANGED
@@ -89,6 +89,12 @@ module Friends
|
|
89
89
|
@description = new_description
|
90
90
|
end
|
91
91
|
|
92
|
+
# @param friend [Friend] the friend to test
|
93
|
+
# @return [Boolean] true iff this activity includes the given friend
|
94
|
+
def includes_friend?(friend:)
|
95
|
+
friend_names.include? friend.name
|
96
|
+
end
|
97
|
+
|
92
98
|
# Find the names of all friends in this description.
|
93
99
|
# @return [Array] list of all friend names in the description
|
94
100
|
def friend_names
|
data/lib/friends/introvert.rb
CHANGED
@@ -11,6 +11,7 @@ module Friends
|
|
11
11
|
DEFAULT_FILENAME = "./friends.md"
|
12
12
|
ACTIVITIES_HEADER = "### Activities:"
|
13
13
|
FRIENDS_HEADER = "### Friends:"
|
14
|
+
GRAPH_DATE_FORMAT = "%b %Y" # Used as the param for date.strftime().
|
14
15
|
|
15
16
|
# @param filename [String] the name of the friends Markdown file
|
16
17
|
def initialize(filename: DEFAULT_FILENAME)
|
@@ -131,18 +132,8 @@ module Friends
|
|
131
132
|
|
132
133
|
# Filter by friend name if argument is passed.
|
133
134
|
unless with.nil?
|
134
|
-
|
135
|
-
|
136
|
-
case friends.size
|
137
|
-
when 1
|
138
|
-
# If exactly one friend matches, use that friend to filter.
|
139
|
-
acts = acts.select { |a| a.friend_names.include? friends.first.name }
|
140
|
-
when 0 then raise FriendsError, "No friend found for \"#{with}\""
|
141
|
-
else
|
142
|
-
raise FriendsError,
|
143
|
-
"More than one friend found for \"#{with}\": "\
|
144
|
-
"#{friends.map(&:name).join(', ')}"
|
145
|
-
end
|
135
|
+
friend = friend_with_name_in(with)
|
136
|
+
acts = acts.select { |act| act.includes_friend?(friend: friend) }
|
146
137
|
end
|
147
138
|
|
148
139
|
# If we need to, trim the list.
|
@@ -151,6 +142,38 @@ module Friends
|
|
151
142
|
acts.map(&:display_text)
|
152
143
|
end
|
153
144
|
|
145
|
+
# Find data points for graphing a given friend's relationship over time.
|
146
|
+
# @param name [String] the name of the friend to use
|
147
|
+
# @return [Hash] with the following format:
|
148
|
+
# {
|
149
|
+
# "Jan 2015" => 3, # The month and number of activities with that
|
150
|
+
# "Feb 2015" => 0, # friend during that month.
|
151
|
+
# "Mar 2015" => 9
|
152
|
+
# }
|
153
|
+
# The keys of the hash are all of the months (inclusive) between the first
|
154
|
+
# and last month in which activities for the given friend have been
|
155
|
+
# recorded.
|
156
|
+
def graph(name:)
|
157
|
+
friend = friend_with_name_in(name) # Find the friend by name.
|
158
|
+
|
159
|
+
# Filter out activities that don't include the given friend.
|
160
|
+
acts = activities.select { |act| act.includes_friend?(friend: friend) }
|
161
|
+
|
162
|
+
# Initialize the table of activities to have all of the months of that
|
163
|
+
# friend's activity range (including months in the middle of the range
|
164
|
+
# with no relevant activities).
|
165
|
+
act_table = {}
|
166
|
+
(acts.last.date..acts.first.date).each do |date|
|
167
|
+
act_table[date.strftime(GRAPH_DATE_FORMAT)] = 0
|
168
|
+
end
|
169
|
+
|
170
|
+
acts.each do |activity|
|
171
|
+
month = activity.date.strftime(GRAPH_DATE_FORMAT)
|
172
|
+
act_table[month] += 1
|
173
|
+
end
|
174
|
+
act_table
|
175
|
+
end
|
176
|
+
|
154
177
|
# Sets the n_activities field on each friend.
|
155
178
|
def set_n_activities!
|
156
179
|
# Construct a hash of friend name to frequency of appearance.
|
@@ -233,7 +256,25 @@ module Friends
|
|
233
256
|
end
|
234
257
|
end
|
235
258
|
|
236
|
-
# @param
|
259
|
+
# @param text [String] the name (or substring) of the friend to search for
|
260
|
+
# @return [Friend] the friend that matches
|
261
|
+
# @raise [FriendsError] if 0 of 2+ friends match the given text
|
262
|
+
def friend_with_name_in(text)
|
263
|
+
friends = friends_with_name_in(text)
|
264
|
+
|
265
|
+
case friends.size
|
266
|
+
when 1
|
267
|
+
# If exactly one friend matches, use that friend.
|
268
|
+
return friends.first
|
269
|
+
when 0 then raise FriendsError, "No friend found for \"#{text}\""
|
270
|
+
else
|
271
|
+
raise FriendsError,
|
272
|
+
"More than one friend found for \"#{text}\": "\
|
273
|
+
"#{friends.map(&:name).join(', ')}"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# @param text [String] the name (or substring) of the friends to search for
|
237
278
|
# @return [Array] a list of all friends that match the given text
|
238
279
|
def friends_with_name_in(text)
|
239
280
|
regex = Regexp.new(text, Regexp::IGNORECASE)
|
data/lib/friends/version.rb
CHANGED
data/test/activity_spec.rb
CHANGED
@@ -184,6 +184,26 @@ describe Friends::Activity do
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
+
describe "#includes_friend?" do
|
188
|
+
subject { activity.includes_friend?(friend: friend) }
|
189
|
+
|
190
|
+
describe "when the given friend is in the activity" do
|
191
|
+
let(:friend) { friend1 }
|
192
|
+
|
193
|
+
it "returns true" do
|
194
|
+
subject.must_equal true
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "when the given friend is not in the activity" do
|
199
|
+
let(:friend) { Friends::Friend.new(name: "Claude Debussy") }
|
200
|
+
|
201
|
+
it "returns false" do
|
202
|
+
subject.must_equal false
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
187
207
|
describe "#friend_names" do
|
188
208
|
subject { activity.friend_names }
|
189
209
|
|
data/test/introvert_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe Friends::Introvert do
|
|
14
14
|
"**#{friend_names.last}**."
|
15
15
|
),
|
16
16
|
Friends::Activity.new(
|
17
|
-
date_s: (Date.today
|
17
|
+
date_s: (Date.today - 1).to_s,
|
18
18
|
description: "Called **#{friend_names.last}**."
|
19
19
|
)
|
20
20
|
]
|
@@ -261,4 +261,77 @@ describe Friends::Introvert do
|
|
261
261
|
end
|
262
262
|
end
|
263
263
|
end
|
264
|
+
|
265
|
+
describe "#graph" do
|
266
|
+
subject { introvert.graph(name: friend_name) }
|
267
|
+
|
268
|
+
describe "when friend name is invalid" do
|
269
|
+
let(:friend_name) { "Oscar the Grouch" }
|
270
|
+
|
271
|
+
it "raises an error" do
|
272
|
+
proc { subject }.must_raise Friends::FriendsError
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe "when friend name has more than one match" do
|
277
|
+
let(:friend_name) { "e" }
|
278
|
+
|
279
|
+
it "raises an error" do
|
280
|
+
introvert.stub(:friends, friends) do
|
281
|
+
proc { subject }.must_raise Friends::FriendsError
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "when friend name is valid" do
|
287
|
+
let(:friend_name) { "George" }
|
288
|
+
let(:activities) do
|
289
|
+
[
|
290
|
+
Friends::Activity.new(
|
291
|
+
date_s: Date.today.to_s,
|
292
|
+
description: "Lunch with **George Washington Carver**."
|
293
|
+
),
|
294
|
+
|
295
|
+
# Create another activity with a gap of over a month between it and
|
296
|
+
# the next activity, so we can test that we correctly return data for
|
297
|
+
# months in the range with no activities.
|
298
|
+
Friends::Activity.new(
|
299
|
+
date_s: (Date.today - 70).to_s,
|
300
|
+
description: "Called **George Washington Carver**."
|
301
|
+
),
|
302
|
+
|
303
|
+
# Create an activity that doesn't involve our friend name.
|
304
|
+
Friends::Activity.new(
|
305
|
+
date_s: (Date.today - 150).to_s,
|
306
|
+
description: "Called **Betsy Ross** on the phone."
|
307
|
+
)
|
308
|
+
]
|
309
|
+
end
|
310
|
+
|
311
|
+
it "returns a hash of months and frequencies" do
|
312
|
+
introvert.stub(:friends, friends) do
|
313
|
+
introvert.stub(:activities, activities) do
|
314
|
+
strftime_format = Friends::Introvert::GRAPH_DATE_FORMAT
|
315
|
+
|
316
|
+
first = activities[0]
|
317
|
+
second = activities[1]
|
318
|
+
months = (second.date..first.date).map do |date|
|
319
|
+
date.strftime(strftime_format)
|
320
|
+
end.uniq
|
321
|
+
|
322
|
+
# Make sure all of the months are in our output data set (even the
|
323
|
+
# months with no relevant activity). This also checks that the
|
324
|
+
# irrelevant activity is not in our data set.
|
325
|
+
subject.keys.must_equal(months)
|
326
|
+
|
327
|
+
# Check that the activities for George are recorded, and that all
|
328
|
+
# other months have no activities recorded.
|
329
|
+
subject[first.date.strftime(strftime_format)].must_equal 1
|
330
|
+
subject[second.date.strftime(strftime_format)].must_equal 1
|
331
|
+
subject.values.inject(:+).must_equal 2
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
264
337
|
end
|