bterlson-reddit 0.2.1 → 0.3.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.
@@ -15,6 +15,7 @@ module Reddit
15
15
  @domain = attributes['domain']
16
16
  @author = User.new(attributes['author']) unless attributes['author'].nil?
17
17
  @id = attributes['id']
18
+ # Reddit's created_at timestamps are currently wonky, so this will return the wrong time.
18
19
  @created_at = Time.at(attributes['created']) unless attributes['created'].nil?
19
20
  @saved = attributes['saved']
20
21
  @clicked = attributes['clicked']
@@ -37,8 +38,9 @@ module Reddit
37
38
  end
38
39
 
39
40
  # returns a CommentList of this article's comments.
40
- def comments
41
- return CommentList.new(@id)
41
+ def comments(options = {})
42
+ @comments_list ||= CommentList.new(@id)
43
+ return @comments_list.top_level(options)
42
44
  end
43
45
  end
44
46
  end
@@ -1,5 +1,4 @@
1
1
  module Reddit
2
-
3
2
  # A reddit comment.
4
3
  class Comment
5
4
  attr_reader :body, :name, :ups, :downs, :url, :domain, :author, :id, :created_at, :replies
@@ -21,7 +20,6 @@ module Reddit
21
20
  @replies << Comment.new(reply['data'])
22
21
  end
23
22
  end
24
-
25
23
  end
26
24
 
27
25
  # Returns the score for this comment.
@@ -29,5 +27,9 @@ module Reddit
29
27
  return @ups - @downs
30
28
  end
31
29
 
30
+ # returns a number representing how controversial this comment is
31
+ def controversy_score
32
+ (@ups + @downs).to_f / [score.abs, 1].max
33
+ end
32
34
  end
33
35
  end
@@ -10,15 +10,10 @@ module Reddit
10
10
  end
11
11
 
12
12
  # returns the top level comments for the thread.
13
- def top_level
14
- resources = get_resources(@url)
15
-
16
- comments = []
17
- resources.each do |comment|
18
- comments << Comment.new(comment['data'])
13
+ def top_level(options = {})
14
+ get_resources(@url, :querystring => options[:querystring], :count => options[:count]) do |resource_json|
15
+ comment = Comment.new(resource_json['data'])
19
16
  end
20
-
21
- return comments
22
17
  end
23
18
 
24
19
  private
@@ -26,6 +21,6 @@ module Reddit
26
21
  # forward any method calls to the top level comments array.
27
22
  def method_missing(meth, *args, &block)
28
23
  top_level.send(meth, *args, &block)
29
- end
24
+ end
30
25
  end
31
26
  end
@@ -9,33 +9,37 @@ module Reddit
9
9
  @url = @name.nil? ? BASE_URL : SUBREDDIT_URL.gsub('[subreddit]', @name)
10
10
  end
11
11
 
12
- def hot
13
- articles 'hot'
12
+ def hot(options = {})
13
+ articles 'hot', options
14
14
  end
15
15
 
16
- def top
17
- articles 'top'
16
+ def top(options = {})
17
+ articles 'top', options
18
18
  end
19
19
 
20
- def new
21
- articles 'new'
20
+ def new(options = {})
21
+ options[:querystring] = 'sort=new'
22
+ articles 'new', options
22
23
  end
23
24
 
24
- def controversial
25
- articles 'controversial'
25
+ def rising(options = {})
26
+ options[:querystring] = 'sort=rising'
27
+ articles 'new', options
28
+ end
29
+
30
+ def controversial(options = {})
31
+ articles 'controversial', options
26
32
  end
27
33
 
28
34
  # Returns the articles found in this reddit.
29
- def articles(page = 'hot')
30
- resources = get_resources("#{@url}#{page}/")
31
-
32
- articles = []
33
-
34
- resources.each do |article|
35
- articles << Article.new(article['data'])
35
+ # Options are:
36
+ # Count: Return at least this many articles.
37
+ # Querystring: Querystring to append to resource request
38
+
39
+ def articles(page = 'hot', options = {})
40
+ get_resources("#{@url}#{page}", options) do |resource_json|
41
+ Article.new(resource_json['data'])
36
42
  end
