friends 0.23 → 0.24

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: 560b5f5b4af9d8762ecb97f2a0ffdbb0cd8be02e
4
- data.tar.gz: 39338ddbdffe9c8c712bed9e5078d0cfc5c8877a
3
+ metadata.gz: 6d3011809caabfa111b478a57a39b562a260ada1
4
+ data.tar.gz: 8a3d2a2b250ea85c44950a2bffbe3142940d2481
5
5
  SHA512:
6
- metadata.gz: d2349672e223cec6d4f08df3ffce8209b8190540eb51862c72354d9a36144c813232bc18327a8d04c0da0d62ad1101f1a0931858f029e8e9cf1c94ea433c7ae0
7
- data.tar.gz: 3c833ed0d247ea59064335482a182b9ffbb094b172e5388370161baba6992e0f7f125d41fb967628b14e581b252039e17c7fbb3cac9a800fc0c9cf6945983e37
6
+ metadata.gz: 6e3df7a84a7b2033a093de24ce0728961857db8cb99b0d34da1d661fc541a7c0df4386452717894dc3c7c515c9b62ad6e8653f3e27277d50b0a194e526afa8e0
7
+ data.tar.gz: 9c61b1fd7eb6796462f32afaa756b3a11bf2510567d36fedbd40df2fa61043062ec8f01d540d6c47bba04156bdac9bac74b680351779d0f5b814ed53e4bd0791
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Change Log
2
2
 
