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 CHANGED
@@ -1,4 +1,3 @@
1
1
  == FEATURES/PROBLEMS:
2
2
 
3
- * Search all
4
3
  * Integrate with Tagging service
@@ -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 /I do a '(.+)' search for '(.+)'/ do |search, term|
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
- case search
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: #{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
@@ -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 web
6
+ Scenario Outline: Search
7
7
  Given a valid API key
8
- When I do a 'web' search for 'monkeys'
9
- Then I will receive search results
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
- More Examples:
13
- | type | term |
14
- | web | monkey |
15
- | images | monkey |
16
- | news | monkey |
17
- | spell | girafe |
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
 
@@ -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
- @endpoint = 'http://boss.yahooapis.com/ysearch/'
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
- request = URI.parse(build_request_url(terms, search_type, config))
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 config.format?
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', config.count) if search_results.kind_of?(Boss::ResultCollection)
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
@@ -5,8 +5,10 @@ module Boss
5
5
  class Config < OpenStruct
6
6
 
7
7
  def initialize(hash={})
8
- #Setup defaults
9
- super({:count => 10, :lang => "en"}.merge(hash))
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
@@ -2,7 +2,7 @@ module Boss #:nodoc:
2
2
  class VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- TINY = 8
5
+ TINY = 9
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -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.8"
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{2008-10-31}
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.2.0}
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 current_version >= 3 then
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.8.2"])
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.8.2"])
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.8.2"])
41
+ s.add_dependency(%q<hoe>, [">= 1.12.2"])
40
42
  end
41
43
  end
@@ -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 => '{"ysearchresponse":{}}', :code => "200"}.merge(stubs))
18
+ mock('http_response', {:body => yahoo_json, :code => "200"}.merge(stubs))
19
19
  end
20
20
 
21
- def yahoo_json
22
- '{"ysearchresponse":{}}'
21
+ def mock_http_response_with_one_record(stubs={})
22
+ mock_http_response(:body => yahoo_json)
23
23
  end
24
24
 
25
- before(:each) do
26
- @api = Boss::Api.new( appid = 'test' )
27
- @api.endpoint = 'http://www.example.com/'
25
+ def mock_results(stubs={})
26
+ mock(:results, {:totalhits => 100 }.merge(stubs) )
28
27
  end
29
-
30
- describe "responding to spelling search" do
31
-
32
- it "should make a spelling request to yahoo service" do
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
- describe "responding to news search" do
48
- it "should make a news request to yahoo service" do
49
- Net::HTTP.should_receive(:get_response).and_return{ mock_http_response }
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
- describe "responding to image search" do
63
- it "should make a image request to yahoo service" do
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
- @api.search_images("hippo")
67
- end
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
- describe "responding to web search" do
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
- @api.search_web("monkey")
83
- end
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
- it "should build the web objects" do
86
- Net::HTTP.stub!(:get_response).and_return{ mock_http_response }
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
@@ -5,6 +5,7 @@ describe Boss::Config do
5
5
  it "should create with defaults" do
6
6
  config = Boss::Config.new
7
7
 
8
+ config.respond_to?(:limit).should be_true
8
9
  config.respond_to?(:count).should be_true
9
10
  config.respond_to?(:lang).should be_true
10
11
  end
@@ -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.8
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: 2008-10-31 00:00:00 -07:00
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.8.2
53
+ version: 1.12.2
50
54
  version:
51
55
  description: Api wrapping Yahoo Boss search
52
56
  email: