eshopworks-rboss 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/TODO.txt +0 -1
- data/features/steps/web_search_steps.rb +20 -8
- data/features/web_search.feature +16 -9
- data/lib/boss/api.rb +40 -7
- data/lib/boss/config.rb +4 -2
- data/lib/boss/result_collection.rb +6 -0
- data/lib/boss/version.rb +1 -1
- data/rboss.gemspec +9 -7
- data/spec/boss/api_spec.rb +83 -60
- data/spec/boss/config_spec.rb +1 -0
- data/spec/boss/result_collection_spec.rb +10 -0
- metadata +7 -3
data/TODO.txt
CHANGED
@@ -12,27 +12,39 @@ Given "a valid API key" do
|
|
12
12
|
@api = Boss::Api.new("put-your-api-key-here")
|
13
13
|
end
|
14
14
|
|
15
|
-
When
|
15
|
+
When /^I do the following search$/ do |table|
|
16
|
+
# Do integer conversions
|
17
|
+
table.send :map_headers!, 'limit' => :limit, 'count' => :count
|
18
|
+
table.map_column!(:limit) { |x| x.nil? ? nil : x.to_i }
|
19
|
+
table.map_column!(:count) { |x| x.nil? ? nil : x.to_i }
|
16
20
|
|
17
|
-
|
21
|
+
search_options = table.hashes.first
|
22
|
+
|
23
|
+
type = search_options.delete('type')
|
24
|
+
term = search_options.delete('term')
|
25
|
+
|
26
|
+
case type
|
18
27
|
when 'web'
|
19
|
-
@results = @api.search_web(term)
|
28
|
+
@results = @api.search_web(term, search_options)
|
20
29
|
when 'news'
|
21
|
-
@results = @api.search_news(term)
|
30
|
+
@results = @api.search_news(term, search_options)
|
22
31
|
when 'images'
|
23
|
-
@results = @api.search_images(term)
|
32
|
+
@results = @api.search_images(term, search_options)
|
24
33
|
when 'spell'
|
25
|
-
@results = @api.search_spelling(term)
|
34
|
+
@results = @api.search_spelling(term, search_options)
|
26
35
|
else
|
27
|
-
raise Exception.new "invalid search: #{
|
36
|
+
raise Exception.new "invalid search: #{type}"
|
28
37
|
end
|
29
|
-
|
30
38
|
end
|
31
39
|
|
32
40
|
Then /I will receive search results/ do
|
33
41
|
@results.results.nil?.should == false
|
34
42
|
end
|
35
43
|
|
44
|
+
Then /^I will receive "([^\"]*)" search results$/ do |number_of_results|
|
45
|
+
@results.results.size.should == number_of_results.to_i
|
46
|
+
end
|
47
|
+
|
36
48
|
Then /I will be able to see the total hits/ do
|
37
49
|
@results.totalhits.to_i.should > 0
|
38
50
|
end
|
data/features/web_search.feature
CHANGED
@@ -3,16 +3,23 @@ Feature: Web Search
|
|
3
3
|
As a API user
|
4
4
|
I want to query yahoo boss
|
5
5
|
|
6
|
-
Scenario: Search
|
6
|
+
Scenario Outline: Search
|
7
7
|
Given a valid API key
|
8
|
-
When I do
|
9
|
-
|
8
|
+
When I do the following search
|
9
|
+
| type | term | count | limit |
|
10
|
+
| <type> | <term> | <count> | <limit> |
|
11
|
+
Then I will receive "<result_count>" search results
|
10
12
|
And I will be able to see the total hits
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
Examples: search types
|
15
|
+
| type | term | count | limit | result_count |
|
16
|
+
| web | monkey | | 5 | 5 |
|
17
|
+
| web | monkey | 1 | 3 | 3 |
|
18
|
+
| web | monkey | 5 | 6 | 10 |
|
19
|
+
| web | monkey | 50 | 100 | 100 |
|
20
|
+
| images | monkey | 1 | 10 | 10 |
|
21
|
+
| news | monkey | 1 | 1 | 1 |
|
22
|
+
| spell | girafe | 1 | 1 | 1 |
|
23
|
+
|
24
|
+
|
18
25
|
|
data/lib/boss/api.rb
CHANGED
@@ -8,11 +8,13 @@ module Boss
|
|
8
8
|
|
9
9
|
class Api
|
10
10
|
|
11
|
+
MAX_COUNT = 50
|
11
12
|
attr_accessor :endpoint
|
12
13
|
|
13
14
|
def initialize(app_id)
|
14
15
|
@app_id = app_id
|
15
|
-
@
|
16
|
+
@basepoint = 'http://boss.yahooapis.com'
|
17
|
+
@endpoint = "#{@basepoint}/ysearch/"
|
16
18
|
end
|
17
19
|
|
18
20
|
def search(term, *conditions, &block)
|
@@ -35,28 +37,59 @@ module Boss
|
|
35
37
|
search_boss(term, SearchService::SPELLING, *conditions, &block)
|
36
38
|
end
|
37
39
|
|
40
|
+
def next_page
|
41
|
+
make_request(@basepoint + @next_page_path) if @next_page_path
|
42
|
+
end
|
43
|
+
|
44
|
+
def previous_page
|
45
|
+
make_request(@basepoint + @previous_page_path) if @previous_page_path
|
46
|
+
end
|
47
|
+
|
38
48
|
private
|
39
49
|
def search_boss(terms, search_type=SearchService::WEB, config = {})
|
40
50
|
config = config.empty? ? Config.new : Config.new(config)
|
41
51
|
yield config if block_given?
|
42
|
-
|
52
|
+
|
43
53
|
raise InvalidFormat, "'#{config.format}' is not a valid format. Valid formats are: #{FORMATS.join(',')}" unless FORMATS.include?(config.format) || config.format?
|
44
54
|
raise InvalidConfig, "count must be > 0" unless config.count>0
|
45
55
|
raise InvalidConfig, "App ID cannot be empty!" if @app_id.empty?
|
46
56
|
|
47
|
-
|
57
|
+
config.count = MAX_COUNT if config.count > MAX_COUNT
|
58
|
+
|
59
|
+
# Remember search instructions for iterating over pages
|
60
|
+
@current_config = config.dup
|
61
|
+
|
62
|
+
if limit = config.delete_field(:limit)
|
63
|
+
# Do a first request to gather required information
|
64
|
+
results = make_request(build_request_url(terms, search_type, config) )
|
65
|
+
total_count = results.totalhits.to_i
|
66
|
+
limit = total_count if total_count < limit
|
67
|
+
number_of_requests = (limit.to_f / config.count).ceil - 1
|
68
|
+
|
69
|
+
number_of_requests.times { results += next_page }
|
70
|
+
results
|
71
|
+
else
|
72
|
+
results = make_request(build_request_url(terms, search_type, config))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def make_request(url)
|
77
|
+
request = URI.parse(url)
|
48
78
|
response = Net::HTTP.get_response(request)
|
49
79
|
|
50
80
|
case response.code
|
51
81
|
when "200"
|
52
82
|
data = response.body
|
53
|
-
|
54
|
-
if
|
83
|
+
|
84
|
+
if @current_config.format?
|
55
85
|
search_results = ResultFactory.build(data)
|
56
86
|
|
57
87
|
# set requested page count size
|
58
88
|
# Used in math to determine total pages and current page
|
59
|
-
search_results.set_instance_variable('page_count',
|
89
|
+
search_results.set_instance_variable('page_count', @current_config.count) if search_results.kind_of?(Boss::ResultCollection)
|
90
|
+
|
91
|
+
@next_page_path = search_results.nextpage if search_results.respond_to?(:nextpage)
|
92
|
+
@previous_page_path = search_results.prevpage if search_results.respond_to?(:prevpage)
|
60
93
|
else
|
61
94
|
search_results = data
|
62
95
|
end
|
@@ -94,4 +127,4 @@ module Boss
|
|
94
127
|
|
95
128
|
end
|
96
129
|
|
97
|
-
end
|
130
|
+
end
|
data/lib/boss/config.rb
CHANGED
@@ -5,8 +5,10 @@ module Boss
|
|
5
5
|
class Config < OpenStruct
|
6
6
|
|
7
7
|
def initialize(hash={})
|
8
|
-
#Setup defaults
|
9
|
-
|
8
|
+
#Setup defaults
|
9
|
+
hash[:count] ||= hash[:limit] ||= 10
|
10
|
+
|
11
|
+
super({:lang => "en"}.merge(hash))
|
10
12
|
end
|
11
13
|
|
12
14
|
def to_url
|
@@ -25,6 +25,12 @@ module Boss
|
|
25
25
|
@results[key]
|
26
26
|
end
|
27
27
|
|
28
|
+
def +(other)
|
29
|
+
clone = self.clone
|
30
|
+
clone.instance_variable_set("@results", clone.results + other.results)
|
31
|
+
clone
|
32
|
+
end
|
33
|
+
|
28
34
|
# Implements neccessary api for the will_paginate view helper
|
29
35
|
# to work with result sets out of the box
|
30
36
|
def size
|
data/lib/boss/version.rb
CHANGED
data/rboss.gemspec
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
Gem::Specification.new do |s|
|
2
4
|
s.name = %q{rboss}
|
3
|
-
s.version = "0.1.
|
5
|
+
s.version = "0.1.9"
|
4
6
|
|
5
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
8
|
s.authors = ["Joseph Wilk"]
|
7
|
-
s.date = %q{
|
9
|
+
s.date = %q{2009-06-04}
|
8
10
|
s.description = %q{Api wrapping Yahoo Boss search}
|
9
11
|
s.email = ["joe@eshopworks.co.uk"]
|
10
12
|
s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "README.txt", "TODO.txt"]
|
@@ -14,28 +16,28 @@ Gem::Specification.new do |s|
|
|
14
16
|
s.rdoc_options = ["--main", "README.txt"]
|
15
17
|
s.require_paths = ["lib"]
|
16
18
|
s.rubyforge_project = %q{rboss}
|
17
|
-
s.rubygems_version = %q{1.
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
18
20
|
s.summary = %q{Api wrapping Yahoo Boss search}
|
19
21
|
|
20
22
|
if s.respond_to? :specification_version then
|
21
23
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
22
24
|
s.specification_version = 2
|
23
25
|
|
24
|
-
if
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
25
27
|
s.add_runtime_dependency(%q<json_pure>, [">= 1.1.3"])
|
26
28
|
s.add_runtime_dependency(%q<rspec>, [">= 1.1.4"])
|
27
29
|
s.add_runtime_dependency(%q<diff-lcs>, [">= 1.1.2"])
|
28
|
-
s.add_development_dependency(%q<hoe>, [">= 1.
|
30
|
+
s.add_development_dependency(%q<hoe>, [">= 1.12.2"])
|
29
31
|
else
|
30
32
|
s.add_dependency(%q<json_pure>, [">= 1.1.3"])
|
31
33
|
s.add_dependency(%q<rspec>, [">= 1.1.4"])
|
32
34
|
s.add_dependency(%q<diff-lcs>, [">= 1.1.2"])
|
33
|
-
s.add_dependency(%q<hoe>, [">= 1.
|
35
|
+
s.add_dependency(%q<hoe>, [">= 1.12.2"])
|
34
36
|
end
|
35
37
|
else
|
36
38
|
s.add_dependency(%q<json_pure>, [">= 1.1.3"])
|
37
39
|
s.add_dependency(%q<rspec>, [">= 1.1.4"])
|
38
40
|
s.add_dependency(%q<diff-lcs>, [">= 1.1.2"])
|
39
|
-
s.add_dependency(%q<hoe>, [">= 1.
|
41
|
+
s.add_dependency(%q<hoe>, [">= 1.12.2"])
|
40
42
|
end
|
41
43
|
end
|
data/spec/boss/api_spec.rb
CHANGED
@@ -15,80 +15,44 @@ describe Boss::Api do
|
|
15
15
|
#TODO: Mock HTTPSuccess
|
16
16
|
def mock_http_response(stubs={})
|
17
17
|
# mock('Net::HTTPSuccess',{:head => Net::HTTPSuccess.new('1.2', '200', 'OK'), :body => '{"ysearchresponse":{}}' })
|
18
|
-
mock('http_response', {:body =>
|
18
|
+
mock('http_response', {:body => yahoo_json, :code => "200"}.merge(stubs))
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
|
21
|
+
def mock_http_response_with_one_record(stubs={})
|
22
|
+
mock_http_response(:body => yahoo_json)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
@api.endpoint = 'http://www.example.com/'
|
25
|
+
def mock_results(stubs={})
|
26
|
+
mock(:results, {:totalhits => 100 }.merge(stubs) )
|
28
27
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
Net::HTTP.should_receive(:get_response).and_return{ mock_http_response }
|
34
|
-
|
35
|
-
@api.search_spelling("girafes")
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should build the spelling objects" do
|
39
|
-
Net::HTTP.stub!(:get_response).and_return{ mock_http_response }
|
40
|
-
Boss::ResultFactory.should_receive(:build).with(yahoo_json)
|
41
|
-
|
42
|
-
@api.search_spelling("girafes")
|
43
|
-
end
|
44
|
-
|
28
|
+
|
29
|
+
def yahoo_json(hash = {})
|
30
|
+
defaults = {:nextpage => "nextpage", :totalhits => "1000"}
|
31
|
+
%[{"ysearchresponse": #{defaults.merge(hash).to_json} }]
|
45
32
|
end
|
46
33
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
@api.search_news("monkey")
|
52
|
-
end
|
53
|
-
|
54
|
-
it "should build the news objects" do
|
55
|
-
Net::HTTP.stub!(:get_response).and_return{ mock_http_response }
|
56
|
-
Boss::ResultFactory.should_receive(:build).with(yahoo_json)
|
57
|
-
|
58
|
-
@api.search_news("monkey")
|
59
|
-
end
|
34
|
+
before(:each) do
|
35
|
+
@api = Boss::Api.new( appid = 'test' )
|
36
|
+
@api.endpoint = 'http://www.example.com/'
|
60
37
|
end
|
61
38
|
|
62
|
-
|
63
|
-
|
64
|
-
Net::HTTP.should_receive(:get_response).and_return{ mock_http_response }
|
39
|
+
%w(spelling new web image).each do |type|
|
40
|
+
describe "responding to #{type} search" do
|
65
41
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
it "should build the image objects" do
|
70
|
-
Net::HTTP.stub!(:get_response).and_return{ mock_http_response }
|
71
|
-
Boss::ResultFactory.should_receive(:build).with(yahoo_json)
|
72
|
-
|
73
|
-
@api.search_images("hippo")
|
74
|
-
end
|
75
|
-
end
|
42
|
+
it "should make a #{type} request to yahoo service" do
|
43
|
+
Net::HTTP.should_receive(:get_response).and_return{ mock_http_response }
|
76
44
|
|
77
|
-
|
78
|
-
|
79
|
-
it "should make a web request to yahoo service" do
|
80
|
-
Net::HTTP.should_receive(:get_response).and_return{ mock_http_response }
|
45
|
+
@api.search_web("monkey")
|
46
|
+
end
|
81
47
|
|
82
|
-
|
83
|
-
|
48
|
+
it "should build the #{type} objects" do
|
49
|
+
Net::HTTP.stub!(:get_response).and_return{ mock_http_response }
|
50
|
+
Boss::ResultFactory.should_receive(:build).with(yahoo_json).and_return(mock_results)
|
84
51
|
|
85
|
-
|
86
|
-
|
87
|
-
Boss::ResultFactory.should_receive(:build).with(yahoo_json)
|
52
|
+
@api.search_web("monkey")
|
53
|
+
end
|
88
54
|
|
89
|
-
@api.search_web("monkey")
|
90
55
|
end
|
91
|
-
|
92
56
|
end
|
93
57
|
|
94
58
|
describe "failed search" do
|
@@ -183,5 +147,64 @@ describe Boss::Api do
|
|
183
147
|
end
|
184
148
|
|
185
149
|
end
|
186
|
-
|
150
|
+
|
151
|
+
describe "#next_page" do
|
152
|
+
it "should request the next results when 'nextpage' is given" do
|
153
|
+
response_mock = mock_http_response(:body => yahoo_json(:nextpage => "nextpage"))
|
154
|
+
|
155
|
+
Net::HTTP.should_receive(:get_response).exactly(2).times.and_return{ response_mock }
|
156
|
+
@api.search_web("monkey?magic", :count => 1)
|
157
|
+
@api.next_page.should be_kind_of Boss::ResultCollection
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should not request the next results when 'nextpage' is nil" do
|
161
|
+
response_mock = mock_http_response(:body => yahoo_json(:nextpage => nil))
|
162
|
+
|
163
|
+
Net::HTTP.should_receive(:get_response).exactly(1).times.and_return{ response_mock }
|
164
|
+
@api.search_web("monkey?magic", :count => 1)
|
165
|
+
@api.next_page.should be_nil
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "#previous_page" do
|
170
|
+
it "should request the next results when 'nextpage' is given" do
|
171
|
+
response_mock = mock_http_response(:body => yahoo_json(:prevpage => "prevpage"))
|
172
|
+
|
173
|
+
Net::HTTP.should_receive(:get_response).exactly(2).times.and_return{ response_mock }
|
174
|
+
@api.search_web("monkey?magic", :count => 1)
|
175
|
+
@api.previous_page.should be_kind_of Boss::ResultCollection
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should not request the next results when 'nextpage' is nil" do
|
179
|
+
response_mock = mock_http_response(:body => yahoo_json(:prevpage => nil))
|
180
|
+
|
181
|
+
Net::HTTP.should_receive(:get_response).exactly(1).times.and_return{ response_mock }
|
182
|
+
@api.search_web("monkey?magic", :count => 1)
|
183
|
+
@api.previous_page.should be_nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "MAX_COUNT" do
|
188
|
+
it "should be 50" do
|
189
|
+
Boss::Api::MAX_COUNT.should eql 50
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "#search" do
|
194
|
+
|
195
|
+
[ {:count => 2, :limit => 100, :number_of_requests => 50, :totalhits => "400"},
|
196
|
+
{:count => 50, :limit => 100, :number_of_requests => 2, :totalhits => "400"},
|
197
|
+
{:count => 3, :limit => 50, :number_of_requests => 17, :totalhits => "50"},
|
198
|
+
{:count => 3, :limit => 50, :number_of_requests => 14, :totalhits => "40"},
|
199
|
+
{:count => 100, :limit => 1, :number_of_requests => 1, :totalhits => "40"},
|
200
|
+
{:limit => 50, :count => nil, :number_of_requests => 1, :totalhits => "100"}
|
201
|
+
].each do |prop|
|
202
|
+
it "should do #{prop[:number_of_requests]} number of requests when count is #{prop[:count]} and limit is #{prop[:limit]}" do
|
203
|
+
response_mock = mock_http_response(:body => yahoo_json(:nextpage => "nextpage", :totalhits => prop[:totalhits]))
|
204
|
+
Net::HTTP.should_receive(:get_response).exactly(prop[:number_of_requests]).times.and_return{ response_mock }
|
205
|
+
@api.search_web("monkey?magic", :count => prop[:count], :limit => prop[:limit])
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
187
210
|
end
|
data/spec/boss/config_spec.rb
CHANGED
@@ -21,6 +21,16 @@ describe Boss::ResultCollection do
|
|
21
21
|
|
22
22
|
end
|
23
23
|
|
24
|
+
it "should allow merging two results collections" do
|
25
|
+
collection_one = Boss::ResultCollection.new
|
26
|
+
collection_one << 1
|
27
|
+
collection_two = Boss::ResultCollection.new
|
28
|
+
collection_two << 2
|
29
|
+
|
30
|
+
collection_one += collection_two
|
31
|
+
collection_one.size.should be 2
|
32
|
+
end
|
33
|
+
|
24
34
|
end
|
25
35
|
|
26
36
|
describe Boss::ResultCollection, 'implementing the will_paginate collection api' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eshopworks-rboss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Wilk
|
@@ -9,11 +9,12 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-06-04 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json_pure
|
17
|
+
type: :runtime
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
@@ -23,6 +24,7 @@ dependencies:
|
|
23
24
|
version:
|
24
25
|
- !ruby/object:Gem::Dependency
|
25
26
|
name: rspec
|
27
|
+
type: :runtime
|
26
28
|
version_requirement:
|
27
29
|
version_requirements: !ruby/object:Gem::Requirement
|
28
30
|
requirements:
|
@@ -32,6 +34,7 @@ dependencies:
|
|
32
34
|
version:
|
33
35
|
- !ruby/object:Gem::Dependency
|
34
36
|
name: diff-lcs
|
37
|
+
type: :runtime
|
35
38
|
version_requirement:
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
@@ -41,12 +44,13 @@ dependencies:
|
|
41
44
|
version:
|
42
45
|
- !ruby/object:Gem::Dependency
|
43
46
|
name: hoe
|
47
|
+
type: :development
|
44
48
|
version_requirement:
|
45
49
|
version_requirements: !ruby/object:Gem::Requirement
|
46
50
|
requirements:
|
47
51
|
- - ">="
|
48
52
|
- !ruby/object:Gem::Version
|
49
|
-
version: 1.
|
53
|
+
version: 1.12.2
|
50
54
|
version:
|
51
55
|
description: Api wrapping Yahoo Boss search
|
52
56
|
email:
|