hackernews 0.1.0 → 0.2.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.
data/README.rdoc CHANGED
@@ -10,16 +10,16 @@ This is a fairly simple Ruby class for accessing certain parts of Hacker News (h
10
10
 
11
11
  * Handles Hacker News session cookie and login.
12
12
  * Can retreive user page, along with karma and average karma values.
13
+ * Can vote on posts and comments.
14
+ * Can comment on posts and other comments.
13
15
 
14
16
  == To Do
15
17
 
16
- * Add ability to vote.
17
18
  * Add ability to post news.
18
- * Add ability to post comments.
19
19
 
20
20
  == Source Code
21
21
 
22
- http://github.com/seven1m/hackernews
22
+ http://github.com/seven1m/hackernews
23
23
 
24
24
  == Installation
25
25
 
@@ -32,6 +32,8 @@ This is a fairly simple Ruby class for accessing certain parts of Hacker News (h
32
32
  hn = HackerNews.new(USERNAME, PASSWORD)
33
33
  hn.karma # => '62'
34
34
  hn.average_karma # => '1.87'
35
+ hn.vote(123)
36
+ hn.comment(123, 'my comment')
35
37
 
36
38
  == Feedback
37
39
 
data/lib/hackernews.rb CHANGED
@@ -4,34 +4,33 @@ require 'open-uri'
4
4
 
5
5
  class HackerNews
6
6
 
7
- VERSION = '0.1.0'
7
+ VERSION = '0.2.0'
8
8
 
9
9
  # Returns the version string for the library.
10
10
  def self.version
11
11
  VERSION
12
12
  end
13
13
 
14
- BASE_URL = 'http://news.ycombinator.com'
15
- USER_URL = 'http://news.ycombinator.com/user?id=%s'
14
+ BASE_URL = "http://news.ycombinator.com"
15
+ ITEM_URL = "#{BASE_URL}/item?id=%s"
16
+ USER_URL = "#{BASE_URL}/user?id=%s"
17
+ LOGIN_SUBMIT_URL = "#{BASE_URL}/y"
18
+ COMMENT_SUBMIT_URL = "#{BASE_URL}/r"
19
+
20
+ class LoginError < RuntimeError; end
16
21
 
17
22
  # Creates a new HackerNews object.
18
23
  # Specify your username and password.
19
24
  def initialize(username, password)
20
- login_url = open(BASE_URL).read.match(/href="([^"]+)">login<\/a>/)[1]
21
- form_html = open(BASE_URL + login_url).read
22
- submit_url = URI.parse(BASE_URL)
23
- response = Net::HTTP.new(submit_url.host, submit_url.port).start do |http|
24
- req = Net::HTTP::Post.new('/y')
25
- req.set_form_data(
26
- 'fnid' => form_html.match(/<input type=hidden name="fnid" value="([^"]+)"/)[1],
27
- 'u' => username,
28
- 'p' => password
29
- )
30
- http.request(req)
31
- end
32
- @cookie = response.header['set-cookie']
25
+ login_url = get(BASE_URL).match(/href="([^"]+)">login<\/a>/)[1]
26
+ form_html = get(BASE_URL + login_url)
27
+ fnid = form_html.match(/<input type=hidden name="fnid" value="([^"]+)"/)[1]
28
+ response = post(LOGIN_SUBMIT_URL, 'fnid' => fnid, 'u' => username, 'p' => password)
33
29
  @username = username
34
30
  @password = password
31
+ unless @cookie = response.header['set-cookie']
32
+ raise LoginError, "Login credentials did not work."
33
+ end
35
34
  end
36
35
 
37
36
  # Retrieves the karma for the logged in user, or for the specified username (if given).
@@ -49,13 +48,53 @@ class HackerNews
49
48
  username ||= @username
50
49
  @user_pages ||= {}
51
50
  @user_pages[username] ||= begin
52
- url = URI.parse(USER_URL % username)
51
+ get(USER_URL % username)
52
+ end
53
+ end
54
+
55
+ # Up-vote on a post or on a comment by passing in the id number.
56
+ def vote(id)
57
+ url = get(ITEM_URL % id).match(/<a id=up_\d+ onclick="return vote\(this\)" href="(vote\?[^"]+)">/)[1]
58
+ get(BASE_URL + '/' + url)
59
+ end
60
+
61
+ # Post a comment on a posted item or on another comment.
62
+ def comment(id, text)
63
+ fnid = get(ITEM_URL % id).match(/<input type=hidden name="fnid" value="([^"]+)"/)[1]
64
+ post(COMMENT_SUBMIT_URL, 'fnid' => fnid, 'text' => text)
65
+ end
66
+
67
+ private
68
+
69
+ def url_path_and_query(url)
70
+ if url.path and url.query
71
+ "#{url.path}?#{url.query}"
72
+ elsif url.path.to_s.any?
73
+ url.path
74
+ else
75
+ '/'
76
+ end
77
+ end
78
+
79
+ def get(url)
80
+ url = URI.parse(url)
53
81
  response = Net::HTTP.start(url.host, url.port) do |http|
54
- header = {'Cookie' => @cookie}
55
- http.get(url.path + '?' + url.query, header)
82
+ http.get(url_path_and_query(url), build_header)
56
83
  end
57
84
  response.body
58
85
  end
59
- end
86
+
87
+ def post(url, data)
88
+ url = URI.parse(url)
89
+ Net::HTTP.new(url.host, url.port).start do |http|
90
+ req = Net::HTTP::Post.new(url.path, build_header)
91
+ req.set_form_data(data)
92
+ http.request(req)
93
+ end
94
+ end
95
+
96
+ def build_header
97
+ @cookie && {'Cookie' => @cookie}
98
+ end
60
99
 
61
100
  end
@@ -12,10 +12,16 @@ class HackerNewsTest < Test::Unit::TestCase
12
12
  end
13
13
  end
14
14
 
15
- def test_login
15
+ def test_session_cookie
16
16
  assert @hn.instance_eval('@cookie') =~ /user=[a-z0-9]+;/i
17
17
  end
18
18
 
19
+ def test_login_failure
20
+ assert_raise HackerNews::LoginError do
21
+ HackerNews.new('foobar00000', 'baz')
22
+ end
23
+ end
24
+
19
25
  def test_karma
20
26
  assert @hn.karma.to_s != ''
21
27
  assert @hn.karma('pg').to_i > 59000
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hackernews
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Tim Morgan
@@ -38,18 +43,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
38
43
  requirements:
39
44
  - - ">="
40
45
  - !ruby/object:Gem::Version
46
+ segments:
47
+ - 0
41
48
  version: "0"
42
- version:
43
49
  required_rubygems_version: !ruby/object:Gem::Requirement
44
50
  requirements:
45
51
  - - ">="
46
52
  - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
47
55
  version: "0"
48
- version:
49
56
  requirements: []
50
57
 
51
58
  rubyforge_project:
52
- rubygems_version: 1.3.5
59
+ rubygems_version: 1.3.6
53
60
  signing_key:
54
61
  specification_version: 3
55
62
  summary: Ruby gem to login and interact with the Hacker News website.