37
-
38
- return articles
39
43
  end
40
44
  end
41
45
  end
@@ -7,22 +7,36 @@ module Reddit
7
7
  private
8
8
 
9
9
  # Grabs the resources at the URL and returns the parsed json.
10
- def get_resources(url)
11
- puts "Getting URL: #{url}"
10
+ def get_resources(url, options = {}, &block)
11
+ querystring = options.delete(:querystring) || ''
12
+ count = options.delete(:count) || 25
12
13
  url = URI.parse(url)
14
+ items = []
15
+ after = ''
13
16
 
14
- res = Net::HTTP.start(url.host, url.port) {|http|
15
- http.get(url.path + ".json")
16
- }
17
+ while items.size < count
18
+ res = Net::HTTP.start(url.host, url.port) {|http|
19
+ http.get("#{url.path}.json?#{querystring}&after=#{after}&limit=#{count - items.size}")
20
+ }
17
21
 
18
- raise SubredditNotFound if res.is_a?(Net::HTTPRedirection)
19
- resources = JSON.parse(res.body, :max_nesting => 0)
22
+ raise SubredditNotFound if res.is_a?(Net::HTTPRedirection)
20
23
 
21
- # comments pages are contained in an array where the first element is the article
22
- # and the second is the actual comments. This is hackish.
23
- resources = resources.last if resources.is_a?(Array)
24
+ resources = JSON.parse(res.body, :max_nesting => 0)
24
25
 
25
- return resources['data']['children']
26
+ # comments pages are contained in an array where the first element is the article
27
+ # and the second is the actual comments. This is hackish.
28
+ resources = resources.last if resources.is_a?(Array)
29
+ resources = resources['data']['children']
30
+ break if resources.size == 0
31
+ resources.each do |resource|
32
+ items << yield(resource)
33
+ end
34
+
35
+ after = items.last.name
36
+ end
37
+
38
+ items
26
39
  end
27
40
  end
41
+
28
42
  end
@@ -2,7 +2,7 @@ module Reddit
2
2
  BASE_URL = "http://www.reddit.com/"
3
3
  PROFILE_URL = BASE_URL + "user/[username]/"
4
4
  SUBREDDIT_URL = BASE_URL + "r/[subreddit]/"
5
- COMMENTS_URL = BASE_URL + "info/[id]/comments/"
5
+ COMMENTS_URL = BASE_URL + "comments/[id]/"
6
6
 
7
7
  # raised when attempting to interact with a subreddit that doesn't exist.
8
8
  class SubredditNotFound < StandardError; end
@@ -11,15 +11,12 @@ module Reddit
11
11
  end
12
12
 
13
13
  # Get the user's comments.
14
- def comments
15
- resources = get_resources(@url)
16
-
17
- comments = []
18
- resources.each do |comment|
19
- comments << Comment.new(comment['data'])
14
+ # Options can include a limit, which sets the number of comments to return.
15
+
16
+ def comments(options = {})
17
+ get_resources("#{@url}comments/", :querystring => options[:querystring], :count => options[:count]) do |resource_json|
18
+ Comment.new(resource_json['data'])
20
19
  end
21
-
22
- return comments
23
20
  end
24
21
  end
25
22
  end
@@ -1,8 +1,8 @@
1
1
  module Reddit #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 2
5
- TINY = 1
4
+ MINOR = 3
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -6,7 +6,7 @@ describe Reddit::Article do
6
6
  end
7
7
 
8
8
  it "should be able to get the article's comments comments" do
9
- Reddit::CommentList.should_receive(:new).with("id").and_return("reddit!")
9
+ Reddit::CommentList.should_receive(:new).with("id").and_return(mock(Reddit::CommentList, :top_level => "reddit!"))
10
10
  @article.comments.should == "reddit!"
