<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,4 +1,3 @@
 == FEATURES/PROBLEMS:
 
-* Search all
 * Integrate with Tagging service
\ No newline at end of file</diff>
      <filename>TODO.txt</filename>
    </modified>
    <modified>
      <diff>@@ -12,27 +12,37 @@ Given &quot;a valid API key&quot; do
   @api = Boss::Api.new(&quot;put-your-api-key-here&quot;)
 end
 
-When /I do a '(.+)' search for '(.+)'/ do |search, term|
+When /^I do the following search$/ do |table|
+  # Do integer conversions
+  table.map_column!('count') { |x| x.to_i }
+  table.map_column!('limit') { |x| x.to_i }
 
-  case search
+  search_options = table.hashes.first
+  type = search_options.delete('type')
+  term = search_options.delete('term')
+
+  case type
   when 'web'
-    @results = @api.search_web(term)    
+    @results = @api.search_web(term, search_options)    
   when 'news'
-    @results = @api.search_news(term)    
+    @results = @api.search_news(term, search_options)    
   when 'images'
-    @results = @api.search_images(term)
+    @results = @api.search_images(term, search_options)
   when 'spell'
-    @results = @api.search_spelling(term)
+    @results = @api.search_spelling(term, search_options)
   else
-    raise Exception.new &quot;invalid search: #{search}&quot;
+    raise Exception.new &quot;invalid search: #{type}&quot;
   end
-
 end
 
 Then /I will receive search results/ do
   @results.results.nil?.should == false
 end
 
+Then /^I will receive &quot;([^\&quot;]*)&quot; search results$/ do |number_of_results|
+  @results.results.size.should == number_of_results.to_i
+end
+
 Then /I will be able to see the total hits/ do
   @results.totalhits.to_i.should &gt; 0 
-end
+end
\ No newline at end of file</diff>
      <filename>features/steps/web_search_steps.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,16 +3,22 @@ Feature: Web Search
   As a API user
   I want to query yahoo boss
 
-  Scenario: Search web
+  Scenario Outline: Search
     Given a valid API key
-    When I do a 'web' search for 'monkeys'
-    Then I will receive search results
+    When I do the following search
+      | type   | term   | count   | limit   |
+      | &lt;type&gt; | &lt;term&gt; | &lt;count&gt; | &lt;limit&gt; |
+    Then I will receive &quot;&lt;result_count&gt;&quot; search results
     And I will be able to see the total hits
   
-  More Examples:
-  | type   | term   |
-  | web    | monkey |
-  | images | monkey |
-  | news   | monkey |
-  | spell  | girafe |
+  Examples: search types
+    | type   | term   | count | limit | result_count |
+    | web    | monkey | 1     | 3     | 3            |
+    | web    | monkey | 5     | 6     | 10           |
+    | web    | monkey | 50    | 100   | 100          |
+    | images | monkey | 1     | 10    | 10           |
+    | news   | monkey | 1     | 1     | 1            | 
+    | spell  | girafe | 1     | 1     | 1            |
+    
+  
 </diff>
      <filename>features/web_search.feature</filename>
    </modified>
    <modified>
      <diff>@@ -8,11 +8,13 @@ module Boss
 
   class Api
 
+    MAX_COUNT = 50
     attr_accessor :endpoint
 
     def initialize(app_id)
       @app_id = app_id
-      @endpoint = 'http://boss.yahooapis.com/ysearch/'
+      @basepoint = 'http://boss.yahooapis.com'
+      @endpoint = &quot;#{@basepoint}/ysearch/&quot;
     end
 
     def search(term, *conditions, &amp;block)
@@ -35,28 +37,61 @@ module Boss
       search_boss(term, SearchService::SPELLING, *conditions, &amp;block)
     end
 
+    def next_page
+      make_request(@basepoint + @next_page_path) if @next_page_path
+    end
+
+    def previous_page
+      make_request(@basepoint + @previous_page_path) if @previous_page_path
+    end
+
     private
     def search_boss(terms, search_type=SearchService::WEB, config = {})
       config = config.empty? ? Config.new : Config.new(config)
       yield config if block_given?
-  
+      
       raise InvalidFormat, &quot;'#{config.format}' is not a valid format. Valid formats are: #{FORMATS.join(',')}&quot; unless FORMATS.include?(config.format) || config.format?
       raise InvalidConfig, &quot;count must be &gt; 0&quot; unless config.count&gt;0
       raise InvalidConfig, &quot;App ID cannot be empty!&quot; if @app_id.empty?
       
