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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7d0669a8ff237217895bb3647d19c2c3619c455f
4
- data.tar.gz: b1df12a309fff583eef49f10c52175ec02b53cc1
3
+ metadata.gz: 788d17a21f2929e7936d68922b971ffcc907a8c4
4
+ data.tar.gz: f710eec4282c863b8e51d1d556478e3b23622aad
5
5
  SHA512:
6
- metadata.gz: 885edb6c2f561e23be58f81dec6cffcc22829cab66f0a3f3e482184dfb939f2391200cf52b2d870c68d00d198356f2c8da5a4645561759cccd945f369135d06c
7
- data.tar.gz: 00f795cfd5f597f83dfb9c1d0a77b04f95eca041b7a2b97ab4d15e2d03aebd908cd4c95e7ae245f779debbfaac5827de2dcf58b9a1dd32eae6e08b2dc904f35e
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
 
@@ -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 |c|
33
- c.desc "List all friends"
34
- c.command :friends do |list_friends|
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
- c.desc "List favorite friends"
41
- c.command :favorites do |list_favorites|
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
- c.desc "Lists all activities"
65
- c.command :activities do |list_activities|
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 |c|
84
- c.desc "Adds a friend"
85
- c.arg_name "NAME"
86
- c.command :friend do |add_friend|
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
- c.desc "Adds an activity"
94
- c.arg_name "DESCRIPTION"
95
- c.command :activity do |add_activity|
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 |c|
105
- c.action do
115
+ command :clean do |clean|
116
+ clean.action do
106
117
  filename = @introvert.clean
107
118
  @message = "File cleaned: \"#{filename}\""
108
119
  end
@@ -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
@@ -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
- friends = friends_with_name_in(with)
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 name [String] the name of the friends to search for
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)
@@ -1,3 +1,3 @@
1
1
  module Friends
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -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
 
@@ -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 + 1).to_s,
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friends
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Evelyn