11
11
  end
12
12
  end
@@ -10,13 +10,9 @@ describe Reddit::CommentList do
10
10
  end
11
11
 
12
12
  it "should fetch the top level comments" do
13
- @comments_list.should_receive(:get_resources).and_return([
14
- {:type => 't1', :data => {:attribute => 'value'}},
15
- {:type => 't1', :data => {:attribute => 'value2'}}
16
- ])
17
-
18
13
  mock_comment = mock(Reddit::Comment)
19
- Reddit::Comment.should_receive(:new).twice.and_return(mock_comment)
14
+
15
+ @comments_list.should_receive(:get_resources).and_return([mock_comment, mock_comment])
20
16
 
21
17
  @comments_list.top_level.should == [mock_comment, mock_comment]
22
18
  end
@@ -17,18 +17,15 @@ describe Reddit::Reddit do
17
17
  end
18
18
 
19
19
  it "should find the articles" do
20
- @reddit.should_receive(:get_resources).and_return(test_articles)
21
-
22
20
  mock_article = mock(Reddit::Article)
23
- Reddit::Article.should_receive(:new).twice.and_return(mock_article)
24
-
21
+ @reddit.should_receive(:get_resources).and_return([mock_article, mock_article])
25
22
  @reddit.articles.should == [mock_article, mock_article]
26
23
  end
27
24
 
28
25
  it "should find top articles" do
29
26
  mock_article = mock(Reddit::Article)
30
27
 
31
- @reddit.should_receive(:articles).with('top').and_return([mock_article, mock_article])
28
+ @reddit.should_receive(:articles).with('top', {}).and_return([mock_article, mock_article])
32
29
 
33
30
  @reddit.top.should == [mock_article, mock_article]
34
31
  end
@@ -36,15 +33,23 @@ describe Reddit::Reddit do
36
33
  it "should find new articles" do
37
34
  mock_article = mock(Reddit::Article)
38
35
 
39
- @reddit.should_receive(:articles).with('new').and_return([mock_article, mock_article])
36
+ @reddit.should_receive(:articles).with('new', {:querystring => 'sort=new'}).and_return([mock_article, mock_article])
40
37
 
41
38
  @reddit.new.should == [mock_article, mock_article]
42
39
  end
43
40
 
41
+ it "should find rising articles" do
42
+ mock_article = mock(Reddit::Article)
43
+
44
+ @reddit.should_receive(:articles).with('new', {:querystring => 'sort=rising'}).and_return([mock_article, mock_article])
45
+
46
+ @reddit.rising.should == [mock_article, mock_article]
47
+ end
48
+
44
49
  it "should find controversial articles" do
45
50
  mock_article = mock(Reddit::Article)
46
51
 
47
- @reddit.should_receive(:articles).with('controversial').and_return([mock_article, mock_article])
52
+ @reddit.should_receive(:articles).with('controversial', {}).and_return([mock_article, mock_article])
48
53
 
49
54
  @reddit.controversial.should == [mock_article, mock_article]
50
55
  end
@@ -52,7 +57,7 @@ describe Reddit::Reddit do
52
57
  it "should find hot articles" do
53
58
  mock_article = mock(Reddit::Article)
54
59
 
55
- @reddit.should_receive(:articles).with('hot').and_return([mock_article, mock_article])
60
+ @reddit.should_receive(:articles).with('hot', {}).and_return([mock_article, mock_article])
56
61
 
57
62
  @reddit.hot.should == [mock_article, mock_article]
58
63
  end
@@ -12,12 +12,23 @@ describe Reddit::ResourceList, ".get_resources" do
12
12
 
13
13
  it "should get the resources from Reddit" do
14
14
  Net::HTTP.should_receive(:start).and_yield(@http_mock).and_return(@response_mock)
