usaidwat 1.4.5 → 1.5.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/Rakefile +7 -0
- data/bin/usaidwat +3 -1
- data/features/timeline.feature +46 -0
- data/lib/usaidwat.rb +1 -0
- data/lib/usaidwat/client.rb +15 -15
- data/lib/usaidwat/commands/timeline.rb +42 -0
- data/lib/usaidwat/formatter.rb +5 -185
- data/lib/usaidwat/formatter/base.rb +39 -0
- data/lib/usaidwat/formatter/comment.rb +92 -0
- data/lib/usaidwat/formatter/count.rb +20 -0
- data/lib/usaidwat/formatter/post.rb +43 -0
- data/lib/usaidwat/formatter/timeline.rb +32 -0
- data/lib/usaidwat/service.rb +30 -46
- data/lib/usaidwat/thing.rb +85 -0
- data/lib/usaidwat/version.rb +6 -1
- data/spec/usaidwat/client_spec.rb +8 -8
- data/spec/usaidwat/formatter_spec.rb +38 -10
- data/spec/usaidwat/service_spec.rb +4 -4
- data/spec/usaidwat/thing_spec.rb +139 -0
- data/spec/usaidwat/version_spec.rb +16 -0
- data/usaidwat.gemspec +1 -2
- metadata +21 -23
- data/lib/usaidwat/ext/snooby.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f691290d3941259d0b2cfdfaa0ac6f256df6bfba
|
4
|
+
data.tar.gz: 4e943e27e1d55d4c3fb08b10c1d8298b24303312
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 568babcb320e2cbddbd18ee4bfb1a3f8a6f33e3a014abe65992d1bfebad0fe5fb9d4199e2df85d8054da83ee36fc80c49087d7d0825ea72cc0a5fc3c2730ef35
|
7
|
+
data.tar.gz: b8532c9d3210713253e8f9a7fd9cf9f5b01bd37babbac633c16454b40bdef0290cdfec2840620acae013958d380e8a46efd512e107801dccc0e67d06a4429783
|
data/Rakefile
CHANGED
@@ -31,6 +31,13 @@ task :release => [:tag, :build] do
|
|
31
31
|
system "gem", "push", GEM
|
32
32
|
end
|
33
33
|
|
34
|
+
desc "Run the test suite"
|
35
|
+
task :test do
|
36
|
+
ok = system "bundle", "exec", "rspec"
|
37
|
+
fail if !ok
|
38
|
+
system "bundle", "exec", "cucumber", "-f", "progress"
|
39
|
+
end
|
40
|
+
|
34
41
|
desc "Clean built products"
|
35
42
|
task :clean do
|
36
43
|
rm Dir.glob("*.gem"), :verbose => true
|
data/bin/usaidwat
CHANGED
@@ -5,7 +5,9 @@ require 'usaidwat'
|
|
5
5
|
require 'usaidwat/ext/mercenary'
|
6
6
|
|
7
7
|
Mercenary.program(:usaidwat) do |p|
|
8
|
-
|
8
|
+
version = USaidWat::VERSION
|
9
|
+
version += " (#{USaidWat::commit_hash})" if USaidWat::VERSION.end_with?('.dev')
|
10
|
+
p.version version
|
9
11
|
p.description 'Answers the age-old question, "Where does a Redditor comment the most?"'
|
10
12
|
p.syntax 'usaidwat <command> [options] <args>'
|
11
13
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
Feature: Show timeline of comments
|
2
|
+
|
3
|
+
As a Redditor
|
4
|
+
I want to visualize when another Redditor comments most often
|
5
|
+
In order to see what time they like to comment
|
6
|
+
|
7
|
+
Scenario: Show timeline data for user
|
8
|
+
Given the Reddit service returns comments for the user "mipadi"
|
9
|
+
When I run `usaidwat timeline mipadi`
|
10
|
+
Then it should pass with:
|
11
|
+
"""
|
12
|
+
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
13
|
+
S * * *
|
14
|
+
M * * * * * * * * *
|
15
|
+
T * * * * * * * * *
|
16
|
+
W * * * * * *
|
17
|
+
T * * * * * * * *
|
18
|
+
F * * * * * * *
|
19
|
+
S * *
|
20
|
+
"""
|
21
|
+
|
22
|
+
Scenario: Show timeline data for a user with no comments
|
23
|
+
Given the Reddit service returns comments for the user "blank"
|
24
|
+
When I run `usaidwat timeline blank`
|
25
|
+
Then it should pass with:
|
26
|
+
"""
|
27
|
+
blank has no comments.
|
28
|
+
"""
|
29
|
+
|
30
|
+
Scenario: Show timeline data without specifying a user
|
31
|
+
Given the Reddit service returns comments for the user "mipadi"
|
32
|
+
When I run `usaidwat timeline testuser`
|
33
|
+
Then the exit status should not be 0
|
34
|
+
And stderr should contain exactly:
|
35
|
+
"""
|
36
|
+
No such user: testuser
|
37
|
+
"""
|
38
|
+
|
39
|
+
Scenario: Show timeline without specifying a user
|
40
|
+
Given the Reddit service returns comments for the user "mipadi"
|
41
|
+
When I run `usaidwat timeline`
|
42
|
+
Then the exit status should not be 0
|
43
|
+
And stderr should contain exactly:
|
44
|
+
"""
|
45
|
+
You must specify a username
|
46
|
+
"""
|
data/lib/usaidwat.rb
CHANGED
data/lib/usaidwat/client.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
require 'snooby'
|
2
1
|
require 'usaidwat/service'
|
3
|
-
require 'usaidwat/ext/snooby'
|
4
2
|
require 'usaidwat/ext/time'
|
5
3
|
|
6
4
|
module USaidWat
|
7
5
|
module Client
|
8
|
-
class ReachabilityError < RuntimeError
|
6
|
+
class ReachabilityError < RuntimeError
|
7
|
+
def initialize(msg = nil)
|
8
|
+
super(msg || 'Reddit unreachable')
|
9
|
+
end
|
10
|
+
end
|
9
11
|
|
10
12
|
class NoSuchUserError < StandardError; end
|
11
13
|
|
@@ -20,20 +22,20 @@ module USaidWat
|
|
20
22
|
user.comments(100)
|
21
23
|
rescue NoMethodError
|
22
24
|
raise NoSuchUserError, username
|
23
|
-
rescue
|
24
|
-
raise ReachabilityError
|
25
|
+
rescue RuntimeError
|
26
|
+
raise ReachabilityError
|
25
27
|
end
|
26
28
|
|
27
29
|
def link_karma
|
28
|
-
about
|
30
|
+
about.link_karma
|
29
31
|
end
|
30
32
|
|
31
33
|
def comment_karma
|
32
|
-
about
|
34
|
+
about.comment_karma
|
33
35
|
end
|
34
36
|
|
35
37
|
def created_at
|
36
|
-
|
38
|
+
about.created_utc
|
37
39
|
end
|
38
40
|
|
39
41
|
def age
|
@@ -48,8 +50,8 @@ module USaidWat
|
|
48
50
|
user.posts
|
49
51
|
rescue NoMethodError
|
50
52
|
raise NoSuchUserError, username
|
51
|
-
rescue
|
52
|
-
raise ReachabilityError
|
53
|
+
rescue RuntimeError
|
54
|
+
raise ReachabilityError
|
53
55
|
end
|
54
56
|
|
55
57
|
private
|
@@ -58,18 +60,16 @@ module USaidWat
|
|
58
60
|
@service.user(username)
|
59
61
|
end
|
60
62
|
|
61
|
-
def about
|
62
|
-
user.about
|
63
|
+
def about
|
64
|
+
user.about
|
63
65
|
rescue NoMethodError
|
64
66
|
raise NoSuchUserError, username
|
65
|
-
rescue TypeError, Net::HTTP::Persistent::Error
|
66
|
-
raise ReachabilityError, "Reddit unreachable"
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
70
|
class Redditor < BaseRedditor
|
71
71
|
def initialize(username)
|
72
|
-
@service =
|
72
|
+
@service = USaidWat::Service::RedditService.new
|
73
73
|
super
|
74
74
|
end
|
75
75
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module USaidWat
|
2
|
+
module Application
|
3
|
+
class Timeline < Command
|
4
|
+
def initialize(prog)
|
5
|
+
prog.command(:timeline) do |c|
|
6
|
+
c.action do |args, options|
|
7
|
+
process(options, args)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def process(options, args)
|
14
|
+
raise ArgumentError.new('You must specify a username') if args.empty?
|
15
|
+
username = args.shift
|
16
|
+
|
17
|
+
redditor = client.new(username)
|
18
|
+
comments = redditor.comments
|
19
|
+
|
20
|
+
quit "#{username} has no comments." if comments.empty?
|
21
|
+
|
22
|
+
buckets = comments_by_days_and_hours(comments)
|
23
|
+
times = Array.new(7) { Array.new(24, 0) }
|
24
|
+
buckets.each do |v|
|
25
|
+
d, h = v
|
26
|
+
times[d][h] += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
formatter = USaidWat::CLI::TimelineFormatter.new
|
30
|
+
puts formatter.format(times)
|
31
|
+
rescue USaidWat::Client::NoSuchUserError
|
32
|
+
quit "No such user: #{username}", :no_such_user
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def comments_by_days_and_hours(comments)
|
38
|
+
comments.map { |c| [c.created_utc.wday, c.created_utc.hour] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/usaidwat/formatter.rb
CHANGED
@@ -1,187 +1,7 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
require 'stringio'
|
7
|
-
require 'ttycaca'
|
8
|
-
require 'usaidwat/ext/string'
|
9
|
-
require 'usaidwat/ext/time'
|
1
|
+
require 'usaidwat/formatter/base'
|
2
|
+
require 'usaidwat/formatter/comment'
|
3
|
+
require 'usaidwat/formatter/count'
|
4
|
+
require 'usaidwat/formatter/post'
|
5
|
+
require 'usaidwat/formatter/timeline'
|
10
6
|
|
11
7
|
Rainbow.enabled = true unless ENV['USAIDWAT_ENV'] == 'cucumber'
|
12
|
-
|
13
|
-
module USaidWat
|
14
|
-
module CLI
|
15
|
-
class BaseFormatter
|
16
|
-
def initialize(options = {})
|
17
|
-
@options = options
|
18
|
-
@count = 0
|
19
|
-
end
|
20
|
-
|
21
|
-
def pattern
|
22
|
-
@options[:pattern]
|
23
|
-
end
|
24
|
-
|
25
|
-
def pattern?
|
26
|
-
!!@options[:pattern]
|
27
|
-
end
|
28
|
-
|
29
|
-
def raw?
|
30
|
-
!!@options[:raw]
|
31
|
-
end
|
32
|
-
|
33
|
-
def relative_dates?
|
34
|
-
@options[:date_format].nil? || @options[:date_format].to_sym != :absolute
|
35
|
-
end
|
36
|
-
|
37
|
-
protected
|
38
|
-
|
39
|
-
def 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.unescape_html.truncate(cols)}\n".color(:magenta))
|
52
|
-
out.write("#{post_date(post)}".color(:blue))
|
53
|
-
out.write("\n#{post.url}") unless post.url.end_with?(post.permalink)
|
54
|
-
@count += 1
|
55
|
-
out.rewind
|
56
|
-
out.read
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def post_link(post)
|
62
|
-
"https://www.reddit.com#{post.permalink.split('/')[0..-2].join('/')}"
|
63
|
-
end
|
64
|
-
|
65
|
-
def post_date(post)
|
66
|
-
Time.at(post.created_utc).ago
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
class CompactPostFormatter < BaseFormatter
|
71
|
-
def format(post)
|
72
|
-
cols = tty.width
|
73
|
-
out = StringIO.new
|
74
|
-
subreddit = post.subreddit
|
75
|
-
cols -= subreddit.length + 1
|
76
|
-
title = post.title.strip.unescape_html.truncate(cols)
|
77
|
-
out.write(subreddit.color(:green))
|
78
|
-
out.write(" #{title}\n")
|
79
|
-
out.rewind
|
80
|
-
out.read
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
class CommentFormatter < BaseFormatter
|
85
|
-
def format(comment)
|
86
|
-
cols = tty.width
|
87
|
-
out = StringIO.new
|
88
|
-
out.write("\n\n") unless @count == 0
|
89
|
-
out.write("#{comment.subreddit}\n".color(:green))
|
90
|
-
out.write("#{comment_link(comment)}\n".color(:yellow))
|
91
|
-
out.write("#{comment.link_title.strip.unescape_html.truncate(cols)}\n".color(:magenta))
|
92
|
-
out.write("#{comment_date(comment)}".color(:blue))
|
93
|
-
out.write(" \u2022 ".color(:cyan))
|
94
|
-
out.write(sprintf("%+d\n", comment_karma(comment)).color(:blue))
|
95
|
-
out.write("\n")
|
96
|
-
out.write("#{comment_body(comment)}\n")
|
97
|
-
@count += 1
|
98
|
-
out.rewind
|
99
|
-
out.read
|
100
|
-
end
|
101
|
-
|
102
|
-
private
|
103
|
-
def markdown
|
104
|
-
@markdown ||= Redcarpet::Markdown.new(Downterm::Render::Terminal, :autolink => true,
|
105
|
-
:strikethrough => true,
|
106
|
-
:superscript => true)
|
107
|
-
end
|
108
|
-
|
109
|
-
def comment_body(comment)
|
110
|
-
body = comment.body.strip.unescape_html
|
111
|
-
body = markdown.render(body) unless raw?
|
112
|
-
if pattern?
|
113
|
-
body.highlight(pattern)
|
114
|
-
else
|
115
|
-
body
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def comment_link(comment)
|
120
|
-
link = comment.link_id.split("_")[-1]
|
121
|
-
"http://www.reddit.com/r/#{comment.subreddit}/comments/#{link}/z/#{comment.id}"
|
122
|
-
end
|
123
|
-
|
124
|
-
def comment_date(comment)
|
125
|
-
d = DateTime.strptime(comment.created_utc.to_s, "%s").to_time.localtime
|
126
|
-
if relative_dates?
|
127
|
-
d.ago
|
128
|
-
else
|
129
|
-
d_part = d.strftime("%a, %-d %b %Y")
|
130
|
-
t_part = d.strftime("%l:%M %p").strip
|
131
|
-
"#{d_part}, #{t_part}"
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def comment_karma(comment)
|
136
|
-
comment.ups - comment.downs
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
class CompactCommentFormatter < BaseFormatter
|
141
|
-
def format(comment)
|
142
|
-
cols = tty.width
|
143
|
-
out = StringIO.new
|
144
|
-
subreddit = comment.subreddit
|
145
|
-
cols -= subreddit.length + 1
|
146
|
-
title = comment.link_title.strip.unescape_html.truncate(cols)
|
147
|
-
key = "#{subreddit} #{title}"
|
148
|
-
if !seen?(key)
|
149
|
-
out.write("#{subreddit}".color(:green))
|
150
|
-
out.write(" #{title}\n")
|
151
|
-
end
|
152
|
-
mark_seen(key)
|
153
|
-
out.rewind
|
154
|
-
out.read
|
155
|
-
end
|
156
|
-
|
157
|
-
private
|
158
|
-
|
159
|
-
def comments
|
160
|
-
@comments ||= Set.new
|
161
|
-
end
|
162
|
-
|
163
|
-
def mark_seen(comment)
|
164
|
-
comments << comment
|
165
|
-
end
|
166
|
-
|
167
|
-
def seen?(comment)
|
168
|
-
comments.include?(comment)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
class TallyFormatter < BaseFormatter
|
173
|
-
def format(partition_data)
|
174
|
-
out = StringIO.new
|
175
|
-
longest_subreddit = partition_data.longest
|
176
|
-
subreddits = partition_data.counts
|
177
|
-
subreddits.each do |subreddit_count|
|
178
|
-
subreddit, tally = subreddit_count
|
179
|
-
line = sprintf("%-*s %3d\n", longest_subreddit, subreddit, tally)
|
180
|
-
out.write(line)
|
181
|
-
end
|
182
|
-
out.rewind
|
183
|
-
out.read
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'downterm'
|
2
|
+
require 'rainbow/ext/string'
|
3
|
+
require 'redcarpet'
|
4
|
+
require 'ttycaca'
|
5
|
+
|
6
|
+
module USaidWat
|
7
|
+
module CLI
|
8
|
+
module TTYFormatter
|
9
|
+
def tty
|
10
|
+
@tty ||= Ttycaca::Terminal.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class BaseFormatter
|
15
|
+
include TTYFormatter
|
16
|
+
|
17
|
+
def initialize(options = {})
|
18
|
+
@options = options
|
19
|
+
@count = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def pattern
|
23
|
+
@options[:pattern]
|
24
|
+
end
|
25
|
+
|
26
|
+
def pattern?
|
27
|
+
!!@options[:pattern]
|
28
|
+
end
|
29
|
+
|
30
|
+
def raw?
|
31
|
+
!!@options[:raw]
|
32
|
+
end
|
33
|
+
|
34
|
+
def relative_dates?
|
35
|
+
@options[:date_format].nil? || @options[:date_format].to_sym != :absolute
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|