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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81fff9eeb3cb27fb171c0e765857245b69a7cfa6
4
- data.tar.gz: 0e8665fd1c0b68b607fd0354624ea089e0e9015d
3
+ metadata.gz: f691290d3941259d0b2cfdfaa0ac6f256df6bfba
4
+ data.tar.gz: 4e943e27e1d55d4c3fb08b10c1d8298b24303312
5
5
  SHA512:
6
- metadata.gz: e00710b0a337b430c7fa576dc4ca58e847e402b060d316df87561fb555133c41b0d7a7247208d1ff6d3773330c5aee481aeb69f3ab833e3ecaf97151f3aefedb
7
- data.tar.gz: 80415d6345702d01eb5d4bc5df197e09ed4b308bf1ed657e25cf4fc4300eba3653ecb5c3dfa5e36667c9f6888504af28e1a0341a4e893810a1c345f384c2df87
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
- p.version USaidWat::VERSION
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
@@ -9,6 +9,7 @@ require "usaidwat/algo"
9
9
  require "usaidwat/client"
10
10
  require "usaidwat/command"
11
11
  require "usaidwat/formatter"
12
+ require "usaidwat/thing"
12
13
  require "usaidwat/version"
13
14
 
14
15
  require_all "usaidwat/commands"
@@ -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; end
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 TypeError, Net::HTTP::Persistent::Error
24
- raise ReachabilityError, "Reddit unreachable"
25
+ rescue RuntimeError
26
+ raise ReachabilityError
25
27
  end
26
28
 
27
29
  def link_karma
28
- about('link_karma')
30
+ about.link_karma
29
31
  end
30
32
 
31
33
  def comment_karma
32
- about('comment_karma')
34
+ about.comment_karma
33
35
  end
34
36
 
35
37
  def created_at
36
- Time.at(about('created_utc'))
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 TypeError, Net::HTTP::Persistent::Error
52
- raise ReachabilityError, "Reddit unreachable"
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(key)
62
- user.about[key]
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 = Snooby::Client.new("usaidwat v#{USaidWat::VERSION}")
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
@@ -1,187 +1,7 @@
1
- require 'date'
2
- require 'downterm'
3
- require 'rainbow/ext/string'
4
- require 'redcarpet'
5
- require 'set'
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