-      request =  URI.parse(build_request_url(terms, search_type, config))
+      config.count = MAX_COUNT if config.count &gt; MAX_COUNT
+      
+      # Remember search instructions for iterating over pages
+      @current_config = config.dup
+
+      if limit = config.delete_field(:limit)
+        # Do a first request to gather required information
+        results = make_request(build_request_url(terms, search_type, config) )
+
+        total_count = results.totalhits.to_i
+        limit = total_count if total_count &lt; limit
+        number_of_requests = (limit.to_f / config.count).ceil - 1
+
+        number_of_requests.times { results += next_page }
+        
+        results
+      else
+        results = make_request(build_request_url(terms, search_type, config))
+      end
+    end
+    
+    def make_request(url)
+      request =  URI.parse(url)
       response = Net::HTTP.get_response(request)
 
       case response.code
       when &quot;200&quot;
         data = response.body
       
-        if config.format?
+        if @current_config.format?
           search_results = ResultFactory.build(data)
           
           # set requested page count size
           # Used in math to determine total pages and current page
-          search_results.set_instance_variable('page_count', config.count) if search_results.kind_of?(Boss::ResultCollection)
+          search_results.set_instance_variable('page_count', @current_config.count) if search_results.kind_of?(Boss::ResultCollection)
+
+          @next_page_path = search_results.nextpage if search_results.respond_to?(:nextpage)
+          @previous_page_path = search_results.prevpage if search_results.respond_to?(:prevpage)
         else
           search_results = data
         end</diff>
      <filename>lib/boss/api.rb</filename>
    </modified>
    <modified>
      <diff>@@ -25,6 +25,12 @@ module Boss
       @results[key]
     end
     
+    def +(other)
+      clone = self.clone
+      clone.instance_variable_set(&quot;@results&quot;, clone.results + other.results)
+      clone
+    end
+    
     # Implements neccessary api for the will_paginate view helper
     # to work with result sets out of the box
     def size</diff>
      <filename>lib/boss/result_collection.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,11 +15,16 @@ describe Boss::Api do
   #TODO: Mock HTTPSuccess
   def mock_http_response(stubs={})
     # mock('Net::HTTPSuccess',{:head =&gt; Net::HTTPSuccess.new('1.2', '200', 'OK'), :body =&gt; '{&quot;ysearchresponse&quot;:{}}' })
-    mock('http_response', {:body =&gt; '{&quot;ysearchresponse&quot;:{}}', :code =&gt; &quot;200&quot;}.merge(stubs))
+    mock('http_response', {:body =&gt; yahoo_json, :code =&gt; &quot;200&quot;}.merge(stubs))
   end
