hackernews 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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.