3
+ ## [v0.24](https://github.com/JacobEvelyn/friends/tree/v0.24) (2016-05-17)
4
+ [Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.23...v0.24)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Change \#hashtags to @tags [\#122](https://github.com/JacobEvelyn/friends/issues/122)
9
+
10
+ **Fixed bugs:**
11
+
12
+ - Uninitialized constant Friends::Activity::Set when graphing by tag [\#133](https://github.com/JacobEvelyn/friends/issues/133)
13
+
14
+ **Merged pull requests:**
15
+
16
+ - Change tags to `@tag` format, auto-migrate old tags, and fix set loading issue [\#134](https://github.com/JacobEvelyn/friends/pull/134) ([JacobEvelyn](https://github.com/JacobEvelyn))
17
+
3
18
  ## [v0.23](https://github.com/JacobEvelyn/friends/tree/v0.23) (2016-05-16)
4
19
  [Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.22...v0.23)
5
20
 
data/README.md CHANGED
@@ -17,7 +17,7 @@ Extrovert-approved.
17
17
  - `add`
18
18
  - [`add activity`](#add-activity)
19
19
  - [`add friend`](#add-friend)
20
- - [`add hashtag`](#add-hashtag)
20
+ - [`add tag`](#add-tag)
21
21
  - [`add location`](#add-location)
22
22
  - [`add nickname`](#add-nickname)
23
23
  - [`clean`](#clean)
@@ -29,10 +29,10 @@ Extrovert-approved.
29
29
  - [`list favorite friends`](#list-favorite-friends)
30
30
  - [`list favorite locations`](#list-favorite-locations)
31
31
  - [`list friends`](#list-friends)
32
- - [`list hashtags`](#list-hashtags)
32
+ - [`list tags`](#list-tags)
33
33
  - [`list locations`](#list-locations)
34
34
  - `remove`
35
- - [`remove hashtag`](#remove-hashtag)
35
+ - [`remove tag`](#remove-tag)
36
36
  - [`remove nickname`](#remove-nickname)
37
37
  - `rename`
38
38
  - [`rename friend`](#rename-friend)
@@ -88,13 +88,13 @@ Easy, huh?
88
88
 
89
89
  - **Activities**: The things you do. Each activity has a date associated with
90
90
  it. Activities may optionally contain any number of *friends*, *locations*,
91
- and *hashtags*.
91
+ and *tags*.
92
92
  - **Friends**: The people you do *activities* with. Each friend has a name and,
93
93
  optionally, one or several nicknames. (Examples: `John`, `Grace Hopper`)
94
94
  - **Locations**: The places in which *activities* happen. (Examples: `Paris`,
95
95
  `Marie's Diner`)
96
- - **Hashtags**: A way to categorize your *activities* with tags of your
97
- choosing. (Examples: `#exercise`, `#concert`)
96
+ - **Tags**: A way to categorize your *activities* with tags of your
97
+ choosing. (Examples: `@exercise`, `@school`)
98
98
 
99
99
  The `friends.md` Markdown file that stores all of your data contains:
100
100
 
@@ -209,7 +209,7 @@ You can also use the first initial of a last name instead of the whole thing.
209
209
  them) based on whether you're in the middle of a sentence or not:
210
210
 
211
211
  ```bash
212
- $ friends add activity "Got lunch with Earnest H and Earnest S. in the park. Man, I like Earnest H. but really love Earnest S."
212
+ $ friends add activity Got lunch with Earnest H and Earnest S. in the park. Man, I like Earnest H. but really love Earnest S.
213
213
  Activity added: "2016-05-01: Got lunch with Earnest Hemingway and Earnest Shackleton in the park. Man, I like Earnest Hemingway but really love Earnest Shackleton."
214
214
  ```
215
215
 
@@ -220,12 +220,12 @@ $ friends add activity Went swimming near atlantis with George.
220
220
  Activity added: "2016-01-06: Went swimming near Atlantis with George Washington Carver."
221
221
  ```
222
222
 
223
- Hashtags will be colored if they're provided (though this README can't display
223
+ Tags will be colored if they're provided (though this README can't display
224
224
  color so you'll just have to have faith here):
225
225
 
226
226
  ```bash
227
- $ friends add activity "The office softball team wins a game! #work #exercise"
228
- Activity added: "2016-05-05: The office softball team wins a game! #work #exercise"
227
+ $ friends add activity The office softball team wins a game! @work @exercise
228
+ Activity added: "2016-05-05: The office softball team wins a game! @work @exercise"
229
229
  ```
230
230
 
231
231
  You can of course specify a date for the activity:
@@ -263,11 +263,11 @@ $ friends add friend Grace Hopper
263
263
  Friend added: "Grace Hopper"
264
264
  ```
265
265
 
266
- #### `add hashtag`
266
+ #### `add tag`
267
267
 
268
268
  ```bash
269
- $ friends add hashtag "Grace Hopper" science
270
- Hashtag added to friend: "Grace Hopper #science
269
+ $ friends add tag "Grace Hopper" science
270
+ Tag added to friend: "Grace Hopper @science"
271
271
  ```
272
272
 
273
273
  #### `add location`
@@ -338,7 +338,7 @@ Jan 2015 |█████
338
338
  Feb 2015 |███
339
339
  ```
340
340
 
341
- Or graph only activities with a certain hashtag:
341
+ Or graph only activities with a certain tag:
342
342
 
343
343
  ```bash
344
344
  $ friends graph --tagged food
@@ -400,8 +400,8 @@ Lists recent activities:
400
400
 
401
401
  ```bash
402
402
  $ friends list activities
403
- 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
404
- 2014-12-31: Celebrated the new year with Marie Curie in New York City. #partying
403
+ 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
404
+ 2014-12-31: Celebrated the new year with Marie Curie in New York City. @partying
405
405
  2014-11-15: Talked to George Washington Carver on the phone for an hour.
406
406
  ```
407
407
 
@@ -409,15 +409,15 @@ You can adjust how many activities are shown:
409
409
 
410
410
  ```bash
411
411
  $ friends list activities --limit 2
412
- 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
413
- 2014-12-31: Celebrated the new year with Marie Curie in New York City. #partying
412
+ 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
413
+ 2014-12-31: Celebrated the new year with Marie Curie in New York City. @partying
414
414
  ```
415
415
 
416
416
  Or only list the activities you did with a certain friend:
417
417
 
418
418
  ```bash
419
419
  $ friends list activities --with George
420
- 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
420
+ 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
421
421
  2014-11-15: Talked to George Washington Carver on the phone for an hour.
422
422
  ```
423
423
 
@@ -425,21 +425,21 @@ Or filter your activities by location:
425
425
 
426
426
  ```bash
427
427
  $ friends list activities --in "New York"
428
- 2014-12-31: Celebrated the new year with Marie Curie in New York City. #partying
428
+ 2014-12-31: Celebrated the new year with Marie Curie in New York City. @partying
429
429
  ```
430
430
 
431
- Or by hashtag:
431
+ Or by tag:
432
432
 
433
433
  ```bash
434
434
  $ friends list activities --tagged food
435
- 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
435
+ 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
436
436
  ```
437
437
 
438
438
  And you can mix and match these options to your heart's content:
439
439
 
440
440
  ```bash
441
441
  $ friends list activities --tagged food --with Grace
442
- 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. #food
442
+ 2015-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
443
443
  ```
444
444
 
445
445
  #### `list favorite friends`
@@ -495,13 +495,13 @@ Grace Hopper
495
495
  Marie Curie
496
496
  ```
497
497
 
498
- You can also include friend nicknames, locations, and hashtags:
498
+ You can also include friend nicknames, locations, and tags:
499
499
 
500
500
  ```bash
501
501
  $ friends list friends --verbose
502
502
  George Washington Carver
503
- Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] #navy #science
504
- Marie Curie [Atlantis] #science
503
+ Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] @navy @science
504
+ Marie Curie [Atlantis] @science
505
505
  ```
506
506
 
507
507
  You can filter your friends by location:
@@ -511,7 +511,7 @@ $ friends list friends --in Paris
511
511
  Marie Curie
512
512
  ```
513
513
 
514
- And you can also filter your friends by hashtag:
514
+ And you can also filter your friends by tag:
515
515
 
516
516
  ```bash
517
517
  $ friends list friends --tagged science
@@ -519,33 +519,33 @@ Grace Hopper
519
519
  Marie Curie
520
520
  ```
521
521
 
522
- #### `list hashtags`
522
+ #### `list tags`
523
523
 
524
- Lists all hashtags you've used, in alphabetical order:
524
+ Lists all tags you've used, in alphabetical order:
525
525
 
526
526
  ```bash
527
- $ friends list hashtags
528
- #dancing
529
- #food
530
- #school
531
- #swanky
527
+ $ friends list tags
528
+ @dancing
529
+ @food
530
+ @school
531
+ @swanky
532
532
  ```
533
533
 
534
- You can limit this to only hashtags from activities:
534
+ You can limit this to only tags from activities:
535
535
 
536
536
  ```bash
537
- $ friends list hashtags --from activities
538
- #dancing
539
- #food
540
- #swanky
537
+ $ friends list tags --from activities
538
+ @dancing
539
+ @food
540
+ @swanky
541
541
  ```
542
542
 
543
- Or only hashtags from friends:
543
+ Or only tags from friends:
544
544
 
545
545
  ```bash
546
- $ friends list hashtags --from friends
547
- #school
548
- #swanky
546
+ $ friends list tags --from friends
547
+ @school
548
+ @swanky
549
549
  ```
550
550
 
551
551
  #### `list locations`
@@ -559,13 +559,13 @@ New York City
559
559
  Paris
560
560
  ```
561
561
 
562
- #### `remove hashtag`
562
+ #### `remove tag`
563
563
 
564
- Removes a specific hashtag from a friend:
564
+ Removes a specific tag from a friend:
565
565
 
566
566
  ```bash
567
- $ friends remove hashtag "Grace Hopper" fun
568
- Hashtag removed from friend: "Grace Hopper (a.k.a. Amazing Grace) #OtherHashtag"
567
+ $ friends remove tag "Grace Hopper" fun
568
+ Tag removed from friend: "Grace Hopper (a.k.a. Amazing Grace) @OtherTag"
569
569
  ```
570
570
 
571
571
  #### `remove nickname`
data/bin/friends CHANGED
@@ -19,21 +19,21 @@ version Friends::VERSION
19
19
  subcommand_option_handling :normal
20
20
  arguments :strict
21
21
 
22
- class Hashtag
23
- # @param str [String] of the form "hashtag" or "#hashtag"
24
- # @return [String] the string, with whitespace stripped and a hashtag
22
+ class Tag
23
+ # @param str [String] of the form "tag" or "@tag"
24
+ # @return [String] the string, with whitespace stripped and a tag
25
25
  # prepended if there isn't one already
26
26
  # NOTE: This logic could be only in the accept block if GLI allowed type
27
27
  # conversions for arguments.
28
28
  # See: https://github.com/davetron5000/gli/issues/241
29
- def self.convert_to_hashtag(str)
29
+ def self.convert_to_tag(str)
30
30
  str = str.strip
31
- !str.empty? && str[0] == "#" ? str : "##{str}"
31
+ !str.empty? && str[0] == "@" ? str : "@#{str}"
32
32
  end
33
33
  end
34
34
 
35
- accept(Hashtag) do |value|
36
- Hashtag.convert_to_hashtag(value)
35
+ accept(Tag) do |value|
36
+ Tag.convert_to_tag(value)
37
37
  end
38
38
 
39
39
  class Stripped; end
@@ -97,13 +97,13 @@ command :list do |list|
97
97
  type: Stripped
98
98
 
99
99
  list_friends.flag [:tagged],
100
- arg_name: "HASHTAG",
101
- desc: "List only friends with the given hashtag",
102
- type: Hashtag
100
+ arg_name: "@TAG",
101
+ desc: "List only friends with the given tag",
102
+ type: Tag
103
103
 
104
104
  list_friends.switch [:verbose],
105
105
  negatable: false,
106
- desc: "Output friend nicknames, locations, and hashtags"
106
+ desc: "Output friend nicknames, locations, and tags"
107
107
 
108
108
  list_friends.action do |_, options|
109
109
  puts @introvert.list_friends(
@@ -133,9 +133,9 @@ command :list do |list|
133
133
  type: Stripped
134
134
 
135
135
  list_activities.flag [:tagged],
136
- arg_name: "HASHTAG",
137
- desc: "List only activities with the given hashtag",
138
- type: Hashtag
136
+ arg_name: "@TAG",
137
+ desc: "List only activities with the given tag",
138
+ type: Tag
139
139
 
140
140
  list_activities.action do |_, options|
141
141
  puts @introvert.list_activities(
@@ -154,14 +154,14 @@ command :list do |list|
154
154
  end
155
155
  end
156
156
 
157
- list.desc "List all hashtags used"
158
- list.command :hashtags do |list_hashtags|
159
- list_hashtags.flag [:from],
157
+ list.desc "List all tags used"
158
+ list.command :tags do |list_tags|
159
+ list_tags.flag [:from],
160
160
  arg_name: '"activities" or "friends" (default: both)',
161
- desc: "List only hashtags from activities or friends "\
161
+ desc: "List only tags from activities or friends "\
162
162
  "instead of both"
163
- list_hashtags.action do |_, options|
164
- puts @introvert.list_hashtags(from: options[:from])
163
+ list_tags.action do |_, options|
164
+ puts @introvert.list_tags(from: options[:from])
165
165
  end
166
166
  end
167
167
 
@@ -266,15 +266,15 @@ command :add do |add|
266
266
  end
267
267
  end
268
268
 
269
- add.desc "Adds a hashtag to a friend"
270
- add.arg_name "NAME HASHTAG"
271
- add.command :hashtag do |add_hashtag|
272
- add_hashtag.action do |_, _, args|
273
- friend = @introvert.add_hashtag(
269
+ add.desc "Adds a tag to a friend"
270
+ add.arg_name "NAME @TAG"
271
+ add.command :tag do |add_tag|
272
+ add_tag.action do |_, _, args|
273
+ friend = @introvert.add_tag(
274
274
  name: args.first,
275
- hashtag: Hashtag.convert_to_hashtag(args[1])
275
+ tag: Tag.convert_to_tag(args[1])
276
276
  )
277
- @message = "Hashtag added to friend: \"#{friend}\""
277
+ @message = "Tag added to friend: \"#{friend}\""
278
278
  @dirty = true # Mark the file for cleaning.
279
279
  end
280
280
  end
@@ -305,15 +305,15 @@ command :remove do |remove|
305
305
  end
306
306
  end
307
307
 
308
- remove.desc "Removes a hashtag to a friend"
309
- remove.arg_name "NAME HASHTAG"
310
- remove.command :hashtag do |remove_hashtag|
311
- remove_hashtag.action do |_, _, args|
312
- friend = @introvert.remove_hashtag(
308
+ remove.desc "Removes a tag from a friend"
309
+ remove.arg_name "NAME @TAG"
310
+ remove.command :tag do |remove_tag|
311
+ remove_tag.action do |_, _, args|
312
+ friend = @introvert.remove_tag(
313
313
  name: args.first,
314
- hashtag: Hashtag.convert_to_hashtag(args[1])
314
+ tag: Tag.convert_to_tag(args[1])
315
315
  )
316
- @message = "Hashtag removed from friend: \"#{friend}\""
316
+ @message = "Tag removed from friend: \"#{friend}\""
317
317
  @dirty = true # Mark the file for cleaning.
318
318
  end
319
319
  end
@@ -332,9 +332,9 @@ command :graph do |graph|
332
332
  type: Stripped
333
333
 
334
334
  graph.flag [:tagged],
335
- arg_name: "HASHTAG",
336
- desc: "Graph activities with the given hashtag",
337
- type: Hashtag
335
+ arg_name: "@TAG",
336
+ desc: "Graph activities with the given tag",
337
+ type: Tag
338
338
 
339
339
  graph.action do |_, options|
340
340
  # This math is taken from Minitest's Pride plugin (the PrideLOL class).
data/friends.md CHANGED
@@ -1,13 +1,13 @@
1
1
  ### Activities:
2
- - 2015-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute. #food
3
- - 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**. #food
4
- - 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. #partying
2
+ - 2015-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute. @food
3
+ - 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**. @food
4
+ - 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. @partying
5
5
  - 2014-11-15: Talked to **George Washington Carver** on the phone for an hour.
6
6
 
7
7
  ### Friends:
8
8
  - George Washington Carver
9
- - Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] #navy #science
10
- - Marie Curie [Atlantis] #science
9
+ - Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] @navy @science
10
+ - Marie Curie [Atlantis] @science
11
11
 
12
12
  ### Locations:
13
13
  - Atlantis
data/lib/friends.rb CHANGED
@@ -4,5 +4,5 @@ require "friends/introvert"
4
4
  require "friends/version"
5
5
 
6
6
  module Friends
7
- HASHTAG_REGEX = /#\p{Alnum}+/
7
+ TAG_REGEX = /@\p{Alnum}+/
8
8
  end
@@ -5,6 +5,7 @@
5
5
  require "chronic"
6
6
  require "memoist"
7
7
  require "paint"
8
+ require "set"
8
9
 
9
10
  require "friends/serializable"
10
11
 
@@ -71,7 +72,7 @@ module Friends
71
72
  end
72
73
 
73
74
  description_s = description_s.
74
- gsub(HASHTAG_REGEX, Paint['\0', :bold, :cyan])
75
+ gsub(TAG_REGEX, Paint['\0', :bold, :cyan])
75
76
 
76
77
  "#{date_s}: #{description_s}"
77
78
  end
@@ -118,25 +119,25 @@ module Friends
118
119
 
119
120
  # @param location [Location] the location to test
120
121
  # @return [Boolean] true iff this activity includes the given location
121
- def includes_location?(location:)
122
+ def includes_location?(location)
122
123
  @description.scan(/(?<=_)[^_]+(?=_)/).include? location.name
123
124
  end
124
125
 
125
126
  # @param friend [Friend] the friend to test
126
127
  # @return [Boolean] true iff this activity includes the given friend
127
- def includes_friend?(friend:)
128
+ def includes_friend?(friend)
128
129
  friend_names.include? friend.name
129
130
  end
130
131
 
131
- # @param hashtag [String] the hashtag to test, of the form "#hashtag"
132
- # @return [Boolean] true iff this activity includes the given hashtag
133
- def includes_hashtag?(hashtag:)
134
- hashtags.include? hashtag
132
+ # @param tag [String] the tag to test, of the form "@tag"
133
+ # @return [Boolean] true iff this activity includes the given tag
134
+ def includes_tag?(tag)
135
+ tags.include? tag
135
136
  end
136
137
 
137
- # @return [Set] all hashtags in this activity (including the "#")
138
- def hashtags
139
- Set.new(@description.scan(HASHTAG_REGEX))
138
+ # @return [Set] all tags in this activity (including the "@")
139
+ def tags
140
+ Set.new(@description.scan(TAG_REGEX))
140
141
  end
141
142
 
142
143
  # Find the names of all friends in this description.
@@ -17,7 +17,7 @@ module Friends
17
17
  def self.deserialization_regex
18
18
  # Note: this regex must be on one line because whitespace is important
19
19
  # rubocop:disable Metrics/LineLength
20
- /(#{SERIALIZATION_PREFIX})?(?<name>[^\(\[#]*[^\(\[#\s])(\s+\(#{NICKNAME_PREFIX}(?<nickname_str>.+)\))?(\s+\[(?<location_name>[^\]]+)\])?(\s+(?<hashtags_str>(#{HASHTAG_REGEX}\s*)+))?/
20
+ /(#{SERIALIZATION_PREFIX})?(?<name>[^\(\[#]*[^\(\[#\s])(\s+\(#{NICKNAME_PREFIX}(?<nickname_str>.+)\))?(\s+\[(?<location_name>[^\]]+)\])?(\s+(?<tags_str>(#{TAG_REGEX}\s*)+))?/
21
21
  # rubocop:enable Metrics/LineLength
22
22
  end
23
23
 
@@ -31,19 +31,19 @@ module Friends
31
31
  name:,
32
32
  nickname_str: nil,
33
33
  location_name: nil,
34
- hashtags_str: nil
34
+ tags_str: nil
35
35
  )
36
36
  @name = name
37
37
  @nicknames = nickname_str &&
38
38
  nickname_str.split(" #{NICKNAME_PREFIX}") ||
39
39
  []
40
40
  @location_name = location_name
41
- @hashtags = hashtags_str && hashtags_str.split(/\s+/) || []
41
+ @tags = tags_str && tags_str.split(/\s+/) || []
42
42
  end
43
43
 
44
44
  attr_accessor :name
45
45
  attr_accessor :location_name
46
- attr_reader :hashtags
46
+ attr_reader :tags
47
47
 
48
48
  # @return [String] the file serialization text for the friend
49
49
  def serialize
@@ -61,25 +61,25 @@ module Friends
61
61
 
62
62
  location_str = " [#{@location_name}]" unless @location_name.nil?
63
63
 
64
- hashtag_str = " #{@hashtags.join(' ')}" unless @hashtags.empty?
64
+ tag_str = " #{@tags.join(' ')}" unless @tags.empty?
65
65
 
66
- "#{@name}#{nickname_str}#{location_str}#{hashtag_str}"
66
+ "#{@name}#{nickname_str}#{location_str}#{tag_str}"
67
67
  end
68
68
 
69
- # Adds a hashtag, ignoring duplicates.
70
- # @param hashtag [String] the hashtag to add
71
- def add_hashtag(hashtag)
72
- @hashtags << hashtag
73
- @hashtags.uniq!
69
+ # Adds a tag, ignoring duplicates.
70
+ # @param tag [String] the tag to add, of the format: "@tag"
71
+ def add_tag(tag)
72
+ @tags << tag
73
+ @tags.uniq!
74
74
  end
75
75
 
76
- # @param hashtag [String] the hashtag to remove
77
- def remove_hashtag(hashtag)
78
- unless @hashtags.include? hashtag
79
- raise FriendsError, "Hashtag \"#{hashtag}\" not found for \"#{name}\""
76
+ # @param tag [String] the tag to remove, of the format: "@tag"
77
+ def remove_tag(tag)
78
+ unless @tags.include? tag
79
+ raise FriendsError, "Tag \"#{tag}\" not found for \"#{name}\""
80
80
  end
81
81
 
82
- @hashtags.delete(hashtag)
82
+ @tags.delete(tag)
83
83
  end
84
84
 
85
85
  # Adds a nickname, ignoring duplicates.
@@ -4,6 +4,8 @@
4
4
  # able to be used directly within another Ruby program, without needing to call
5
5
  # the command-line script explicitly.
6
6
 
7
+ require "set"
8
+
7
9
  require "friends/activity"
8
10
  require "friends/friend"
9
11
  require "friends/graph"
@@ -158,26 +160,26 @@ module Friends
158
160
  friend
159
161
  end
160
162
 
161
- # Add a hashtag to an existing friend.
163
+ # Add a tag to an existing friend.
162
164
  # @param name [String] the name of the friend
163
- # @param hashtag [String] the hashtag to add to the friend
165
+ # @param tag [String] the tag to add to the friend, of the form: "@tag"
164
166
  # @raise [FriendsError] if 0 or 2+ friends match the given name
165
167
  # @return [Friend] the existing friend
166
- def add_hashtag(name:, hashtag:)
168
+ def add_tag(name:, tag:)
167
169
  friend = friend_with_name_in(name)
168
- friend.add_hashtag(hashtag)
170
+ friend.add_tag(tag)
169
171
  friend
170
172
  end
171
173
 
172
- # Remove a hashtag from an existing friend.
174
+ # Remove a tag from an existing friend.
173
175
  # @param name [String] the name of the friend
174
- # @param hashtag [String] the hashtag to remove from the friend
176
+ # @param tag [String] the tag to remove from the friend, of the form: "@tag"
175
177
  # @raise [FriendsError] if 0 or 2+ friends match the given name
176
178
  # @raise [FriendsError] if the friend does not have the given nickname
177
179
  # @return [Friend] the existing friend
178
- def remove_hashtag(name:, hashtag:)
180
+ def remove_tag(name:, tag:)
179
181
  friend = friend_with_name_in(name)
180
- friend.remove_hashtag(hashtag)
182
+ friend.remove_tag(tag)
181
183
  friend
182
184
  end
183
185
 
@@ -196,10 +198,10 @@ module Friends
196
198
  # List all friend names in the friends file.
197
199
  # @param location_name [String] the name of a location to filter by, or nil
198
200
  # for unfiltered
199
- # @param tagged [String] the name of a hashtag to filter by, or nil for
200
- # unfiltered
201
+ # @param tagged [String] the name of a tag to filter by (of the form:
202
+ # "@tag"), or nil for unfiltered
201
203
  # @param verbose [Boolean] true iff we should output friend names with
202
- # nicknames, locations, and hashtags; false for names only
204
+ # nicknames, locations, and tags; false for names only
203
205
  # @return [Array] a list of all friend names
204
206
  def list_friends(location_name:, tagged:, verbose:)
205
207
  fs = @friends
@@ -210,8 +212,8 @@ module Friends
210
212
  fs = fs.select { |friend| friend.location_name == location.name }
211
213
  end
212
214
 
213
- # Filter by hashtag if one is passed.
214
- fs = fs.select { |friend| friend.hashtags.include? tagged } if tagged
215
+ # Filter by tag if one is passed.
216
+ fs = fs.select { |friend| friend.tags.include? tagged } if tagged
215
217
 
216
218
  verbose ? fs.map(&:to_s) : fs.map(&:name)
217
219
  end
@@ -239,10 +241,10 @@ module Friends
239
241
  # unfiltered
240
242
  # @param location_name [String] the name of a location to filter by, or nil
241
243
  # for unfiltered
242
- # @param tagged [String] the name of a hashtag to filter by, or nil for
243
- # unfiltered
244
+ # @param tagged [String] the name of a tag to filter by (of the form:
245
+ # "@tag"), or nil for unfiltered
244
246
  # @return [Array] a list of all activity text values
245
- # @raise [FriendsError] if friend, location or hashtag cannot be found or
247
+ # @raise [FriendsError] if friend, location or tag cannot be found or
246
248
  # is ambiguous
247
249
  def list_activities(limit:, with:, location_name:, tagged:)
248
250
  acts = filtered_activities(
@@ -264,21 +266,21 @@ module Friends
264
266
  end
265
267
 
266
268
  # @param from [String] one of: ["activities", "friends", nil]
267
- # If not nil, limits the hashtags returned to only those from either
269
+ # If not nil, limits the tags returned to only those from either
268
270
  # activities or friends.
269
- # @return [Array] a sorted list of all hashtags in activity descriptions
270
- def list_hashtags(from:)
271
+ # @return [Array] a sorted list of all tags in activity descriptions
272
+ def list_tags(from:)
271
273
  output = Set.new
272
274
 
273
275
  unless from == "friends" # If from is "activities" or nil.
274
276
  @activities.each_with_object(output) do |activity, set|
275
- set.merge(activity.hashtags)
277
+ set.merge(activity.tags)
276
278
  end
277
279
  end
278
280
 
279
281
  unless from == "activities" # If from is "friends" or nil.
280
282
  @friends.each_with_object(output) do |friend, set|
281
- set.merge(friend.hashtags)
283
+ set.merge(friend.tags)
282
284
  end
283
285
  end
284
286
 
@@ -286,7 +288,7 @@ module Friends
286
288
  end
287
289
 
288
290
  # Find data points for graphing activities over time.
289
- # Optionally filter by friend, location and hashtag
291
+ # Optionally filter by friend, location and tag
290
292
  #
291
293
  # The returned hash uses the following format:
292
294
  # {
@@ -301,10 +303,10 @@ module Friends
301
303
  # unfiltered
302
304
  # @param location_name [String] the name of a location to filter by, or nil
303
305
  # for unfiltered
304
- # @param tagged [String] the name of a hashtag to filter by, or nil for
305
- # unfiltered
306
+ # @param tagged [String] the name of a tag to filter by (of the form:
307
+ # "@tag"), or nil for unfiltered
306
308
  # @return [Hash{String => Integer}]
307
- # @raise [FriendsError] if friend, location or hashtag cannot be found or
309
+ # @raise [FriendsError] if friend, location or tag cannot be found or
308
310
  # is ambiguous
309
311
  def graph(with:, location_name:, tagged:)
310
312
  # There is no point trying to graph no activities
@@ -463,15 +465,15 @@ module Friends
463
465
 
464
466
  private
465
467
 
466
- # Filter activities by friend, location and hashtag
468
+ # Filter activities by friend, location and tag
467
469
  # @param with [String] the name of a friend to filter by, or nil for
468
470
  # unfiltered
469
471
  # @param location_name [String] the name of a location to filter by, or nil
470
472
  # for unfiltered
471
- # @param tagged [String] the name of a hashtag to filter by, or nil for
473
+ # @param tagged [String] the name of a tag to filter by, or nil for
472
474
  # unfiltered
473
475
  # @return [Array] an array of activities
474
- # @raise [FriendsError] if friend, location or hashtag cannot be found or
476
+ # @raise [FriendsError] if friend, location or tag cannot be found or
475
477
  # is ambiguous
476
478
  def filtered_activities(with:, location_name:, tagged:)
477
479
  acts = @activities
@@ -479,18 +481,18 @@ module Friends
479
481
  # Filter by friend name if argument is passed.
480
482
  unless with.nil?
481
483
  friend = friend_with_name_in(with)
482
- acts = acts.select { |act| act.includes_friend?(friend: friend) }
484
+ acts = acts.select { |act| act.includes_friend?(friend) }
483
485
  end
484
486
 
485
487
  # Filter by location name if argument is passed.
486
488
  unless location_name.nil?
487
489
  location = location_with_name_in(location_name)
488
- acts = acts.select { |act| act.includes_location?(location: location) }
490
+ acts = acts.select { |act| act.includes_location?(location) }
489
491
  end
490
492
 
491
493
  # Filter by tag if argument is passed.
492
494
  unless tagged.nil?
493
- acts = acts.select { |act| act.includes_hashtag?(hashtag: tagged) }
495
+ acts = acts.select { |act| act.includes_tag?(tagged) }
494
496
  end
495
497
 
496
498
  acts
@@ -567,6 +569,15 @@ module Friends
567
569
  # Parse the line and update the parsing state.
568
570
  state = parse_line!(line, line_num: line_num, state: state)
569
571
  end
572
+
573
+ # Migrate old tag format (#tag) into new tag format (@tag). This code will
574
+ # be removed in the 1.0 release.
575
+ unless @friends.any? { |f| f.tags.any? } ||
576
+ @activities.any? { |a| a.tags.any? }
577
+ contents = File.read(@filename)
578
+ File.write(@filename, contents.gsub(/#(\p{Alnum}+)/, "@\\1"))
579
+ read_file
580
+ end
570
581
  end
571
582
 
572
583
  # Parse the given line, adding to the various internal data structures as
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Friends
4
- VERSION = "0.23"
4
+ VERSION = "0.24"
5
5
  end
@@ -430,7 +430,7 @@ describe Friends::Activity do
430
430
  end
431
431
 
432
432
  describe "#includes_location?" do
433
- subject { activity.includes_location?(location: loc) }
433
+ subject { activity.includes_location?(loc) }
434
434
  let(:loc) { Friends::Location.new(name: "Atlantis") }
435
435
 
436
436
  describe "when the given location is in the activity" do
@@ -445,7 +445,7 @@ describe Friends::Activity do
445
445
  end
446
446
 
447
447
  describe "#includes_friend?" do
448
- subject { activity.includes_friend?(friend: friend) }
448
+ subject { activity.includes_friend?(friend) }
449
449
 
450
450
  describe "when the given friend is in the activity" do
451
451
  let(:friend) { friend1 }
@@ -458,36 +458,36 @@ describe Friends::Activity do
458
458
  end
459
459
  end
460
460
 
461
- describe "#hashtags" do
462
- subject { activity.hashtags }
461
+ describe "#tags" do
462
+ subject { activity.tags }
463
463
 
464
- describe "when the activity has no hashtags" do
464
+ describe "when the activity has no tags" do
465
465
  let(:activity) { Friends::Activity.new(str: "Enormous ball pit!") }
466
466
  it { subject.must_be :empty? }
467
467
  end
468
468
 
469
- describe "when the activity has hashtags" do
470
- let(:activity) { Friends::Activity.new(str: "Party! #fun #crazy #fun") }
471
- it { subject.must_equal Set.new(["#fun", "#crazy"]) }
469
+ describe "when the activity has tags" do
470
+ let(:activity) { Friends::Activity.new(str: "Party! @fun @crazy @fun") }
471
+ it { subject.must_equal Set.new(["@fun", "@crazy"]) }
472
472
  end
473
473
  end
474
474
 
475
- describe "#includes_hashtag?" do
476
- subject { activity.includes_hashtag?(hashtag: hashtag) }
477
- let(:activity) { Friends::Activity.new(str: "Enormous ball pit! #fun") }
475
+ describe "#includes_tag?" do
476
+ subject { activity.includes_tag?(tag) }
477
+ let(:activity) { Friends::Activity.new(str: "Enormous ball pit! @fun") }
478
478
 
479
- describe "when the given hashtag is not in the activity" do
480
- let(:hashtag) { "#garbage" }
479
+ describe "when the given tag is not in the activity" do
480
+ let(:tag) { "@garbage" }
481
481
  it { subject.must_equal false }
482
482
  end
483
483
 
484
- describe "when the given word is in the activity but not as a hashtag" do
485
- let(:hashtag) { "#ball" }
484
+ describe "when the given word is in the activity but not as a tag" do
485
+ let(:tag) { "@ball" }
486
486
  it { subject.must_equal false }
487
487
  end
488
488
 
489
- describe "when the given hashtag is in the activity" do
490
- let(:hashtag) { "#fun" }
489
+ describe "when the given tag is in the activity" do
490
+ let(:tag) { "@fun" }
491
491
  it { subject.must_equal true }
492
492
  end
493
493
  end
data/test/friend_spec.rb CHANGED
@@ -81,35 +81,35 @@ describe Friends::Friend do
81
81
  end
82
82
  end
83
83
 
84
- describe "#add_hashtag" do
85
- subject { friend.add_hashtag("#college") }
84
+ describe "#add_tag" do
85
+ subject { friend.add_tag("@college") }
86
86
 
87
87
  it "adds the nickname" do
88
88
  subject
89
- friend.hashtags.must_include("#college")
89
+ friend.tags.must_include("@college")
90
90
  end
91
91
 
92
92
  it "does not keep duplicates" do
93
93
  # Add the same nickname twice. Do not use `subject` because it's memoized.
94
- friend.add_hashtag("#college")
95
- friend.add_hashtag("#college")
94
+ friend.add_tag("@college")
95
+ friend.add_tag("@college")
96
96
 
97
- friend.hashtags.must_equal ["#college"]
97
+ friend.tags.must_equal ["@college"]
98
98
  end
99
99
  end
100
100
 
101
- describe "#remove_hashtag" do
102
- subject { friend.remove_hashtag("#school") }
101
+ describe "@remove_tag" do
102
+ subject { friend.remove_tag("@school") }
103
103
 
104
- describe "when the hashtag is present" do
104
+ describe "when the tag is present" do
105
105
  let(:friend) do
106
- Friends::Friend.new(name: friend_name, hashtags_str: "#school #work")
106
+ Friends::Friend.new(name: friend_name, tags_str: "@school @work")
107
107
  end
108
108
 
109
109
  it "removes the nickname" do
110
- friend.instance_variable_get(:@hashtags).must_equal ["#school", "#work"]
110
+ friend.instance_variable_get(:@tags).must_equal ["@school", "@work"]
111
111
  subject
112
- friend.instance_variable_get(:@hashtags).must_equal ["#work"]
112
+ friend.instance_variable_get(:@tags).must_equal ["@work"]
113
113
  end
114
114
  end
115
115
 
@@ -38,7 +38,7 @@ describe Friends::Introvert do
38
38
  let(:friend_names) { ["George Washington Carver", "Betsy Ross"] }
39
39
  let(:friends) do
40
40
  friend_names.map do |name|
41
- Friends::Friend.new(name: name, hashtags_str: "#test")
41
+ Friends::Friend.new(name: name, tags_str: "@test")
42
42
  end
43
43
  end
44
44
  let(:activities) do
@@ -146,15 +146,15 @@ describe Friends::Introvert do
146
146
  end
147
147
  end
148
148
 
149
- describe "when a hashtag has been passed" do
149
+ describe "when a tag has been passed" do
150
150
  let(:location_name) { nil }
151
- let(:tagged) { "#superhero" }
151
+ let(:tagged) { "@superhero" }
152
152
  let(:verbose) { false }
153
153
  let(:friends) do
154
154
  [
155
155
  Friends::Friend.new(name: "Mark Watney"),
156
- Friends::Friend.new(name: "Aquaman", hashtags_str: "#superhero"),
157
- Friends::Friend.new(name: "Shark-Boy", hashtags_str: "#superhero"),
156
+ Friends::Friend.new(name: "Aquaman", tags_str: "@superhero"),
157
+ Friends::Friend.new(name: "Shark-Boy", tags_str: "@superhero"),
158
158
  Friends::Friend.new(name: "Ms. Nowhere")
159
159
  ]
160
160
  end
@@ -268,35 +268,35 @@ describe Friends::Introvert do
268
268
  end
269
269
  end
270
270
 
271
- describe "#list_hashtags" do
272
- subject { introvert.list_hashtags(from: from) }
271
+ describe "#list_tags" do
272
+ subject { introvert.list_tags(from: from) }
273
273
 
274
274
  let(:activities) do
275
275
  [
276
- Friends::Activity.new(str: "Lunch in the park. #picnic #food"),
277
- Friends::Activity.new(str: "Fancy dinner. #food #swanky")
276
+ Friends::Activity.new(str: "Lunch in the park. @picnic @food"),
277
+ Friends::Activity.new(str: "Fancy dinner. @food @swanky")
278
278
  ]
279
279
  end
280
280
 
281
281
  let(:friends) do
282
282
  [
283
- Friends::Friend.new(name: "Grace Hopper", hashtags_str: "#coder #navy"),
284
- Friends::Friend.new(name: "James Bond", hashtags_str: "#cool #swanky")
283
+ Friends::Friend.new(name: "Grace Hopper", tags_str: "@coder @navy"),
284
+ Friends::Friend.new(name: "James Bond", tags_str: "@cool @swanky")
285
285
  ]
286
286
  end
287
287
 
288
288
  describe "when from flag is nil" do
289
289
  let(:from) { nil }
290
- it "lists all hashtags in sorted order" do
290
+ it "lists all tags in sorted order" do
291
291
  stub_activities(activities) do
292
292
  stub_friends(friends) do
293
293
  subject.must_equal [
294
- "#coder",
295
- "#cool",
296
- "#food",
297
- "#navy",
298
- "#picnic",
299
- "#swanky"
294
+ "@coder",
295
+ "@cool",
296
+ "@food",
297
+ "@navy",
298
+ "@picnic",
299
+ "@swanky"
300
300
  ]
301
301
  end
302
302
  end
@@ -305,10 +305,10 @@ describe Friends::Introvert do
305
305
 
306
306
  describe 'when from flag is "activities"' do
307
307
  let(:from) { "activities" }
308
- it "lists all activity hashtags in sorted order" do
308
+ it "lists all activity tags in sorted order" do
309
309
  stub_activities(activities) do
310
310
  stub_friends(friends) do
311
- subject.must_equal ["#food", "#picnic", "#swanky"]
311
+ subject.must_equal ["@food", "@picnic", "@swanky"]
312
312
  end
313
313
  end
314
314
  end
@@ -316,10 +316,10 @@ describe Friends::Introvert do
316
316
 
317
317
  describe 'when from flag is "friends"' do
318
318
  let(:from) { "friends" }
319
- it "lists all friend hashtags in sorted order" do
319
+ it "lists all friend tags in sorted order" do
320
320
  stub_activities(activities) do
321
321
  stub_friends(friends) do
322
- subject.must_equal ["#coder", "#cool", "#navy", "#swanky"]
322
+ subject.must_equal ["@coder", "@cool", "@navy", "@swanky"]
323
323
  end
324
324
  end
325
325
  end
@@ -497,7 +497,7 @@ describe Friends::Introvert do
497
497
  end
498
498
  end
499
499
 
500
- describe "when not filtering by a hashtag" do
500
+ describe "when not filtering by a tag" do
501
501
  let(:tagged) { nil }
502
502
 
503
503
  it "lists the activities" do
@@ -507,17 +507,17 @@ describe Friends::Introvert do
507
507
  end
508
508
  end
509
509
 
510
- describe "when filtering by a hashtag" do
510
+ describe "when filtering by a tag" do
511
511
  let(:activities) do
512
512
  [
513
- Friends::Activity.new(str: "Tennis after work. #exercise #tennis"),
514
- Friends::Activity.new(str: "Wimbledon! #tennis"),
515
- Friends::Activity.new(str: "Drinks after work. #beer")
513
+ Friends::Activity.new(str: "Tennis after work. @exercise @tennis"),
514
+ Friends::Activity.new(str: "Wimbledon! @tennis"),
515
+ Friends::Activity.new(str: "Drinks after work. @beer")
516
516
  ]
517
517
  end
518
518
 
519
- describe "when the hashtag ('#hashtag') is not used at all" do
520
- let(:tagged) { "#garbage" }
519
+ describe "when the tag ('@tag') is not used at all" do
520
+ let(:tagged) { "@garbage" }
521
521
  it "returns no results" do
522
522
  stub_activities(activities) do
523
523
  subject.must_equal []
@@ -525,18 +525,18 @@ describe Friends::Introvert do
525
525
  end
526
526
  end
527
527
 
528
- describe "when the hashtag ('#hashtag') is used once" do
529
- let(:tagged) { "#beer" }
530
- it "returns the activity with that hashtag" do
528
+ describe "when the tag ('@tag') is used once" do
529
+ let(:tagged) { "@beer" }
530
+ it "returns the activity with that tag" do
531
531
  stub_activities(activities) do
532
532
  subject.must_equal [activities.last.to_s]
533
533
  end
534
534
  end
535
535
  end
536
536
 
537
- describe "when the hashtag ('#hashtag') is used multiple times" do
538
- let(:tagged) { "#tennis" }
539
- it "returns the activities with that hashtag" do
537
+ describe "when the tag ('@tag') is used multiple times" do
538
+ let(:tagged) { "@tennis" }
539
+ it "returns the activities with that tag" do
540
540
  stub_activities(activities) do
541
541
  subject.must_equal activities[0..1].map(&:to_s)
542
542
  end
@@ -685,9 +685,9 @@ describe Friends::Introvert do
685
685
  end
686
686
  end
687
687
 
688
- describe "#add_hashtag" do
688
+ describe "#add_tag" do
689
689
  subject do
690
- introvert.add_hashtag(name: friend_names.first, hashtag: "#school")
690
+ introvert.add_tag(name: friend_names.first, tag: "@school")
691
691
  end
692
692
 
693
693
  it "returns the modified friend" do
@@ -697,13 +697,13 @@ describe Friends::Introvert do
697
697
  end
698
698
  end
699
699
 
700
- describe "#remove_hashtag" do
700
+ describe "#remove_tag" do
701
701
  subject do
702
- introvert.remove_hashtag(name: "Jeff", hashtag: "#school")
702
+ introvert.remove_tag(name: "Jeff", tag: "@school")
703
703
  end
704
704
 
705
705
  it "returns the modified friend" do
706
- friend = Friends::Friend.new(name: "Jeff", hashtags_str: "#school")
706
+ friend = Friends::Friend.new(name: "Jeff", tags_str: "@school")
707
707
  stub_friends([friend]) do
708
708
  subject.must_equal friend
709
709
  end
@@ -820,18 +820,18 @@ describe Friends::Introvert do
820
820
 
821
821
  let(:activities) do
822
822
  [
823
- # With a friend
823
+ # In a location
824
824
  Friends::Activity.new(
825
825
  str: "2016-01-01: \
826
826
  At _The Eiffel Tower_ with **George Washington Carver**."
827
827
  ),
828
828
 
829
- # In a location
829
+ # Tagged
830
830
  Friends::Activity.new(
831
- str: "2016-01-01: Called **George Washington Carver**. #phone"
831
+ str: "2016-01-01: Called **George Washington Carver**. @phone"
832
832
  ),
833
833
 
834
- # Tagged with a hashtag
834
+ # With a friend
835
835
  Friends::Activity.new(
836
836
  str: "2016-01-01: Hung out with **Betsy Ross**."
837
837
  )
@@ -894,10 +894,10 @@ describe Friends::Introvert do
894
894
  end
895
895
  end
896
896
 
897
- describe "Filtering by hashtag" do
898
- let(:tagged) { "#phone" }
897
+ describe "Filtering by tag" do
898
+ let(:tagged) { "@phone" }
899
899
 
900
- it "graphs activities tagged with a hashtag" do
900
+ it "graphs tagged activities" do
901
901
  stub_activities(activities) do
902
902
  subject.must_equal("Jan 2016" => 1)
903
903
  end
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.23'
4
+ version: '0.24'
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-16 00:00:00.000000000 Z
11
+ date: 2016-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chronic