friends 0.22 → 0.23

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: 66aa8129ccdd246b263b11bca7381c826e051d3b
4
- data.tar.gz: 186024855c83d91b0f42f29e5e09a9ca8dfab8c3
3
+ metadata.gz: 560b5f5b4af9d8762ecb97f2a0ffdbb0cd8be02e
4
+ data.tar.gz: 39338ddbdffe9c8c712bed9e5078d0cfc5c8877a
5
5
  SHA512:
6
- metadata.gz: 119e32928a14606556b7d0601e11d5223c3ebca405707d44a1a06e497c73918b13fd7a497c1aa1a4ac264344bd57b3007db06e52429a4f22922bf8f748d62be1
7
- data.tar.gz: 8a4c1483bb0d90cddbdf011978a2a94dd6ea7e1a6e8825cd37d9e51c17b6b7a4c4d766a8b36d812cb3ab061a1bb2463b3c936e0dfad65c77456ece09c19680ab
6
+ metadata.gz: d2349672e223cec6d4f08df3ffce8209b8190540eb51862c72354d9a36144c813232bc18327a8d04c0da0d62ad1101f1a0931858f029e8e9cf1c94ea433c7ae0
7
+ data.tar.gz: 3c833ed0d247ea59064335482a182b9ffbb094b172e5388370161baba6992e0f7f125d41fb967628b14e581b252039e17c7fbb3cac9a800fc0c9cf6945983e37
@@ -1,5 +1,17 @@
1
1
  # Change Log
2
2
 
