yanapi 0.3.1 → 0.3.2
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/CHANGELOG +25 -14
- data/README +15 -2
- data/lib/yanapi/api.rb +1 -1
- data/lib/yanapi/error.rb +7 -3
- data/lib/yanapi/query.rb +51 -42
- data/lib/yanapi/version.rb +1 -1
- data/test/test_query.rb +14 -4
- metadata +23 -7
data/CHANGELOG
CHANGED
@@ -1,22 +1,33 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
== COMPLETED
|
2
|
+
=== 0.3.2
|
3
|
+
The main class +Query+ reimplemented and covered by tests.
|
4
|
+
|
5
|
+
The overall documentation enhanced.
|
6
|
+
=== 0.3.1
|
3
7
|
All four search methods implemented.
|
4
|
-
|
8
|
+
|
9
|
+
WARNING! This is the first release with a new two dimensional interface! Consult
|
10
|
+
README for further details.
|
11
|
+
=== 0.1.1
|
5
12
|
Small fixes in the documentation.
|
6
|
-
|
7
|
-
The search methods <tt>questionSearch</tt> and <tt>getByCategory</tt
|
8
|
-
|
13
|
+
=== 0.1.0
|
14
|
+
The search methods <tt>questionSearch</tt> and <tt>getByCategory</tt>
|
15
|
+
implemented without tests.
|
16
|
+
=== 0.0.1
|
9
17
|
Initial release of the lib.
|
10
18
|
|
11
19
|
|
12
|
-
|
13
|
-
|
20
|
+
== PLANNED
|
21
|
+
=== 0.4.0
|
14
22
|
Enhance the documentation.
|
15
23
|
|
16
24
|
Implement multivalue parameters.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
25
|
+
=== 0.5.0
|
26
|
+
Implement semantic checks for all query types.
|
27
|
+
|
28
|
+
Enhance the documentation.
|
29
|
+
=== 0.6.0
|
30
|
+
=== 0.7.0
|
31
|
+
=== 0.8.0
|
32
|
+
=== 0.9.0
|
33
|
+
=== 1.0.0
|
data/README
CHANGED
@@ -21,13 +21,25 @@ It is possible to restrict key word based search through a category.
|
|
21
21
|
|
22
22
|
Question Search and User Search cannot be extended by key words or category IDs.
|
23
23
|
|
24
|
-
|
24
|
+
YANAPI tries to as flexible as possible. It restricts unallowed parameter
|
25
25
|
combinations and forces using mandatory ones. But it doesn't care about defaults.
|
26
26
|
For example, as for this writing the default output value is an xml based format.
|
27
27
|
If it changes in the future, the user will be responsible to choose the format.
|
28
28
|
No defaults are hardcoded in YANAPI.
|
29
|
+
|
29
30
|
YANAPI provides the minimal acceptable query.
|
30
31
|
|
32
|
+
== INSTALLATION
|
33
|
+
To install YANAPI ussue the following command:
|
34
|
+
$ gem install yanapi
|
35
|
+
|
36
|
+
You might want to install versions prior to +0.3.1+:
|
37
|
+
$ gem install yanapi -v 0.1.1
|
38
|
+
|
39
|
+
If you want to do a system wide installation, do this as root
|
40
|
+
(possibly using +sudo+).
|
41
|
+
|
42
|
+
Alternatively use your Gemfile for dependency management.
|
31
43
|
== SYNOPSIS
|
32
44
|
|
33
45
|
YANAPI requires a parameter hash with two dimensions:
|
@@ -57,7 +69,7 @@ A small example shall demostrate the usage:
|
|
57
69
|
api.get # => default xml structure
|
58
70
|
|
59
71
|
|
60
|
-
For details on particular keys and defaults see {the official description}[http://developer.yahoo.com/answers/] and the RDoc documentation in this
|
72
|
+
For details on particular keys and defaults see {the official description}[http://developer.yahoo.com/answers/] and the RDoc documentation in this library.
|
61
73
|
|
62
74
|
== EXCEPTION HIERARCHY
|
63
75
|
While using YANAPI you can face three kinds of errors:
|
@@ -90,5 +102,6 @@ Please contact me with your suggestions, bug reports and feature requests.
|
|
90
102
|
== LICENSE
|
91
103
|
|
92
104
|
YANAPI is a copyrighted software by Andrei Beliankou, 2011.
|
105
|
+
|
93
106
|
You may use, redistribute and change it under the terms
|
94
107
|
provided in the LICENSE file.
|
data/lib/yanapi/api.rb
CHANGED
data/lib/yanapi/error.rb
CHANGED
@@ -20,9 +20,13 @@ module YANAPI
|
|
20
20
|
# A technical error accessing the server,
|
21
21
|
# may be caught and handled with a new attempt.
|
22
22
|
class ExternalError < Error
|
23
|
-
def initialize(
|
24
|
-
msg = "Some external error occured:\n"
|
25
|
-
|
23
|
+
def initialize(arg)
|
24
|
+
msg = "Some external error occured:\n"
|
25
|
+
if arg.kind_of?(String)
|
26
|
+
msg += arg
|
27
|
+
else
|
28
|
+
msg += "#{exception.class}: #{exception.message}"
|
29
|
+
end
|
26
30
|
super(msg)
|
27
31
|
end
|
28
32
|
end
|
data/lib/yanapi/query.rb
CHANGED
@@ -16,8 +16,8 @@ module YANAPI
|
|
16
16
|
VALID_OUTPUT_FORMATS = [nil, 'xml', 'php', 'rss', 'json']
|
17
17
|
|
18
18
|
# It accepts a two dimensional hash:
|
19
|
-
#
|
20
|
-
#
|
19
|
+
# {:method => 'questionSearch', :query_params =>
|
20
|
+
# {:appid => 'YahooDemo', :query => 'Haus'}}
|
21
21
|
def initialize(params)
|
22
22
|
@method = params[:method]
|
23
23
|
@params = check_params(params[:query_params])
|
@@ -25,43 +25,24 @@ module YANAPI
|
|
25
25
|
@output = @params[:output] || 'xml'
|
26
26
|
end
|
27
27
|
|
28
|
-
# main method
|
28
|
+
# This is the main method. It gets the body of the HTTP response and
|
29
|
+
# invokes the respective check.
|
30
|
+
# It returns the response or <nil> if the response is emtpy.
|
29
31
|
def get
|
30
|
-
|
32
|
+
|
33
|
+
http_response = get_response(@url)
|
31
34
|
|
32
35
|
case @output
|
33
36
|
when 'xml'
|
34
|
-
|
35
|
-
xml = Nokogiri::XML::Document.parse(http_response.body, nil, nil, 0)
|
36
|
-
result = prove_xml(xml) ? http_response.body : nil
|
37
|
+
prove_xml(http_response)
|
37
38
|
when 'json'
|
38
|
-
|
39
|
+
fail NotImplementedError, 'We do not handle JSON yet!'
|
39
40
|
when 'php'
|
40
|
-
|
41
|
+
fail NotImplementedError, 'We do not handle PHP yet!'
|
41
42
|
when 'rss'
|
42
|
-
|
43
|
+
fail NotImplementedError, 'We do not handle RSS yet!'
|
43
44
|
end
|
44
45
|
|
45
|
-
# TODO: make a fine grained distinction
|
46
|
-
case http_response
|
47
|
-
when Net::HTTPSuccess
|
48
|
-
return result
|
49
|
-
|
50
|
-
when Net::HTTPBadRequest,
|
51
|
-
Net::HTTPForbidden,
|
52
|
-
Net::HTTPServiceUnavailable
|
53
|
-
raise ExternalError.new("#{http_response}:\n\n#{result}")
|
54
|
-
else
|
55
|
-
raise ExternalError.new("#{http_response}:\n\n#{result}")
|
56
|
-
end
|
57
|
-
|
58
|
-
# are all external errors caught here?
|
59
|
-
rescue Net::HTTPError, SocketError, Timeout::Error,
|
60
|
-
IOError, SystemCallError => e
|
61
|
-
raise ExternalError, e
|
62
|
-
# add JSON, PHP, RSS errors here
|
63
|
-
rescue Nokogiri::XML::SyntaxError => e
|
64
|
-
raise ContentError.new(e)
|
65
46
|
end # get
|
66
47
|
|
67
48
|
#########
|
@@ -90,7 +71,7 @@ module YANAPI
|
|
90
71
|
# Multiple values will be provided as Arrays:
|
91
72
|
# :category_id => [123, 456, 789].
|
92
73
|
params.each_pair do |k, v|
|
93
|
-
unless (v.
|
74
|
+
unless (v.kind_of?(String) || v.kind_of?(Fixnum))
|
94
75
|
fail UserError, "The value <:#{k}> is not unique!"
|
95
76
|
end
|
96
77
|
end
|
@@ -107,7 +88,7 @@ module YANAPI
|
|
107
88
|
|
108
89
|
|
109
90
|
expanded_params = expand_params(params) # It is an array now!
|
110
|
-
escaped_params =
|
91
|
+
escaped_params = expanded_params.map do |k, v|
|
111
92
|
k = URI.escape(k.to_s, unsafe = reserved_chars)
|
112
93
|
v = URI.escape(v.to_s, unsafe = reserved_chars)
|
113
94
|
"#{k}=#{v}"
|
@@ -135,26 +116,54 @@ module YANAPI
|
|
135
116
|
expanded_params.sort_by { |k, v| [k.to_s, v.to_s] }
|
136
117
|
end
|
137
118
|
|
138
|
-
#
|
139
|
-
#
|
140
|
-
|
119
|
+
# It sends a HTTP request, rescues any external errors issuing an
|
120
|
+
# YANAPI::ExternalError, checks the HTTP code and fails if it is not 200.
|
121
|
+
# Otherwise it returns the body of the HTTP response.
|
122
|
+
def get_response(url)
|
123
|
+
begin
|
124
|
+
http_response = Net::HTTP.get_response(url)
|
125
|
+
rescue Net::HTTPError, SocketError, Timeout::Error, IOError => e
|
126
|
+
fail ExternalError, e
|
127
|
+
end
|
128
|
+
|
129
|
+
case http_response
|
130
|
+
when Net::HTTPSuccess
|
131
|
+
http_response.body
|
132
|
+
when Net::HTTPBadRequest,
|
133
|
+
Net::HTTPForbidden,
|
134
|
+
Net::HTTPServiceUnavailable
|
135
|
+
# These errors are documented by Yahoo!.
|
136
|
+
fail(ExternalError, http_response.header)
|
137
|
+
else
|
138
|
+
fail(ExternalError, "Unexpected HTTP response: #{http_response.header}.")
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
# It parses the input, checks for xml validity and proves if it is empty.
|
144
|
+
# It returns then <xml> as string or <nil> if the answer is emtpy.
|
145
|
+
def prove_xml(xml_as_str)
|
146
|
+
begin
|
147
|
+
# Set the value to STRICT (0), otherwise no errors will be raised!
|
148
|
+
xml = Nokogiri::XML::Document.parse(xml_as_str, nil, nil, 0)
|
149
|
+
rescue Nokogiri::XML::SyntaxError
|
150
|
+
fail(ContentError, 'Erroneous XML response!')
|
151
|
+
end
|
152
|
+
|
141
153
|
error = xml.at_xpath('/xmlns:Error', xml.root.namespaces)
|
142
154
|
if error
|
143
155
|
message = 'The following errors were detected:'
|
144
156
|
error.xpath('//xmlns:Message', xml.root.namespaces).each do |msg|
|
145
157
|
message << "\n" + msg.content
|
146
158
|
end
|
147
|
-
|
159
|
+
fail(ContentError, message)
|
148
160
|
end
|
149
161
|
|
150
162
|
question = xml.at_xpath('//xmlns:Question', xml.root.namespaces)
|
151
163
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
nil
|
156
|
-
end
|
157
|
-
end
|
164
|
+
question ? xml_as_str : nil
|
165
|
+
end
|
166
|
+
|
158
167
|
end # Query
|
159
168
|
|
160
169
|
end # YANAPI
|
data/lib/yanapi/version.rb
CHANGED
data/test/test_query.rb
CHANGED
@@ -40,8 +40,7 @@ class TestQuery < Test::Unit::TestCase
|
|
40
40
|
|
41
41
|
# The following constants should have the given value and type.
|
42
42
|
def test_values_of_constants
|
43
|
-
assert_equal('http://answers.yahooapis.com',
|
44
|
-
YANAPI::Query::HOST)
|
43
|
+
assert_equal('http://answers.yahooapis.com', YANAPI::Query::HOST)
|
45
44
|
assert_equal('AnswersService', YANAPI::Query::SERVICE)
|
46
45
|
assert_equal('V1', YANAPI::Query::SERVICE_VERSION)
|
47
46
|
end
|
@@ -95,7 +94,18 @@ class TestQuery < Test::Unit::TestCase
|
|
95
94
|
|
96
95
|
|
97
96
|
## Functional tests
|
98
|
-
|
97
|
+
|
98
|
+
# It should create well formed urls.
|
99
|
+
def test_url_form
|
100
|
+
q = YANAPI::Query.new(@params)
|
101
|
+
print q.inspect
|
102
|
+
etalon_url = 'http://answers.yahooapis.com/AnswersService/V1/questionSearch?appid=YahooDemo&output=xml&query=Haus'
|
103
|
+
real_url = q.instance_variable_get(:@url).to_s
|
104
|
+
assert_equal(etalon_url, real_url)
|
105
|
+
|
106
|
+
warn "\nWARNING! Expand this test for multivalue parameters!\n"
|
107
|
+
end
|
108
|
+
|
99
109
|
# It should return <nil> if the server response contains
|
100
110
|
# no answer. It is possible if we out of range or search criteria
|
101
111
|
# are too strict.
|
@@ -122,7 +132,7 @@ class TestQuery < Test::Unit::TestCase
|
|
122
132
|
assert_instance_of(String, actual_api_answer, 'Not String!')
|
123
133
|
assert_equal(expected_api_answer, actual_api_answer)
|
124
134
|
|
125
|
-
warn "\
|
135
|
+
warn "\nWANRING! Add tests for <php>, <json> and <rss>!\n"
|
126
136
|
end
|
127
137
|
|
128
138
|
# It should fail with an appropriate exception
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yanapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 2
|
10
|
+
version: 0.3.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Andrei Beliankou
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-10 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: nokogiri
|
@@ -32,9 +32,25 @@ dependencies:
|
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
35
|
+
name: rdoc
|
36
36
|
prerelease: false
|
37
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 33
|
43
|
+
segments:
|
44
|
+
- 3
|
45
|
+
- 9
|
46
|
+
- 1
|
47
|
+
version: 3.9.1
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: fakeweb
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
38
54
|
none: false
|
39
55
|
requirements:
|
40
56
|
- - ">="
|
@@ -44,7 +60,7 @@ dependencies:
|
|
44
60
|
- 0
|
45
61
|
version: "0"
|
46
62
|
type: :development
|
47
|
-
version_requirements: *
|
63
|
+
version_requirements: *id003
|
48
64
|
description: YANAPI is a programmatic API for Yahoo! Answers web services. It enables you to search for questions using key words, user IDs, category names and IDs and a pricise ID of a question. The output is provided in the xml format as well as json, php and rss.
|
49
65
|
email: a.belenkow@uni-trier.de
|
50
66
|
executables: []
|
@@ -124,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
140
|
requirements: []
|
125
141
|
|
126
142
|
rubyforge_project: yanapi
|
127
|
-
rubygems_version: 1.7
|
143
|
+
rubygems_version: 1.8.7
|
128
144
|
signing_key:
|
129
145
|
specification_version: 3
|
130
146
|
summary: YANAPI is an API for Yahoo! Answers web services.
|