rstackoverflow 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 12387e336eeaad5a7d0631b57b3f4cc038c6dc3d
4
+ data.tar.gz: 65df29d177341a81d87f0278812cde6f7425aa64
5
+ SHA512:
6
+ metadata.gz: bb2491b0069dfb77882b6bea846ae3c7f7d3573a35468ee22d99a683c3de3a62e1a3cf240485e3c6d675ed517ebb4228e9455f9f65fd41c907a288734e9d4ad7
7
+ data.tar.gz: 52b7cc6c07361959b2e1c726a678511e9b179956a371a2e3cfff288f798791de9bbb6a4dbca1d3ac4b8279f9b501f56fc7abcf5d5cbfb9b89544a96899f1618f
@@ -0,0 +1,25 @@
1
+ module StackOverflow
2
+ require 'nokogiri'
3
+ require 'net/http'
4
+
5
+ module ExtensionString
6
+ refine String do
7
+ def escape!
8
+ self.tr!(' ', '+')
9
+ URI.escape(self)
10
+ end
11
+ end
12
+ end
13
+
14
+ module Engine
15
+ Native = :native
16
+ Github = :github
17
+ Engines = [Native, Github]
18
+ end
19
+
20
+ class InvalidSearchEngine < StandardError
21
+ end
22
+ end
23
+
24
+ require_relative 'rstackoverflow/utils'
25
+ require_relative 'rstackoverflow/question'
@@ -0,0 +1,76 @@
1
+ module StackOverflow
2
+ module Questions
3
+ using ExtensionString
4
+ extend Engine
5
+ class << self
6
+ def add_class(klass, *args)
7
+ eval <<-init
8
+ class #{klass.capitalize}
9
+ attr_reader :#{args.join(', :')}
10
+ def initialize(#{args.join(', ')})
11
+ #{args.map {|e| "@#{e} = #{e};"}.join}
12
+ end
13
+ end
14
+ init
15
+ end
16
+
17
+ def [](q_index)
18
+ return nil unless q_index.is_a? Integer
19
+ html = Utils.http_get("/questions/#{q_index}")
20
+ href = html.css('a').first.attr('href')
21
+ arr = href.split('/')
22
+ return nil unless q_index == arr[2].to_i
23
+ html = Utils.http_get(href)
24
+ q_title = html.css('div[id=question-header]').first.css('a').text
25
+ question = html.css('div[class=question]').first
26
+ q_detail = question.css('div[class=post-text]').text.strip
27
+ q_vote = question.css('span[class~=vote-count-post]').text.to_i
28
+ list = question.css('div[class=post-taglist]')
29
+ q_labels = list.first.children.css('a').map {|e| e.text}
30
+ q_comments = question.css('td[class=comment-text]').map {|e| e.text.strip}
31
+
32
+ answers = html.css('div[class~=answer]').map do |e|
33
+ a_index = e.attr('id')[7..-1].to_i
34
+ a_detail = e.css('div[class=post-text]').text.strip
35
+ a_accepted = e.attr('class').index('accepted-answer') ? true : false
36
+ a_vote = e.css('span[class~=vote-count-post]').text.to_i
37
+ a_comments = e.css('td[class=comment-text]').map {|t| t.text.strip}
38
+ Answer.new(a_index, a_detail, a_accepted, a_vote, a_comments)
39
+ end
40
+ Question.new(q_index, "#{Utils::DOMAIN}#{href}",
41
+ q_title, q_detail, q_vote, q_labels, q_comments,
42
+ answers)
43
+ end
44
+
45
+ alias :at :[]
46
+
47
+ def search(cond, engine = Native)
48
+ return nil unless cond.is_a? String
49
+ case engine
50
+ when Native
51
+ html = Utils.http_get("/search?q=#{cond.escape!}")
52
+ results = html.css('div[class~=search-result]')
53
+ results.map do |r|
54
+ r_index = r.attr('id')[17..-1].to_i
55
+ r_title = r.css('div[class=result-link]').text.strip
56
+ r_abstract = r.css('div[class=excerpt]').text.strip
57
+ r_vote = r.css('span[class~=vote-count-post]').text.to_i
58
+ r_answer_count = r.css('div[class~=answered-accepted]').text.to_i
59
+ Result.new(r_index, r_title, r_abstract, r_vote, r_answer_count)
60
+ end
61
+ when Github
62
+ #TODO
63
+ fail InvalidSearchEngine
64
+ else
65
+ fail InvalidSearchEngine
66
+ end
67
+ end
68
+ end
69
+
70
+ add_class :Question, :id, :url, :title, :detail, :vote, :labels, :comments, :answers
71
+ add_class :Answer, :id, :detail, :accepted, :vote, :comments
72
+ #TODO
73
+ #add_class :Comment
74
+ add_class :Result, :id, :title, :abstract, :vote, :answer_count
75
+ end
76
+ end
@@ -0,0 +1,10 @@
1
+ module StackOverflow::Utils
2
+ require 'uri'
3
+ DOMAIN = 'http://stackoverflow.com'
4
+
5
+ def self.http_get(suffix)
6
+ uri = URI("#{DOMAIN}#{suffix}")
7
+ text = Net::HTTP.get(uri)
8
+ Nokogiri::HTML(text)
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rstackoverflow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Breeze
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.5.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.5'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.5.2
33
+ description: Ruby toolkit to Stackoverflow API by html parsing.
34
+ email: sjtubreeze@163.com
35
+ executables: []
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - lib/rstackoverflow.rb
40
+ - lib/rstackoverflow/question.rb
41
+ - lib/rstackoverflow/utils.rb
42
+ homepage: https://github.com/jicong/rStackOverflow
43
+ licenses:
44
+ - MIT
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.9.2
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.4.8
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: StackOverflow API
66
+ test_files: []