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 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