3
+ ## [v0.23](https://github.com/JacobEvelyn/friends/tree/v0.23) (2016-05-16)
4
+ [Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.22...v0.23)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Change `graph` to use `--with`, `--tagged`, and `--in` flags [\#124](https://github.com/JacobEvelyn/friends/issues/124)
9
+ - Add `graph` command for locations [\#109](https://github.com/JacobEvelyn/friends/issues/109)
10
+
11
+ **Merged pull requests:**
12
+
13
+ - Allow graph to be filtered by friend, location and hashtag [\#129](https://github.com/JacobEvelyn/friends/pull/129) ([andypearson](https://github.com/andypearson))
14
+
3
15
  ## [v0.22](https://github.com/JacobEvelyn/friends/tree/v0.22) (2016-05-14)
4
16
  [Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.21...v0.22)
5
17
 
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gemspec
data/README.md CHANGED
@@ -318,24 +318,51 @@ Opening "./friends.md" in atom
318
318
 
319
319
  #### `graph`
320
320
 
321
- Graphs (in color!) your relationship with a given friend over time:
321
+ Graphs (in color!) your activities over time:
322
322
 
323
323
  ```bash
324
- $ friends graph George
324
+ $ friends graph
325
+ Nov 2014 |███
326
+ Dec 2014 |██
327
+ Jan 2015 |███████
328
+ Feb 2015 |█████
329
+ ```
330
+
331
+ Or graph only activities with a certain friend:
332
+
333
+ ```bash
334
+ $ friends graph --with George
325
335
  Nov 2014 |█
326
336
  Dec 2014 |
327
337
  Jan 2015 |█████
328
338
  Feb 2015 |███
329
339
  ```
330
340
 
331
- Or just graph all of your activities:
341
+ Or graph only activities with a certain hashtag:
332
342
 
333
343
  ```bash
334
- $ friends graph
335
- Nov 2014 |███
336
- Dec 2014 |██
337
- Jan 2015 |███████
338
- Feb 2015 |█████
344
+ $ friends graph --tagged food
345
+ Nov 2014 |█
346
+ Dec 2014 |
347
+ Jan 2015 |
348
+ Feb 2015 |███
349
+ ```
350
+
351
+ Or graph only activities in a certain location:
352
+
353
+ ```bash
354
+ $ friends graph --in Paris
355
+ Nov 2014 |█
356
+ Dec 2014 |
357
+ Jan 2015 |
358
+ Feb 2015 |█
359
+ ```
360
+
361
+ And you can use multiple of these flags together:
362
+
363
+ ```bash
364
+ $ friends graph --in Paris --tagged food --with George
365
+ Nov 2014 |█
339
366
  ```
340
367
 
341
368
  #### `help`
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rake/testtask"
3
5
 
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "gli"
4
5
  require "paint"
@@ -27,7 +28,7 @@ class Hashtag
27
28
  # See: https://github.com/davetron5000/gli/issues/241
28
29
  def self.convert_to_hashtag(str)
29
30
  str = str.strip
30
- str.size > 0 && str[0] == "#" ? str : "##{str}"
31
+ !str.empty? && str[0] == "#" ? str : "##{str}"
31
32
  end
32
33
  end
33
34
 
@@ -35,10 +36,8 @@ accept(Hashtag) do |value|
35
36
  Hashtag.convert_to_hashtag(value)
36
37
  end
37
38
 
38
- class Stripped ; end
39
- accept(Stripped) do |value|
40
- value.strip
41
- end
39
+ class Stripped; end
40
+ accept(Stripped, &:strip)
42
41
 
43
42
  switch [:quiet],
44
43
  negatable: false,
@@ -320,10 +319,24 @@ command :remove do |remove|
320
319
  end
321
320
  end
322
321
 
323
- desc "Graph all activities or a friend's relationship over time"
324
- arg_name "NAME (default: none)"
322
+ desc "Graph activities over time"
325
323
  command :graph do |graph|
326
- graph.action do |_, _, args|
324
+ graph.flag [:with],
325
+ arg_name: "NAME",
326
+ desc: "Graph activities with the given friend",
327
+ type: Stripped
328
+
329
+ graph.flag [:in],
330
+ arg_name: "LOCATION",
331
+ desc: "Graph activities in the given location",
332
+ type: Stripped
333
+
334
+ graph.flag [:tagged],
335
+ arg_name: "HASHTAG",
336
+ desc: "Graph activities with the given hashtag",
337
+ type: Hashtag
338
+
339
+ graph.action do |_, options|
327
340
  # This math is taken from Minitest's Pride plugin (the PrideLOL class).
328
341
  PI_3 = Math::PI / 3
329
342
 
@@ -336,7 +349,11 @@ command :graph do |graph|
336
349
  [r, g, b].map { |c| c * 51 }
337
350
  end
338
351
 
339
- data = @introvert.graph(name: args.first)
352
+ data = @introvert.graph(
353
+ with: options[:with],
354
+ location_name: options[:in],
355
+ tagged: options[:tagged]
356
+ )
340
357
 
341
358
  data.each do |month, count|
342
359
  print "#{month} |"
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  lib = File.expand_path("../lib", __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require "friends/version"
@@ -33,5 +34,5 @@ Gem::Specification.new do |spec|
33
34
  spec.add_development_dependency "minitest", "~> 5.5"
34
35
  spec.add_development_dependency "overcommit", "~> 0.30"
35
36
  spec.add_development_dependency "rake", "~> 10.0"
36
- spec.add_development_dependency "rubocop", "~> 0.36"
37
+ spec.add_development_dependency "rubocop", "~> 0.40"
37
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "friends/introvert"
2
4
  require "friends/version"
3
5
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Activity represents an activity you've done with one or more Friends.
3
4
 
4
5
  require "chronic"
@@ -12,8 +13,8 @@ module Friends
12
13
  extend Serializable
13
14
  extend Memoist
14
15
 
15
- SERIALIZATION_PREFIX = "- ".freeze
16
- DATE_PARTITION = ": ".freeze
16
+ SERIALIZATION_PREFIX = "- "
17
+ DATE_PARTITION = ": "
17
18
 
18
19
  # @return [Regexp] the regex for capturing groups in deserialization
19
20
  def self.deserialization_regex
@@ -97,7 +98,7 @@ module Friends
97
98
  # @return [String] if name found in description
98
99
  # @return [nil] if no change was made
99
100
  def update_friend_name(old_name:, new_name:)
100
- @description.gsub!(
101
+ @description = @description.gsub(
101
102
  Regexp.new("(?<=\\*\\*)#{old_name}(?=\\*\\*)"),
102
103
  new_name
103
104
  )
@@ -109,7 +110,10 @@ module Friends
109
110
  # @return [String] if name found in description
110
111
  # @return [nil] if no change was made
111
112
  def update_location_name(old_name:, new_name:)
112
- @description.gsub!(Regexp.new("(?<=_)#{old_name}(?=_)"), new_name)
113
+ @description = @description.gsub(
114
+ Regexp.new("(?<=_)#{old_name}(?=_)"),
115
+ new_name
116
+ )
113
117
  end
114
118
 
115
119
  # @param location [Location] the location to test
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Friend represents a friend. You know, a real-life friend!
3
4
 
4
5
  require "friends"
@@ -9,8 +10,8 @@ module Friends
9
10
  class Friend
10
11
  extend Serializable
11
12
 
12
- SERIALIZATION_PREFIX = "- ".freeze
13
- NICKNAME_PREFIX = "a.k.a. ".freeze
13
+ SERIALIZATION_PREFIX = "- "
14
+ NICKNAME_PREFIX = "a.k.a. "
14
15
 
15
16
  # @return [Regexp] the regex for capturing groups in deserialization
16
17
  def self.deserialization_regex
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Friends
2
4
  class FriendsError < StandardError
3
5
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ # Graphs activities by month
3
+
4
+ module Friends
5
+ class Graph
6
+ DATE_FORMAT = "%b %Y"
7
+
8
+ # @param start_date [Date] the first month of the graph
9
+ # @param end_date [Date] the last month of the graph
10
+ # @param activities [Array<Friends::Activity>] a list of activities to graph
11
+ def initialize(start_date:, end_date:, activities:)
12
+ self.start_date = start_date
13
+ self.end_date = end_date
14
+ self.activities = activities
15
+ end
16
+
17
+ # Render the graph as a hash in the format:
18
+ #
19
+ # {
20
+ # "Jan 2015" => 3, # The number of activities during each month.
21
+ # "Feb 2015" => 0,
22
+ # "Mar 2015" => 9
23
+ # }
24
+ #
25
+ # @return [Hash{String => Integer}]
26
+ def to_h
27
+ empty_graph.tap do |graph|
28
+ activities.each do |activity|
29
+ graph[format_date(activity.date)] += 1
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_accessor :start_date, :end_date, :activities
37
+
38
+ # Render an empty graph as a hash in the format:
39
+ #
40
+ # {
41
+ # "Jan 2015" => 0,
42
+ # "Feb 2015" => 0,
43
+ # "Mar 2015" => 0
44
+ # }
45
+ #
46
+ # @return [Hash{String => Integer}]
47
+ def empty_graph
48
+ Hash[(start_date..end_date).map do |date|
49
+ [format_date(date), 0]
50
+ end]
51
+ end
52
+
53
+ # Format a date for use in the graph label
54
+ # @param date [Date] the date to format
55
+ # @return [String]
56
+ def format_date(date)
57
+ date.strftime(DATE_FORMAT)
58
+ end
59
+ end
60
+ end
@@ -1,20 +1,21 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Introvert is the internal handler for the friends script. It is designed to be
3
4
  # able to be used directly within another Ruby program, without needing to call
4
5
  # the command-line script explicitly.
5
6
 
6
7
  require "friends/activity"
7
8
  require "friends/friend"
9
+ require "friends/graph"
8
10
  require "friends/location"
9
11
  require "friends/friends_error"
10
12
 
11
13
  module Friends
12
14
  class Introvert
13
- DEFAULT_FILENAME = "./friends.md".freeze
14
- ACTIVITIES_HEADER = "### Activities:".freeze
15
- FRIENDS_HEADER = "### Friends:".freeze
16
- LOCATIONS_HEADER = "### Locations:".freeze
17
- GRAPH_DATE_FORMAT = "%b %Y".freeze # Used as the param for date.strftime().
15
+ DEFAULT_FILENAME = "./friends.md"
16
+ ACTIVITIES_HEADER = "### Activities:"
17
+ FRIENDS_HEADER = "### Friends:"
18
+ LOCATIONS_HEADER = "### Locations:"
18
19
 
19
20
  # @param filename [String] the name of the friends Markdown file
20
21
  def initialize(filename: DEFAULT_FILENAME)
@@ -241,26 +242,14 @@ module Friends
241
242
  # @param tagged [String] the name of a hashtag to filter by, or nil for
242
243
  # unfiltered
243
244
  # @return [Array] a list of all activity text values
244
- # @raise [FriendsError] if 0 or 2+ friends match the given `with` text
245
+ # @raise [FriendsError] if friend, location or hashtag cannot be found or
246
+ # is ambiguous
245
247
  def list_activities(limit:, with:, location_name:, tagged:)
246
- acts = @activities
247
-
248
- # Filter by friend name if argument is passed.
249
- unless with.nil?
250
- friend = friend_with_name_in(with)
251
- acts = acts.select { |act| act.includes_friend?(friend: friend) }
252
- end
253
-
254
- # Filter by location name if argument is passed.
255
- unless location_name.nil?
256
- location = location_with_name_in(location_name)
257
- acts = acts.select { |act| act.includes_location?(location: location) }
258
- end
259
-
260
- # Filter by tag if argument is passed.
261
- unless tagged.nil?
262
- acts = acts.select { |act| act.includes_hashtag?(hashtag: tagged) }
263
- end
248
+ acts = filtered_activities(
249
+ with: with,
250
+ location_name: location_name,
251
+ tagged: tagged
252
+ )
264
253
 
265
254
  # If we need to, trim the list.
266
255
  acts = acts.take(limit) unless limit.nil?
@@ -297,7 +286,7 @@ module Friends
297
286
  end
298
287
 
299
288
  # Find data points for graphing activities over time.
300
- # Optionally filter by a friend to see a given relationship over time.
289
+ # Optionally filter by friend, location and hashtag
301
290
  #
302
291
  # The returned hash uses the following format:
303
292
  # {
@@ -308,32 +297,30 @@ module Friends
308
297
  # The keys of the hash are all of the months (inclusive) between the first
309
298
  # and last month in which activities have been recorded.
310
299
  #
311
- # @param name [String] the name of the friend to use
312
- # @return [Hash{String => Fixnum}]
313
- # @raise [FriendsError] if 0 or 2+ friends match the given name
314
- def graph(name: nil)
315
- if name
316
- friend = friend_with_name_in(name) # Find the friend by name.
317
-
318
- # Filter out activities that don't include the given friend.
319
- acts = @activities.select { |act| act.includes_friend?(friend: friend) }
320
- else
321
- acts = @activities
322
- end
323
-
324
- # Initialize the table of activities to have all of the months of that
325
- # friend's activity range (including months in the middle of the range
326
- # with no relevant activities).
327
- act_table = {}
328
- (acts.last.date..acts.first.date).each do |date|
329
- act_table[date.strftime(GRAPH_DATE_FORMAT)] = 0
330
- end
331
-
332
- acts.each do |activity|
333
- month = activity.date.strftime(GRAPH_DATE_FORMAT)
334
- act_table[month] += 1
335
- end
336
- act_table
300
+ # @param with [String] the name of a friend to filter by, or nil for
301
+ # unfiltered
302
+ # @param location_name [String] the name of a location to filter by, or nil
303
+ # for unfiltered
304
+ # @param tagged [String] the name of a hashtag to filter by, or nil for
305
+ # unfiltered
306
+ # @return [Hash{String => Integer}]
307
+ # @raise [FriendsError] if friend, location or hashtag cannot be found or
308
+ # is ambiguous
309
+ def graph(with:, location_name:, tagged:)
310
+ # There is no point trying to graph no activities
311
+ return {} if @activities.empty?
312
+
313
+ activities_to_graph = filtered_activities(
314
+ with: with,
315
+ location_name: location_name,
316
+ tagged: tagged
317
+ )
318
+
319
+ Graph.new(
320
+ start_date: @activities.last.date,
321
+ end_date: @activities.first.date,
322
+ activities: activities_to_graph
323
+ ).to_h
337
324
  end
338
325
 
339
326
  # Suggest friends to do something with.
@@ -476,6 +463,39 @@ module Friends
476
463
 
477
464
  private
478
465
 
466
+ # Filter activities by friend, location and hashtag
467
+ # @param with [String] the name of a friend to filter by, or nil for
468
+ # unfiltered
469
+ # @param location_name [String] the name of a location to filter by, or nil
470
+ # for unfiltered
471
+ # @param tagged [String] the name of a hashtag to filter by, or nil for
472
+ # unfiltered
473
+ # @return [Array] an array of activities
474
+ # @raise [FriendsError] if friend, location or hashtag cannot be found or
475
+ # is ambiguous
476
+ def filtered_activities(with:, location_name:, tagged:)
477
+ acts = @activities
478
+
479
+ # Filter by friend name if argument is passed.
480
+ unless with.nil?
481
+ friend = friend_with_name_in(with)
482
+ acts = acts.select { |act| act.includes_friend?(friend: friend) }
483
+ end
484
+
485
+ # Filter by location name if argument is passed.
486
+ unless location_name.nil?
487
+ location = location_with_name_in(location_name)
488
+ acts = acts.select { |act| act.includes_location?(location: location) }
489
+ end
490
+
491
+ # Filter by tag if argument is passed.
492
+ unless tagged.nil?
493
+ acts = acts.select { |act| act.includes_hashtag?(hashtag: tagged) }
494
+ end
495
+
496
+ acts
497
+ end
498
+
479
499
  # @param type [Symbol] one of: [:friend, :location]
480
500
  # @param limit [Integer] the number of favorite things to return
481
501
  # @return [Array] a list of the favorite things' names and activity counts
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Location represents a location in the world.
3
4
 
4
5
  require "friends/regex_builder"
@@ -8,7 +9,7 @@ module Friends
8
9
  class Location
9
10
  extend Serializable
10
11
 
11
- SERIALIZATION_PREFIX = "- ".freeze
12
+ SERIALIZATION_PREFIX = "- "
12
13
 
13
14
  # @return [Regexp] the regex for capturing groups in deserialization
14
15
  def self.deserialization_regex
@@ -1,30 +1,31 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # RegexpBuilder is an internal class that helps us build regexes to find things.
3
4
 
4
5
  module Friends
5
6
  class RegexBuilder
6
7
  # We don't want to match strings that are "escaped" with a leading
7
8
  # backslash.
8
- NO_LEADING_BACKSLASH = "(?<!\\\\)".freeze
9
+ NO_LEADING_BACKSLASH = "(?<!\\\\)"
9
10
 
10
11
  # We don't want to match strings that are directly touching double asterisks
11
12
  # as these are treated as sacred by our program.
12
- NO_LEADING_ASTERISKS = "(?<!\\*\\*)".freeze
13
- NO_TRAILING_ASTERISKS = "(?!\\*\\*)".freeze
13
+ NO_LEADING_ASTERISKS = "(?<!\\*\\*)"
14
+ NO_TRAILING_ASTERISKS = "(?!\\*\\*)"
14
15
 
15
16
  # We don't want to match strings that are directly touching underscores
16
17
  # as these are treated as sacred by our program.
17
- NO_LEADING_UNDERSCORES = "(?<!_)".freeze
18
- NO_TRAILING_UNDERSCORES = "(?!_)".freeze
18
+ NO_LEADING_UNDERSCORES = "(?<!_)"
19
+ NO_TRAILING_UNDERSCORES = "(?!_)"
19
20
 
20
21
  # We don't want to match strings that are part of other words.
21
- NO_LEADING_ALPHABETICALS = "(?<![A-z])".freeze
22
- NO_TRAILING_ALPHABETICALS = "(?![A-z])".freeze
22
+ NO_LEADING_ALPHABETICALS = "(?<![A-z])"
23
+ NO_TRAILING_ALPHABETICALS = "(?![A-z])"
23
24
 
24
25
  # We ignore case within the regex as opposed to globally to allow consumers
25
26
  # of this API the ability to pass in strings that turn off this modifier
26
27
  # with the "(?-i)" string.
27
- IGNORE_CASE = "(?i)".freeze
28
+ IGNORE_CASE = "(?i)"
28
29
 
29
30
  def self.regex(str)
30
31
  Regexp.new(
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Serializable provides functionality around serialization to the classes that
2
4
  # extend it. This includes a class method to deserialize a string and create an
3
5
  # instance of the class.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Friends
3
- VERSION = "0.22".freeze
4
+ VERSION = "0.23"
4
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "./test/helper"
2
4
 
3
5
  describe Friends::Activity do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "./test/helper"
2
4
 
3
5
  describe Friends::Friend do
@@ -0,0 +1,92 @@
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "codeclimate-test-reporter"
2
4
  CodeClimate::TestReporter.start
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "./test/helper"
2
4
 
3
5
  describe Friends::Introvert do
@@ -804,82 +806,103 @@ describe Friends::Introvert do
804
806
  end
805
807
 
806
808
  describe "#graph" do
807
- subject { introvert.graph(name: friend_name) }
809
+ subject do
810
+ introvert.graph(
811
+ with: with,
812
+ location_name: location_name,
813
+ tagged: tagged
814
+ )
815
+ end
816
+
817
+ let(:with) { nil }
818
+ let(:location_name) { nil }
819
+ let(:tagged) { nil }
808
820
 
809
821
  let(:activities) do
810
822
  [
823
+ # With a friend
811
824
  Friends::Activity.new(
812
- str: "2016-04-01: Lunch with **George Washington Carver**."
825
+ str: "2016-01-01: \
826
+ At _The Eiffel Tower_ with **George Washington Carver**."
813
827
  ),
814
828
 
815
- # Create another activity with a gap of over a month between it and
816
- # the next activity, so we can test that we correctly return data for
817
- # months in the range with no activities.
829
+ # In a location
818
830
  Friends::Activity.new(
819
- str: "2016-02-01: Called **George Washington Carver**."
831
+ str: "2016-01-01: Called **George Washington Carver**. #phone"
820
832
  ),
821
833
 
822
- # Create an activity that doesn't involve our friend name.
834
+ # Tagged with a hashtag
823
835
  Friends::Activity.new(
824
- str: "2016-01-01: Called **Betsy Ross** on the phone."
836
+ str: "2016-01-01: Hung out with **Betsy Ross**."
825
837
  )
826
838
  ]
827
839
  end
828
840
 
829
- describe "when friend name is invalid" do
830
- let(:friend_name) { "Oscar the Grouch" }
841
+ it "graphs activities by month" do
842
+ stub_activities(activities) do
843
+ subject.must_equal("Jan 2016" => 3)
844
+ end
845
+ end
831
846
 
832
- it "raises an error" do
833
- proc { subject }.must_raise Friends::FriendsError
847
+ describe "No activities" do
848
+ it "returns an empty graph" do
849
+ subject.must_equal({})
834
850
  end
835
851
  end
836
852
 
837
- describe "when friend name has more than one match" do
838
- let(:friend_name) { "e" }
853
+ describe "Filtering by friend" do
854
+ let(:with) { "Betsy Ross" }
839
855
 
840
- it "raises an error" do
856
+ it "graphs activities with a friend" do
841
857
  stub_friends(friends) do
842
- proc { subject }.must_raise Friends::FriendsError
858
+ stub_activities(activities) do
859
+ subject.must_equal("Jan 2016" => 1)
860
+ end
843
861
  end
844
862
  end
845
- end
846
863
 
847
- describe "when friend is empty" do
848
- let(:friend_name) { nil }
864
+ describe "when the friend does not exist" do
865
+ let(:with) { "Oscar the Grouch" }
849
866
 
850
- it "returns a hash of months and frequencies" do
851
- stub_friends(friends) do
867
+ it "raises an error" do
852
868
  stub_activities(activities) do
853
- subject.must_equal(
854
- {
855
- "Jan 2016" => 1,
856
- "Feb 2016" => 1,
857
- "Mar 2016" => 0,
858
- "Apr 2016" => 1
859
- }
860
- )
869
+ proc { subject }.must_raise Friends::FriendsError
861
870
  end
862
871
  end
863
872
  end
864
873
  end
865
874
 
866
- describe "when friend name is valid" do
867
- let(:friend_name) { "George" }
875
+ describe "Filtering by location" do
876
+ let(:location_name) { "The Eiffel Tower" }
868
877
 
869
- it "returns a hash of months and frequencies" do
870
- stub_friends(friends) do
878
+ it "graphs activities in a location" do
879
+ stub_locations(locations) do
871
880
  stub_activities(activities) do
872
- subject.must_equal(
873
- {
874
- "Feb 2016" => 1,
875
- "Mar 2016" => 0,
876
- "Apr 2016" => 1
877
- }
878
- )
881
+ subject.must_equal("Jan 2016" => 1)
882
+ end
883
+ end
884
+ end
885
+
886
+ describe "when the location does not exist" do
887
+ let(:location_name) { "Nowhere" }
888
+
889
+ it "raises an error" do
890
+ stub_activities(activities) do
891
+ proc { subject }.must_raise Friends::FriendsError
879
892
  end
880
893
  end
881
894
  end
882
895
  end
896
+
897
+ describe "Filtering by hashtag" do
898
+ let(:tagged) { "#phone" }
899
+
900
+ it "graphs activities tagged with a hashtag" do
901
+ stub_activities(activities) do
902
+ subject.must_equal("Jan 2016" => 1)
903
+ end
904
+ end
905
+ end
883
906
  end
884
907
 
885
908
  describe "#total_friends" do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "./test/helper"
2
4
 
3
5
  describe Friends::Location do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friends
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.22'
4
+ version: '0.23'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Evelyn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-14 00:00:00.000000000 Z
11
+ date: 2016-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chronic
@@ -156,14 +156,14 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '0.36'
159
+ version: '0.40'
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: '0.36'
166
+ version: '0.40'
167
167
  description: Spend time with the people you care about. Introvert-tested. Extrovert-approved.
168
168
  email:
169
169
  - jacobevelyn@gmail.com
@@ -192,6 +192,7 @@ files:
192
192
  - lib/friends/activity.rb
193
193
  - lib/friends/friend.rb
194
194
  - lib/friends/friends_error.rb
195
+ - lib/friends/graph.rb
195
196
  - lib/friends/introvert.rb
196
197
  - lib/friends/location.rb
197
198
  - lib/friends/regex_builder.rb
@@ -199,6 +200,7 @@ files:
199
200
  - lib/friends/version.rb
200
201
  - test/activity_spec.rb
201
202
  - test/friend_spec.rb
203
+ - test/graph_spec.rb
202
204
  - test/helper.rb
203
205
  - test/introvert_spec.rb
204
206
  - test/location_spec.rb
@@ -230,6 +232,7 @@ summary: Spend time with the people you care about.
230
232
  test_files:
231
233
  - test/activity_spec.rb
232
234
  - test/friend_spec.rb
235
+ - test/graph_spec.rb
233
236
  - test/helper.rb
234
237
  - test/introvert_spec.rb
235
238
  - test/location_spec.rb