friends 0.22 → 0.23

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