-
-  def yahoo_json
-    '{&quot;ysearchresponse&quot;:{}}'
+  
+  def mock_http_response_with_one_record(stubs={})
+    mock_http_response(:body =&gt; yahoo_json)
+  end
+  
+  def yahoo_json(hash = {})
+    defaults = {:nextpage =&gt; &quot;nextpage&quot;, :totalhits =&gt; &quot;1000&quot;}
+    %[{&quot;ysearchresponse&quot;: #{defaults.merge(hash).to_json} }]
   end
 
   before(:each) do
@@ -183,5 +188,63 @@ describe Boss::Api do
     end
     
   end
-
+  
+  describe &quot;#next_page&quot; do
+    it &quot;should request the next results when 'nextpage' is given&quot; do
+      response_mock = mock_http_response(:body =&gt; yahoo_json(:nextpage =&gt; &quot;nextpage&quot;))
+    
+      Net::HTTP.should_receive(:get_response).exactly(2).times.and_return{ response_mock }
+      @api.search_web(&quot;monkey?magic&quot;, :count =&gt; 1)
+      @api.next_page.should be_kind_of Boss::ResultCollection
+    end
+    
+    it &quot;should not request the next results when 'nextpage' is nil&quot; do
+      response_mock = mock_http_response(:body =&gt; yahoo_json(:nextpage =&gt; nil))
+    
+      Net::HTTP.should_receive(:get_response).exactly(1).times.and_return{ response_mock }
+      @api.search_web(&quot;monkey?magic&quot;, :count =&gt; 1)
+      @api.next_page.should be_nil
+    end
+  end
+  
+  describe &quot;#previous_page&quot; do
+    it &quot;should request the next results when 'nextpage' is given&quot; do
+      response_mock = mock_http_response(:body =&gt; yahoo_json(:prevpage =&gt; &quot;prevpage&quot;))
+    
+      Net::HTTP.should_receive(:get_response).exactly(2).times.and_return{ response_mock }
+      @api.search_web(&quot;monkey?magic&quot;, :count =&gt; 1)
+      @api.previous_page.should be_kind_of Boss::ResultCollection
+    end
+    
+    it &quot;should not request the next results when 'nextpage' is nil&quot; do
+      response_mock = mock_http_response(:body =&gt; yahoo_json(:prevpage =&gt; nil))
+    
+      Net::HTTP.should_receive(:get_response).exactly(1).times.and_return{ response_mock }
+      @api.search_web(&quot;monkey?magic&quot;, :count =&gt; 1)
+      @api.previous_page.should be_nil
+    end
+  end
+  
+  describe &quot;MAX_COUNT&quot; do
+    it &quot;should be 50&quot; do
+      Boss::Api::MAX_COUNT.should eql 50
+    end
+  end
+  
+  describe &quot;#search&quot; do
+    
+    [ {:count =&gt; 2,   :limit =&gt; 100, :number_of_requests =&gt; 50, :totalhits =&gt; &quot;400&quot;},
+      {:count =&gt; 50,  :limit =&gt; 100, :number_of_requests =&gt; 2,  :totalhits =&gt; &quot;400&quot;},
+      {:count =&gt; 3,   :limit =&gt; 50,  :number_of_requests =&gt; 17, :totalhits =&gt; &quot;50&quot;},
+      {:count =&gt; 3,   :limit =&gt; 50,  :number_of_requests =&gt; 14, :totalhits =&gt; &quot;40&quot;},
+      {:count =&gt; 100, :limit =&gt; 1,   :number_of_requests =&gt; 1,  :totalhits =&gt; &quot;40&quot;}
+    ].each do |prop|
+      it &quot;should do #{prop[:number_of_requests]} number of requests when count is #{prop[:count]} and limit is #{prop[:limit]}&quot; do
+        response_mock = mock_http_response(:body =&gt; yahoo_json(:nextpage =&gt; &quot;nextpage&quot;, :totalhits =&gt; prop[:totalhits]))
+        Net::HTTP.should_receive(:get_response).exactly(prop[:number_of_requests]).times.and_return{ response_mock }
+        @api.search_web(&quot;monkey?magic&quot;, :count =&gt; prop[:count], :limit =&gt; prop[:limit])
+      end
+      
+    end
+  end
 end</diff>
      <filename>spec/boss/api_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,6 +21,16 @@ describe Boss::ResultCollection do
     
   end
   
+  it &quot;should allow merging two results collections&quot; do
+    collection_one = Boss::ResultCollection.new
+    collection_one &lt;&lt; 1
+    collection_two = Boss::ResultCollection.new
+    collection_two &lt;&lt; 2
+    
+    collection_one += collection_two
+    collection_one.size.should be 2
+  end
+  
 end
 
 describe Boss::ResultCollection, 'implementing the will_paginate collection api' do</diff>
      <filename>spec/boss/result_collection_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>22f0cbb14d51011d0f978febdddc44af392d62e6</id>
    </parent>
  </parents>
  <author>
    <name>Jeroen van Dijk</name>
    <email>jeroen@noxa.nl</email>
  </author>
  <url>http://github.com/eshopworks/rboss-gem/commit/3292be3b68ddf0c85094af004ab590a81dd243e4</url>
  <id>3292be3b68ddf0c85094af004ab590a81dd243e4</id>
  <committed-date>2009-05-28T06:13:36-07:00</committed-date>
  <authored-date>2009-05-28T05:57:18-07:00</authored-date>
  <message>Search all is now possible with the option :limit =&gt; 100. Fixed and refactored the features.

Signed-off-by: eShopworks &lt;joe@eshopworks.co.uk&gt;</message>
  <tree>cae203b0d37326e079b7e355bc11f9185a861f6d</tree>
  <committer>
    <name>eShopworks</name>
    <email>joe@eshopworks.co.uk</email>
  </committer>
</commit>
