usaidwat 1.3.0 → 1.4.0
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/.gitattributes +1 -0
- data/README.textile +7 -2
- data/bin/usaidwat +2 -1
- data/features/fixtures/submissions_blank.json +1 -0
- data/features/fixtures/submissions_mipadi.json +1 -0
- data/features/fixtures/submissions_testuser.json +1 -0
- data/features/help.feature +5 -2
- data/features/log.feature +2 -1
- data/features/posts.feature +349 -0
- data/features/step_definitions/time_steps.rb +1 -1
- data/features/tally.feature +12 -6
- data/features/user.feature +2 -1
- data/lib/usaidwat.rb +1 -2
- data/lib/usaidwat/application.rb +87 -61
- data/lib/usaidwat/client.rb +8 -0
- data/lib/usaidwat/count.rb +27 -0
- data/lib/usaidwat/ext/array.rb +5 -0
- data/lib/usaidwat/ext/mercenary.rb +16 -0
- data/lib/usaidwat/filter.rb +40 -0
- data/lib/usaidwat/formatter.rb +42 -1
- data/lib/usaidwat/service.rb +17 -0
- data/lib/usaidwat/version.rb +1 -1
- data/spec/usaidwat/array_spec.rb +36 -0
- data/spec/usaidwat/client_spec.rb +24 -0
- data/spec/usaidwat/count_spec.rb +61 -0
- data/spec/usaidwat/filter_spec.rb +76 -0
- data/spec/usaidwat/formatter_spec.rb +92 -0
- data/usaidwat.gemspec +2 -2
- metadata +40 -26
- data/features/support/time.rb +0 -5
- data/lib/usaidwat/terminal.rb +0 -19
- data/spec/usaidwat/terminal_spec.rb +0 -21
data/features/tally.feature
CHANGED
@@ -63,7 +63,8 @@ Feature: Tally comments
|
|
63
63
|
Scenario: Search for a comment when tallying
|
64
64
|
Given the Reddit service returns comments for the user "mipadi"
|
65
65
|
When I run `usaidwat tally --grep='Heisenbug' mipadi`
|
66
|
-
Then
|
66
|
+
Then the exit status should not be 0
|
67
|
+
And stderr should contain:
|
67
68
|
"""
|
68
69
|
invalid option: --grep=Heisenbug
|
69
70
|
"""
|
@@ -71,7 +72,8 @@ Feature: Tally comments
|
|
71
72
|
Scenario: Search for a comment when sorting
|
72
73
|
Given the Reddit service returns comments for the user "mipadi"
|
73
74
|
When I run `usaidwat tally -c --grep='Heisenbug' mipadi`
|
74
|
-
Then
|
75
|
+
Then the exit status should not be 0
|
76
|
+
And stderr should contain:
|
75
77
|
"""
|
76
78
|
invalid option: --grep=Heisenbug
|
77
79
|
"""
|
@@ -87,7 +89,8 @@ Feature: Tally comments
|
|
87
89
|
Scenario: Tally comments with subreddit
|
88
90
|
Given the Reddit service returns comments for the user "mipadi"
|
89
91
|
When I run `usaidwat tally mipadi AskReddit`
|
90
|
-
Then
|
92
|
+
Then the exit status should not be 0
|
93
|
+
And stderr should contain exactly:
|
91
94
|
"""
|
92
95
|
You cannot specify a subreddit when tallying comments
|
93
96
|
"""
|
@@ -95,7 +98,8 @@ Feature: Tally comments
|
|
95
98
|
Scenario: Sort comments with subreddit
|
96
99
|
Given the Reddit service returns comments for the user "mipadi"
|
97
100
|
When I run `usaidwat tally -c mipadi AskReddit`
|
98
|
-
Then
|
101
|
+
Then the exit status should not be 0
|
102
|
+
And stderr should contain exactly:
|
99
103
|
"""
|
100
104
|
You cannot specify a subreddit when tallying comments
|
101
105
|
"""
|
@@ -103,7 +107,8 @@ Feature: Tally comments
|
|
103
107
|
Scenario: Pass no arguments when tallying
|
104
108
|
Given the Reddit service returns comments for the user "mipadi"
|
105
109
|
When I run `usaidwat tally`
|
106
|
-
Then
|
110
|
+
Then the exit status should not be 0
|
111
|
+
And stderr should contain exactly:
|
107
112
|
"""
|
108
113
|
You must specify a username
|
109
114
|
"""
|
@@ -111,7 +116,8 @@ Feature: Tally comments
|
|
111
116
|
Scenario: Pass no arguments when sorting
|
112
117
|
Given the Reddit service returns comments for the user "mipadi"
|
113
118
|
When I run `usaidwat tally -c`
|
114
|
-
Then
|
119
|
+
Then the exit status should not be 0
|
120
|
+
And stderr should contain exactly:
|
115
121
|
"""
|
116
122
|
You must specify a username
|
117
123
|
"""
|
data/features/user.feature
CHANGED
@@ -27,7 +27,8 @@ Feature: Display user information
|
|
27
27
|
Scenario: Fail to pass a username when querying for information
|
28
28
|
Given the Reddit service returns information for the user "mipadi"
|
29
29
|
When I run `usaidwat info`
|
30
|
-
Then
|
30
|
+
Then the exit status should not be 0
|
31
|
+
And stderr should contain exactly:
|
31
32
|
"""
|
32
33
|
You must specify a username
|
33
34
|
"""
|
data/lib/usaidwat.rb
CHANGED
data/lib/usaidwat/application.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'sysexits'
|
2
2
|
require 'usaidwat/client'
|
3
|
+
require 'usaidwat/count'
|
3
4
|
require 'usaidwat/either'
|
5
|
+
require 'usaidwat/filter'
|
4
6
|
require 'usaidwat/pager'
|
5
|
-
require '
|
7
|
+
require 'usaidwat/ext/array'
|
8
|
+
|
9
|
+
require 'timecop' if ENV['USAIDWAT_ENV'] == 'cucumber'
|
6
10
|
|
7
11
|
module USaidWat
|
8
12
|
module Application
|
@@ -25,6 +29,7 @@ module USaidWat
|
|
25
29
|
|
26
30
|
def initialize(prog)
|
27
31
|
@client = cucumber? ? USaidWat::Client::TestRedditor : USaidWat::Client::Redditor
|
32
|
+
Timecop.freeze(Time.parse(ENV['USAIDWAT_CURRENT_TIME'])) if cucumber_time?
|
28
33
|
end
|
29
34
|
|
30
35
|
protected
|
@@ -33,8 +38,13 @@ module USaidWat
|
|
33
38
|
ENV['USAIDWAT_ENV'] == 'cucumber'
|
34
39
|
end
|
35
40
|
|
41
|
+
def cucumber_time?
|
42
|
+
cucumber? && !ENV['USAIDWAT_CURRENT_TIME'].nil?
|
43
|
+
end
|
44
|
+
|
36
45
|
def quit(message, code=:ok)
|
37
|
-
|
46
|
+
stream = code == :ok ? $stdout : $stderr
|
47
|
+
stream.puts message
|
38
48
|
exit code
|
39
49
|
end
|
40
50
|
end
|
@@ -64,6 +74,8 @@ module USaidWat
|
|
64
74
|
end
|
65
75
|
|
66
76
|
class Log < Command
|
77
|
+
include FilterCommand
|
78
|
+
|
67
79
|
def initialize(prog)
|
68
80
|
prog.command(:log) do |c|
|
69
81
|
c.alias :l
|
@@ -83,15 +95,15 @@ module USaidWat
|
|
83
95
|
def process(options, args)
|
84
96
|
raise ArgumentError.new('You must specify a username') if args.empty?
|
85
97
|
username = args.shift
|
86
|
-
subreddits = args.
|
98
|
+
subreddits = args.subreddits
|
87
99
|
|
88
100
|
redditor = client.new(username)
|
89
101
|
comments = redditor.comments
|
90
102
|
|
91
|
-
res =
|
92
|
-
lambda { |r|
|
93
|
-
lambda { |r|
|
94
|
-
lambda { |r|
|
103
|
+
res = filter_entries('comments', redditor, comments, subreddits) >>
|
104
|
+
lambda { |r| grep_entries('comments', redditor, r.value, options['grep']) } >>
|
105
|
+
lambda { |r| limit_entries('comments', redditor, r.value, options['limit']) } >>
|
106
|
+
lambda { |r| ensure_entries('comments', redditor, r.value) }
|
95
107
|
|
96
108
|
quit res.value if res.left?
|
97
109
|
|
@@ -108,50 +120,83 @@ module USaidWat
|
|
108
120
|
|
109
121
|
private
|
110
122
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
else
|
117
|
-
USaidWat::Right.new(comments)
|
118
|
-
end
|
123
|
+
def list_comments(comments, options = {})
|
124
|
+
oneline = options[:oneline]
|
125
|
+
formatter = (oneline ? USaidWat::CLI::CompactCommentFormatter : USaidWat::CLI::CommentFormatter).new(options)
|
126
|
+
page
|
127
|
+
comments.each { |c| print formatter.format(c) }
|
119
128
|
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Posts < Command
|
132
|
+
include CountCommand
|
133
|
+
include FilterCommand
|
120
134
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
135
|
+
def initialize(prog)
|
136
|
+
prog.command(:posts) do |c|
|
137
|
+
c.action do |args, options|
|
138
|
+
process(options, args)
|
139
|
+
end
|
140
|
+
|
141
|
+
c.command(:log) do |s|
|
142
|
+
s.description "Show a user's submitted posts"
|
143
|
+
s.action do |args, options|
|
144
|
+
process_log(options, args)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
c.command(:tally) do |s|
|
149
|
+
s.description "Tally a user's posts by subreddit"
|
150
|
+
s.option 'count', '-c', '--count', 'Sort output by number of comments'
|
151
|
+
s.action do |args, options|
|
152
|
+
process_tally(options, args)
|
153
|
+
end
|
154
|
+
end
|
129
155
|
end
|
156
|
+
super
|
130
157
|
end
|
131
158
|
|
132
|
-
def
|
133
|
-
|
134
|
-
comments = comments[0...n.to_i]
|
135
|
-
USaidWat::Right.new(comments)
|
159
|
+
def process(options, args)
|
160
|
+
quit "Do you want to tally or log posts?", :usage
|
136
161
|
end
|
137
162
|
|
138
|
-
def
|
139
|
-
if
|
140
|
-
|
141
|
-
|
142
|
-
USaidWat::Right.new(comments)
|
143
|
-
end
|
144
|
-
end
|
163
|
+
def process_log(options, args)
|
164
|
+
raise ArgumentError.new('You must specify a username') if args.empty?
|
165
|
+
username = args.shift
|
166
|
+
subreddits = args.subreddits
|
145
167
|
|
146
|
-
|
147
|
-
|
148
|
-
|
168
|
+
redditor = client.new(username)
|
169
|
+
posts = redditor.posts
|
170
|
+
|
171
|
+
res = filter_entries('posts', redditor, posts, subreddits) >>
|
172
|
+
lambda { |r| ensure_entries('posts', redditor, r.value) }
|
173
|
+
|
174
|
+
quit res.value if res.left?
|
175
|
+
posts = res.value
|
176
|
+
|
177
|
+
formatter = USaidWat::CLI::PostFormatter.new
|
149
178
|
page
|
150
|
-
|
179
|
+
posts.each { |p| print formatter.format(p) }
|
180
|
+
end
|
181
|
+
|
182
|
+
def process_tally(options, args)
|
183
|
+
raise ArgumentError.new('You must specify a username') if args.empty?
|
184
|
+
raise ArgumentError.new('You cannot specify a subreddit when tallying comments') if args.count > 1
|
185
|
+
username = args.first
|
186
|
+
|
187
|
+
redditor = client.new(username)
|
188
|
+
quit "#{redditor.username} has no posts." if redditor.posts.empty?
|
189
|
+
partition_data = partition(redditor.posts, options['count'])
|
190
|
+
formatter = USaidWat::CLI::TallyFormatter.new
|
191
|
+
print formatter.format(partition_data)
|
192
|
+
rescue USaidWat::Client::NoSuchUserError
|
193
|
+
quit "No such user: #{username}", :no_such_user
|
151
194
|
end
|
152
195
|
end
|
153
196
|
|
154
197
|
class Tally < Command
|
198
|
+
include CountCommand
|
199
|
+
|
155
200
|
def initialize(prog)
|
156
201
|
prog.command(:tally) do |c|
|
157
202
|
c.alias :t
|
@@ -171,31 +216,12 @@ module USaidWat
|
|
171
216
|
|
172
217
|
redditor = client.new(username)
|
173
218
|
quit "#{redditor.username} has no comments." if redditor.comments.empty?
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
longest_subreddit = 0
|
178
|
-
buckets = Hash.new { |hash, key| hash[key] = 0 }
|
179
|
-
redditor.comments.each do |comment|
|
180
|
-
subreddit = comment.subreddit
|
181
|
-
longest_subreddit = subreddit.length if subreddit.length > longest_subreddit
|
182
|
-
buckets[subreddit] += 1
|
183
|
-
end
|
184
|
-
algo = algorithm(options['count']).new(buckets)
|
185
|
-
subreddits = buckets.keys.sort { |a,b| algo.sort(a, b) }
|
186
|
-
subreddits.each do |subreddit|
|
187
|
-
tally = buckets[subreddit]
|
188
|
-
printf "%-*s %3d\n", longest_subreddit, subreddit, tally
|
189
|
-
end
|
219
|
+
partition_data = partition(redditor.comments, options['count'])
|
220
|
+
formatter = USaidWat::CLI::TallyFormatter.new
|
221
|
+
print formatter.format(partition_data)
|
190
222
|
rescue USaidWat::Client::NoSuchUserError
|
191
223
|
quit "No such user: #{username}", :no_such_user
|
192
224
|
end
|
193
|
-
|
194
|
-
private
|
195
|
-
|
196
|
-
def algorithm(count)
|
197
|
-
count ? USaidWat::Algorithms::CountAlgorithm : USaidWat::Algorithms::LexicographicalAlgorithm
|
198
|
-
end
|
199
225
|
end
|
200
226
|
end
|
201
227
|
end
|
data/lib/usaidwat/client.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'usaidwat/algo'
|
2
|
+
|
3
|
+
module USaidWat
|
4
|
+
module Application
|
5
|
+
module CountCommand
|
6
|
+
def partition(entries, sort_by_count)
|
7
|
+
longest_subreddit = 0
|
8
|
+
buckets = Hash.new { |hash, key| hash[key] = 0 }
|
9
|
+
entries.each do |e|
|
10
|
+
subreddit = e.subreddit
|
11
|
+
longest_subreddit = subreddit.length if subreddit.length > longest_subreddit
|
12
|
+
buckets[subreddit] += 1
|
13
|
+
end
|
14
|
+
algo = algorithm(sort_by_count).new(buckets)
|
15
|
+
subreddits = buckets.keys.sort { |a,b| algo.sort(a, b) }
|
16
|
+
counts = subreddits.map { |s| buckets[s] }
|
17
|
+
subreddit_counts = subreddits.zip(counts)
|
18
|
+
partition_data = Struct.new(:longest, :counts)
|
19
|
+
partition_data.new(longest_subreddit, subreddit_counts)
|
20
|
+
end
|
21
|
+
|
22
|
+
def algorithm(sort_by_count)
|
23
|
+
sort_by_count ? USaidWat::Algorithms::CountAlgorithm : USaidWat::Algorithms::LexicographicalAlgorithm
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Mercenary
|
2
|
+
class Command
|
3
|
+
def logger(level = nil)
|
4
|
+
unless @logger
|
5
|
+
@logger = Logger.new(STDERR)
|
6
|
+
@logger.level = level || Logger::INFO
|
7
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
8
|
+
"#{msg}\n"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
@logger.level = level unless level.nil?
|
13
|
+
@logger
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module USaidWat
|
2
|
+
module Application
|
3
|
+
module FilterCommand
|
4
|
+
def filter_entries(noun, redditor, entries, subreddits)
|
5
|
+
return USaidWat::Right.new(entries) if subreddits.empty?
|
6
|
+
entries = entries.find_all { |e| subreddits.include?(e.subreddit.downcase) }
|
7
|
+
if entries.empty?
|
8
|
+
USaidWat::Left.new("No #{noun} by #{redditor.username} for #{subreddits.join(', ')}.")
|
9
|
+
else
|
10
|
+
USaidWat::Right.new(entries)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def grep_entries(noun, redditor, entries, grep)
|
15
|
+
return USaidWat::Right.new(entries) if grep.nil?
|
16
|
+
entries = entries.select { |e| e.body =~ /#{grep}/i }
|
17
|
+
if entries.empty?
|
18
|
+
msg = "#{redditor.username} has no #{noun} matching /#{grep}/."
|
19
|
+
USaidWat::Left.new(msg)
|
20
|
+
else
|
21
|
+
USaidWat::Right.new(entries)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def limit_entries(noun, redditor, entries, n)
|
26
|
+
return USaidWat::Right.new(entries) if n.nil?
|
27
|
+
entries = entries[0...n.to_i]
|
28
|
+
USaidWat::Right.new(entries)
|
29
|
+
end
|
30
|
+
|
31
|
+
def ensure_entries(noun, redditor, entries)
|
32
|
+
if entries.empty?
|
33
|
+
USaidWat::Left.new("#{redditor.username} has no #{noun}.")
|
34
|
+
else
|
35
|
+
USaidWat::Right.new(entries)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/usaidwat/formatter.rb
CHANGED
@@ -4,6 +4,7 @@ require 'rainbow/ext/string'
|
|
4
4
|
require 'redcarpet'
|
5
5
|
require 'set'
|
6
6
|
require 'stringio'
|
7
|
+
require 'ttycaca'
|
7
8
|
require 'usaidwat/ext/string'
|
8
9
|
require 'usaidwat/ext/time'
|
9
10
|
|
@@ -36,7 +37,32 @@ module USaidWat
|
|
36
37
|
protected
|
37
38
|
|
38
39
|
def tty
|
39
|
-
@tty ||=
|
40
|
+
@tty ||= Ttycaca::Terminal.new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class PostFormatter < BaseFormatter
|
45
|
+
def format(post)
|
46
|
+
cols = tty.width
|
47
|
+
out = StringIO.new
|
48
|
+
out.write("\n\n\n") unless @count == 0
|
49
|
+
out.write("#{post.subreddit}\n".color(:green))
|
50
|
+
out.write("#{post_link(post)}\n".color(:yellow))
|
51
|
+
out.write("#{post.title.strip.truncate(cols)}\n".color(:magenta))
|
52
|
+
out.write("#{post_date(post)}".color(:blue))
|
53
|
+
@count += 1
|
54
|
+
out.rewind
|
55
|
+
out.read
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def post_link(post)
|
61
|
+
"https://www.reddit.com#{post.permalink.split('/')[0..-2].join('/')}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def post_date(post)
|
65
|
+
Time.at(post.created_utc).ago
|
40
66
|
end
|
41
67
|
end
|
42
68
|
|
@@ -127,5 +153,20 @@ module USaidWat
|
|
127
153
|
comments.include?(comment)
|
128
154
|
end
|
129
155
|
end
|
156
|
+
|
157
|
+
class TallyFormatter < BaseFormatter
|
158
|
+
def format(partition_data)
|
159
|
+
out = StringIO.new
|
160
|
+
longest_subreddit = partition_data.longest
|
161
|
+
subreddits = partition_data.counts
|
162
|
+
subreddits.each do |subreddit_count|
|
163
|
+
subreddit, tally = subreddit_count
|
164
|
+
line = sprintf("%-*s %3d\n", longest_subreddit, subreddit, tally)
|
165
|
+
out.write(line)
|
166
|
+
end
|
167
|
+
out.rewind
|
168
|
+
out.read
|
169
|
+
end
|
170
|
+
end
|
130
171
|
end
|
131
172
|
end
|