15
- @resource_list.send(:get_resources, "http://reddit.com")
15
+ @resource_list.send(:get_resources, "http://reddit.com", :count => 1) do
16
+ mock('object', :name => "object_name")
17
+ end
18
+ end
19
+
20
+ it "should get the specified number of resources" do
21
+ Net::HTTP.should_receive(:start).exactly(:twice).and_yield(@http_mock).and_return(@response_mock)
22
+ @resource_list.send(:get_resources, "http://reddit.com", :count => 2) do
23
+ mock('object', :name => "object_name")
24
+ end
16
25
  end
17
26
 
18
27
  it "should parse the JSON" do
19
28
  JSON.should_receive(:parse).and_return({'kind' => 'Listing', 'data' => {'children' => [{'data' => {'attribute' => 'value'}}]}})
20
- @resource_list.send(:get_resources, "http://reddit.com")
29
+ @resource_list.send(:get_resources, "http://reddit.com", :count => 1) do
30
+ mock('object', :name => "object_name")
31
+ end
21
32
  end
22
33
 
23
34
  it "should raise an error when the subreddit is not found" do
@@ -10,14 +10,8 @@ describe Reddit::User do
10
10
  end
11
11
 
12
12
  it "should fetch the user's comments" do
13
- @user.should_receive(:get_resources).and_return([
14
- {:type => 't1', :data => {:attribute => 'value'}},
15
- {:type => 't1', :data => {:attribute => 'value2'}}
16
- ])
17
-
18
13
  mock_comment = mock(Reddit::Comment)
19
- Reddit::Comment.should_receive(:new).twice.and_return(mock_comment)
20
-
14
+ @user.should_receive(:get_resources).and_return([mock_comment, mock_comment])
21
15
  @user.comments.should == [mock_comment, mock_comment]
22
16
  end
23
17
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bterlson-reddit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Terlson
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-06-13 00:00:00 -07:00
12
+ date: 2008-08-13 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -77,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
77
  requirements: []
78
78
 
79
79
  rubyforge_project:
80
- rubygems_version: 1.0.1
80
+ rubygems_version: 1.2.0
81
81
  signing_key:
82
82
  specification_version: 2
83
83
  summary: Interface with the Reddit.com API.
data/README.txt DELETED
@@ -1,66 +0,0 @@
1
- = reddit
2
-
3
- * http://github.com/bterlson/reddit/
4
-
5
- == DESCRIPTION:
6
-
7
- An interface to the reddit API.
8
-
9
- == FEATURES/PROBLEMS:
10
-
11
- * Retreive articles and their comments
12
- * Retreive user pages
13
-
14
- == SYNOPSIS:
15
-
16
- require 'rubygems'
17
- require 'reddit'
18
-
19
- reddit = Reddit::Session.new
20
-
21
- reddit.main.articles.each do |article|
22
- p article.url
23
-
24
- article.comments.each do |comment|
25
- p comment.body
26
- end
27
- end
28
-
29
- p reddit.subreddit("programming").articles[1].author
30
-
31
- reddit.user("radhruin").comments.each do |comment|
32
- p comment.body
33
- end
34
-
35
- == REQUIREMENTS:
36
-
37
- * JSON >= 1.1.2
38
-
39
- == INSTALL:
40
-
41
- * sudo gem install bterlson-reddit --source=http://gems.github.com
42
-
43
- == LICENSE:
44
-
45
- (The MIT License)
46
-
47
- Copyright (c) 2008 FIX
48
-
49
- Permission is hereby granted, free of charge, to any person obtaining
50
- a copy of this software and associated documentation files (the
51
- 'Software'), to deal in the Software without restriction, including
52
- without limitation the rights to use, copy, modify, merge, publish,
53
- distribute, sublicense, and/or sell copies of the Software, and to
54
- permit persons to whom the Software is furnished to do so, subject to
55
- the following conditions:
56
-
57
- The above copyright notice and this permission notice shall be
58
- included in all copies or substantial portions of the Software.
59
-
60
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
61
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
62
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
63
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
64
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
65
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
66
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.