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