friends 0.28 → 0.29
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 +4 -4
- data/.rubocop.yml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +2 -0
- data/CHANGELOG.md +13 -0
- data/README.md +35 -5
- data/Rakefile +1 -1
- data/bin/friends +7 -379
- data/friends.gemspec +3 -1
- data/lib/friends/activity.rb +4 -4
- data/lib/friends/commands/add.rb +64 -0
- data/lib/friends/commands/clean.rb +9 -0
- data/lib/friends/commands/edit.rb +12 -0
- data/lib/friends/commands/graph.rb +56 -0
- data/lib/friends/commands/list.rb +143 -0
- data/lib/friends/commands/remove.rb +27 -0
- data/lib/friends/commands/rename.rb +30 -0
- data/lib/friends/commands/set.rb +14 -0
- data/lib/friends/commands/stats.rb +11 -0
- data/lib/friends/commands/suggest.rb +20 -0
- data/lib/friends/commands/update.rb +29 -0
- data/lib/friends/graph.rb +7 -7
- data/lib/friends/introvert.rb +23 -18
- data/lib/friends/version.rb +1 -1
- data/test/commands/add/activity_spec.rb +379 -0
- data/test/commands/add/friend_spec.rb +30 -0
- data/test/commands/add/location_spec.rb +30 -0
- data/test/commands/add/nickname_spec.rb +50 -0
- data/test/commands/add/tag_spec.rb +65 -0
- data/test/commands/clean_spec.rb +39 -0
- data/test/commands/graph_spec.rb +147 -0
- data/test/commands/help_spec.rb +45 -0
- data/test/commands/list/activities_spec.rb +136 -0
- data/test/commands/list/favorite/friends_spec.rb +77 -0
- data/test/commands/list/favorite/locations_spec.rb +82 -0
- data/test/commands/list/friends_spec.rb +76 -0
- data/test/commands/list/locations_spec.rb +35 -0
- data/test/commands/list/tags_spec.rb +58 -0
- data/test/commands/remove/nickname_spec.rb +63 -0
- data/test/commands/remove/tag_spec.rb +64 -0
- data/test/commands/rename/friend_spec.rb +55 -0
- data/test/commands/rename/location_spec.rb +43 -0
- data/test/commands/set/location_spec.rb +54 -0
- data/test/commands/stats_spec.rb +41 -0
- data/test/commands/suggest_spec.rb +86 -0
- data/test/commands/update_spec.rb +13 -0
- data/test/helper.rb +114 -0
- metadata +89 -15
- data/test/activity_spec.rb +0 -597
- data/test/friend_spec.rb +0 -241
- data/test/graph_spec.rb +0 -92
- data/test/introvert_spec.rb +0 -969
- data/test/location_spec.rb +0 -60
data/friends.gemspec
CHANGED
@@ -25,13 +25,15 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
spec.add_dependency "chronic", "~> 0.10"
|
27
27
|
spec.add_dependency "gli", "~> 2.12"
|
28
|
-
spec.add_dependency "memoist", "~> 0.
|
28
|
+
spec.add_dependency "memoist", "~> 0.15"
|
29
29
|
spec.add_dependency "paint", "~> 1.0"
|
30
30
|
spec.add_dependency "semverse", "~> 1.2"
|
31
31
|
|
32
32
|
spec.add_development_dependency "bundler", "~> 1.6"
|
33
33
|
spec.add_development_dependency "codeclimate-test-reporter", "~> 0.5"
|
34
34
|
spec.add_development_dependency "minitest", "~> 5.5"
|
35
|
+
spec.add_development_dependency "minitest-parallel_fork", "~> 1.0"
|
36
|
+
spec.add_development_dependency "minitest-proveit", "~> 1.0"
|
35
37
|
spec.add_development_dependency "overcommit", "~> 0.34"
|
36
38
|
spec.add_development_dependency "rake", "~> 11.2"
|
37
39
|
spec.add_development_dependency "rubocop", "~> 0.40"
|
data/lib/friends/activity.rb
CHANGED
@@ -254,10 +254,10 @@ module Friends
|
|
254
254
|
position = 0 # Prevent infinite looping by tracking last match position.
|
255
255
|
loop do
|
256
256
|
# Only make a replacement if we're not between a set of "**"s or "_"s.
|
257
|
-
if match.pre_match.scan("**").size % 2
|
258
|
-
match.post_match.scan("**").size % 2
|
259
|
-
match.pre_match.scan("_").size % 2
|
260
|
-
match.post_match.scan("_").size % 2
|
257
|
+
if (match.pre_match.scan("**").size % 2).zero? &&
|
258
|
+
(match.post_match.scan("**").size % 2).zero? &&
|
259
|
+
(match.pre_match.scan("_").size % 2).zero? &&
|
260
|
+
(match.post_match.scan("_").size % 2).zero?
|
261
261
|
@description = [
|
262
262
|
match.pre_match,
|
263
263
|
indicator,
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Adds a friend (or nickname), activity, or location"
|
4
|
+
command :add do |add|
|
5
|
+
add.desc "Adds a friend"
|
6
|
+
add.arg_name "NAME"
|
7
|
+
add.command :friend do |add_friend|
|
8
|
+
add_friend.action do |_, _, args|
|
9
|
+
friend = @introvert.add_friend(name: args.join(" "))
|
10
|
+
@message = "Friend added: \"#{friend.name}\""
|
11
|
+
@dirty = true # Mark the file for cleaning.
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
add.desc "Adds an activity"
|
16
|
+
add.arg_name "DESCRIPTION"
|
17
|
+
add.command :activity do |add_activity|
|
18
|
+
add_activity.action do |_, _, args|
|
19
|
+
activity = @introvert.add_activity(serialization: args.join(" "))
|
20
|
+
|
21
|
+
# If there's no description, prompt the user for one.
|
22
|
+
if activity.description.nil? || activity.description.empty?
|
23
|
+
activity.description = Readline.readline(activity.to_s)
|
24
|
+
activity.highlight_description(introvert: @introvert)
|
25
|
+
end
|
26
|
+
|
27
|
+
@message = "Activity added: \"#{activity}\""
|
28
|
+
@dirty = true # Mark the file for cleaning.
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
add.desc "Adds a location"
|
33
|
+
add.arg_name "LOCATION"
|
34
|
+
add.command :location do |add_location|
|
35
|
+
add_location.action do |_, _, args|
|
36
|
+
location = @introvert.add_location(name: args.join(" "))
|
37
|
+
@message = "Location added: \"#{location.name}\""
|
38
|
+
@dirty = true # Mark the file for cleaning.
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
add.desc "Adds a nickname to a friend"
|
43
|
+
add.arg_name "NAME NICKNAME"
|
44
|
+
add.command :nickname do |add_nickname|
|
45
|
+
add_nickname.action do |_, _, args|
|
46
|
+
friend = @introvert.add_nickname(name: args.first, nickname: args[1])
|
47
|
+
@message = "Nickname added: \"#{friend}\""
|
48
|
+
@dirty = true # Mark the file for cleaning.
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
add.desc "Adds a tag to a friend"
|
53
|
+
add.arg_name "NAME @TAG"
|
54
|
+
add.command :tag do |add_tag|
|
55
|
+
add_tag.action do |_, _, args|
|
56
|
+
friend = @introvert.add_tag(
|
57
|
+
name: args[0..-2].join(" "),
|
58
|
+
tag: Tag.convert_to_tag(args.last)
|
59
|
+
)
|
60
|
+
@message = "Tag added to friend: \"#{friend}\""
|
61
|
+
@dirty = true # Mark the file for cleaning.
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Opens the `friends.md` file in $EDITOR for manual editing"
|
4
|
+
command :edit do |edit|
|
5
|
+
edit.action do |global_options|
|
6
|
+
editor = ENV["EDITOR"] || "vim"
|
7
|
+
filename = global_options[:filename]
|
8
|
+
|
9
|
+
puts "Opening \"#{filename}\" in #{editor}" unless global_options[:quiet]
|
10
|
+
Kernel.exec "#{editor} #{filename}"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Graph activities over time"
|
4
|
+
command :graph do |graph|
|
5
|
+
graph.flag [:with],
|
6
|
+
arg_name: "NAME",
|
7
|
+
desc: "Graph activities with the given friend",
|
8
|
+
type: Stripped
|
9
|
+
|
10
|
+
graph.flag [:in],
|
11
|
+
arg_name: "LOCATION",
|
12
|
+
desc: "Graph activities in the given location",
|
13
|
+
type: Stripped
|
14
|
+
|
15
|
+
graph.flag [:tagged],
|
16
|
+
arg_name: "@TAG",
|
17
|
+
desc: "Graph activities with the given tag",
|
18
|
+
type: Tag
|
19
|
+
|
20
|
+
graph.flag [:since],
|
21
|
+
arg_name: "DATE",
|
22
|
+
desc: "Graph activities on or after the given date",
|
23
|
+
type: InputDate
|
24
|
+
|
25
|
+
graph.flag [:until],
|
26
|
+
arg_name: "DATE",
|
27
|
+
desc: "Graph activities before or on the given date",
|
28
|
+
type: InputDate
|
29
|
+
|
30
|
+
graph.action do |_, options|
|
31
|
+
# This math is taken from Minitest's Pride plugin (the PrideLOL class).
|
32
|
+
PI_3 = Math::PI / 3
|
33
|
+
|
34
|
+
colors = (0...(6 * 7)).map do |n|
|
35
|
+
n *= 1.0 / 6
|
36
|
+
r = (3 * Math.sin(n) + 3).to_i
|
37
|
+
g = (3 * Math.sin(n + 2 * PI_3) + 3).to_i
|
38
|
+
b = (3 * Math.sin(n + 4 * PI_3) + 3).to_i
|
39
|
+
|
40
|
+
[r, g, b].map { |c| c * 51 }
|
41
|
+
end
|
42
|
+
|
43
|
+
data = @introvert.graph(
|
44
|
+
with: options[:with],
|
45
|
+
location_name: options[:in],
|
46
|
+
tagged: options[:tagged],
|
47
|
+
since_date: options[:since],
|
48
|
+
until_date: options[:until]
|
49
|
+
)
|
50
|
+
|
51
|
+
data.each do |month, count|
|
52
|
+
print "#{month} |"
|
53
|
+
puts colors.take(count).map { |rgb| Paint["█", rgb] }.join
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Lists friends, activities, and locations"
|
4
|
+
command :list do |list|
|
5
|
+
list.desc "List all friends"
|
6
|
+
list.command :friends do |list_friends|
|
7
|
+
list_friends.flag [:in],
|
8
|
+
arg_name: "LOCATION",
|
9
|
+
desc: "List only friends in the given location",
|
10
|
+
type: Stripped
|
11
|
+
|
12
|
+
list_friends.flag [:tagged],
|
13
|
+
arg_name: "@TAG",
|
14
|
+
desc: "List only friends with the given tag",
|
15
|
+
type: Tag
|
16
|
+
|
17
|
+
list_friends.switch [:verbose],
|
18
|
+
negatable: false,
|
19
|
+
desc: "Output friend nicknames, locations, and tags"
|
20
|
+
|
21
|
+
list_friends.action do |_, options|
|
22
|
+
puts @introvert.list_friends(
|
23
|
+
location_name: options[:in],
|
24
|
+
tagged: options[:tagged],
|
25
|
+
verbose: options[:verbose]
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
list.desc "Lists all activities"
|
31
|
+
list.command :activities do |list_activities|
|
32
|
+
list_activities.flag [:limit],
|
33
|
+
arg_name: "NUMBER",
|
34
|
+
desc: "The number of activities to return",
|
35
|
+
default_value: 10,
|
36
|
+
type: Integer
|
37
|
+
|
38
|
+
list_activities.flag [:with],
|
39
|
+
arg_name: "NAME",
|
40
|
+
desc: "List only activities with the given friend",
|
41
|
+
type: Stripped
|
42
|
+
|
43
|
+
list_activities.flag [:in],
|
44
|
+
arg_name: "LOCATION",
|
45
|
+
desc: "List only activities in the given location",
|
46
|
+
type: Stripped
|
47
|
+
|
48
|
+
list_activities.flag [:tagged],
|
49
|
+
arg_name: "@TAG",
|
50
|
+
desc: "List only activities with the given tag",
|
51
|
+
type: Tag
|
52
|
+
|
53
|
+
list_activities.flag [:since],
|
54
|
+
arg_name: "DATE",
|
55
|
+
desc: "List only activities on or after the given date",
|
56
|
+
type: InputDate
|
57
|
+
|
58
|
+
list_activities.flag [:until],
|
59
|
+
arg_name: "DATE",
|
60
|
+
desc: "List only activities before or on the given date",
|
61
|
+
type: InputDate
|
62
|
+
|
63
|
+
list_activities.action do |_, options|
|
64
|
+
puts @introvert.list_activities(
|
65
|
+
limit: options[:limit],
|
66
|
+
with: options[:with],
|
67
|
+
location_name: options[:in],
|
68
|
+
tagged: options[:tagged],
|
69
|
+
since_date: options[:since],
|
70
|
+
until_date: options[:until]
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
list.desc "List all locations"
|
76
|
+
list.command :locations do |list_locations|
|
77
|
+
list_locations.action do
|
78
|
+
puts @introvert.list_locations
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
list.desc "List all tags used"
|
83
|
+
list.command :tags do |list_tags|
|
84
|
+
list_tags.flag [:from],
|
85
|
+
arg_name: '"activities" or "friends" (default: both)',
|
86
|
+
desc: "List only tags from activities or friends instead of"\
|
87
|
+
"both"
|
88
|
+
list_tags.action do |_, options|
|
89
|
+
puts @introvert.list_tags(from: options[:from])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
list.desc "List favorite friends and locations"
|
94
|
+
list.command :favorite do |list_favorite|
|
95
|
+
list_favorite.desc "List favorite friends"
|
96
|
+
list_favorite.command :friends do |list_favorite_friends|
|
97
|
+
list_favorite_friends.flag [:limit],
|
98
|
+
arg_name: "NUMBER",
|
99
|
+
desc: "The number of friends to return",
|
100
|
+
default_value: 10,
|
101
|
+
type: Integer
|
102
|
+
|
103
|
+
list_favorite_friends.action do |_, options|
|
104
|
+
favorites = @introvert.list_favorite_friends(limit: options[:limit])
|
105
|
+
|
106
|
+
if options[:limit] == 1
|
107
|
+
puts "Your best friend is #{favorites.first}"
|
108
|
+
else
|
109
|
+
puts "Your favorite friends:"
|
110
|
+
|
111
|
+
num_str_size = favorites.size.to_s.size + 1
|
112
|
+
favorites.each.with_index(1) do |name, rank|
|
113
|
+
puts "#{"#{rank}.".ljust(num_str_size)} #{name}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
list_favorite.desc "List favorite locations"
|
120
|
+
list_favorite.command :locations do |list_favorite_locations|
|
121
|
+
list_favorite_locations.flag [:limit],
|
122
|
+
arg_name: "NUMBER",
|
123
|
+
desc: "The number of locations to return",
|
124
|
+
default_value: 10,
|
125
|
+
type: Integer
|
126
|
+
|
127
|
+
list_favorite_locations.action do |_, options|
|
128
|
+
favorites = @introvert.list_favorite_locations(limit: options[:limit])
|
129
|
+
|
130
|
+
if options[:limit] == 1
|
131
|
+
puts "Your favorite location is #{favorites.first}"
|
132
|
+
else
|
133
|
+
puts "Your favorite locations:"
|
134
|
+
|
135
|
+
num_str_size = favorites.size.to_s.size + 1
|
136
|
+
favorites.each.with_index(1) do |name, rank|
|
137
|
+
puts "#{"#{rank}.".ljust(num_str_size)} #{name}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Remove a nickname"
|
4
|
+
command :remove do |remove|
|
5
|
+
remove.desc "Removes a nickname from a friend"
|
6
|
+
remove.arg_name "NAME NICKNAME"
|
7
|
+
remove.command :nickname do |remove_nickname|
|
8
|
+
remove_nickname.action do |_, _, args|
|
9
|
+
friend = @introvert.remove_nickname(name: args.first, nickname: args[1])
|
10
|
+
@message = "Nickname removed: \"#{friend}\""
|
11
|
+
@dirty = true # Mark the file for cleaning.
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
remove.desc "Removes a tag from a friend"
|
16
|
+
remove.arg_name "NAME @TAG"
|
17
|
+
remove.command :tag do |remove_tag|
|
18
|
+
remove_tag.action do |_, _, args|
|
19
|
+
friend = @introvert.remove_tag(
|
20
|
+
name: args[0..-2].join(" "),
|
21
|
+
tag: Tag.convert_to_tag(args.last)
|
22
|
+
)
|
23
|
+
@message = "Tag removed from friend: \"#{friend}\""
|
24
|
+
@dirty = true # Mark the file for cleaning.
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Renames a friend or location"
|
4
|
+
command :rename do |rename|
|
5
|
+
rename.desc "Renames a friend"
|
6
|
+
rename.arg_name "NAME NEW_NAME"
|
7
|
+
rename.command :friend do |rename_friend|
|
8
|
+
rename_friend.action do |_, _, args|
|
9
|
+
friend = @introvert.rename_friend(
|
10
|
+
old_name: args.first,
|
11
|
+
new_name: args[1]
|
12
|
+
)
|
13
|
+
@message = "Name changed: \"#{friend}\""
|
14
|
+
@dirty = true # Mark the file for cleaning.
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
rename.desc "Renames a location"
|
19
|
+
rename.arg_name "NAME NEW_NAME"
|
20
|
+
rename.command :location do |rename_location|
|
21
|
+
rename_location.action do |_, _, args|
|
22
|
+
location = @introvert.rename_location(
|
23
|
+
old_name: args.first,
|
24
|
+
new_name: args[1]
|
25
|
+
)
|
26
|
+
@message = "Location renamed: \"#{location.name}\""
|
27
|
+
@dirty = true # Mark the file for cleaning.
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Set data about friends"
|
4
|
+
command :set do |set|
|
5
|
+
set.desc "Set a friend's location"
|
6
|
+
set.arg_name "NAME LOCATION"
|
7
|
+
set.command :location do |set_location|
|
8
|
+
set_location.action do |_, _, args|
|
9
|
+
friend = @introvert.set_location(name: args.first, location_name: args[1])
|
10
|
+
@message = "#{friend.name}'s location set to: #{friend.location_name}"
|
11
|
+
@dirty = true # Mark the file for cleaning.
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "List all stats"
|
4
|
+
command :stats do |stats|
|
5
|
+
stats.action do
|
6
|
+
puts "Total activities: #{@introvert.total_activities}"
|
7
|
+
puts "Total friends: #{@introvert.total_friends}"
|
8
|
+
days = @introvert.elapsed_days
|
9
|
+
puts "Total time elapsed: #{days} day#{'s' if days != 1}"
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Suggest friends to do activities with"
|
4
|
+
command :suggest do |suggest|
|
5
|
+
suggest.flag [:in],
|
6
|
+
arg_name: "LOCATION",
|
7
|
+
desc: "Suggest only friends in the given location",
|
8
|
+
type: Stripped
|
9
|
+
|
10
|
+
suggest.action do |_, options|
|
11
|
+
suggestions = @introvert.suggest(location_name: options[:in])
|
12
|
+
|
13
|
+
puts "Distant friend: "\
|
14
|
+
"#{Paint[suggestions[:distant].sample || 'None found', :bold, :magenta]}"
|
15
|
+
puts "Moderate friend: "\
|
16
|
+
"#{Paint[suggestions[:moderate].sample || 'None found', :bold, :magenta]}"
|
17
|
+
puts "Close friend: "\
|
18
|
+
"#{Paint[suggestions[:close].sample || 'None found', :bold, :magenta]}"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Updates the `friends` program"
|
4
|
+
command :update do |update|
|
5
|
+
update.action do
|
6
|
+
# rubocop:disable Lint/AssignmentInCondition
|
7
|
+
if match = `gem search friends`.match(/^friends\s\(([^\)]+)\)$/)
|
8
|
+
# rubocop:enable Lint/AssignmentInCondition
|
9
|
+
remote_version = match[1]
|
10
|
+
if Semverse::Version.coerce(remote_version) >
|
11
|
+
Semverse::Version.coerce(Friends::VERSION)
|
12
|
+
`gem update friends && gem cleanup friends`
|
13
|
+
@message = if $?.success?
|
14
|
+
Paint[
|
15
|
+
"Updated to friends #{remote_version}", :bold, :green
|
16
|
+
]
|
17
|
+
else
|
18
|
+
Paint[
|
19
|
+
"Error updating to friends version #{remote_version}", :bold, :red
|
20
|
+
]
|
21
|
+
end
|
22
|
+
else
|
23
|
+
@message = Paint[
|
24
|
+
"Already up-to-date (#{Friends::VERSION})", :bold, :green
|
25
|
+
]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|