friends 0.23 → 0.24

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