Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: ada173d930
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 208 lines (158 sloc) 4.053 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

module Google
  class Search
    
    #--
    # Mixins
    #++
    
    include Enumerable
    
    #--
    # Constants
    #++
    
    URI = 'http://www.google.com/uds'
    
    #--
    # Exceptions
    #++
    
    class Error < StandardError; end
    
    ##
    # Version. Defaults to 1.0
    
    attr_accessor :version
    
    ##
    # Offset. Defaults to 0
    
    attr_accessor :offset
    
    ##
    # Language. Defaults to :en
    
    attr_accessor :language
    
    ##
    # Weither or not a search request has been sent.
    
    attr_accessor :sent
    
    ##
    # Query. Defaults to nil
    
    attr_accessor :query
    
    ##
    # API Key. Defaults to :notsupplied
    
    attr_accessor :api_key
    
    ##
    # Size. Defaults to :large
    #
    # - :small = 4
    # - :large = 8
    #
    
    attr_accessor :size
    
    ##
    # Type symbol:
    #
    # - :local
    # - :web
    # - :video
    # - :blog
    # - :news
    # - :image
    # - :book
    # - :patent
    #
    
    attr_accessor :type
    
    ##
    # Additional options. All those listed above
    # are deleted. The remaining represent query
    # string key / value pairs.
    
    attr_reader :options
    
    ##
    # Initialize search _type_ with _options_. Optionally
    # a block may be passed, and the Search instance will
    # be yielded to it.
    
    def initialize type, options = {}, &block
      @type = type
      @version = options.delete(:version) || 1.0
      @offset = options.delete(:offset) || 0
      @size = options.delete(:size) || :large
      @language = options.delete(:language) || :en
      @query = options.delete(:query)
      @api_key = options.delete(:api_key) || :notsupplied
      @options = options
      yield self if block
    end
    
    ##
    # Set a response _block_ which is called every time
    # #get_response is called. Useful for reporting etc.
    
    def each_response &block
      @each_response = block
    end
    
    ##
    # Iterate each item with _block_.
    
    def each_item &block
      response = self.next.response
      if response.valid?
        response.each { |item| yield item }
        each_item &block
      end
    end
    alias :each :each_item
    
    ##
    # Return all items.
    
    def all_items
      select { true }
    end
    alias :all :all_items
    
    ##
    # Return uri.
    
    def get_uri
      raise Error, 'query must be present' unless query.respond_to? :to_str
      raise Error, 'API version must be present' unless Numeric === version
      URI + "/G#{@type}Search?" + ([
        [:start, offset],
        [:rsz, size],
        [:hl, language],
        [:key, api_key],
        [:v, version],
        [:q, Search.url_encode(query.to_str)]
      ] + options.to_a).map { |key, value| "#{key}=#{value}" }.join('&')
    end
    
    ##
    # Prepare for next request.
    
    def next
      @offset += Search.size_for(size) if sent
      self
    end
    
    ##
    # Return raw JSON response string.
    
    def get_raw
      @sent = true
      open(get_uri).read
    end
    
    ##
    # Return hash parsed from the raw JSON response.
    
    def get_hash
      Search.json_decode get_raw
    end
    
    ##
    # Return Response object wrapping the JSON
    # response hash.
    
    def get_response
      raw = get_raw
      hash = Search.json_decode raw
      hash['responseSize'] = size
      response = Response.new hash
      response.raw = raw
      @each_response.call response if @each_response
      response
    end
    alias :response :get_response
    
    ##
    # Return int for size _sym_.
    
    def self.size_for sym
      { :small => 4,
        :large => 8 }[sym]
    end
    
    ##
    # Decode JSON _string_.
    
    def self.json_decode string
      JSON.parse string
    end
    
    ##
    # Url encode _string_.
    
    def self.url_encode string
      string.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
        '%' + $1.unpack('H2' * $1.size).join('%').upcase
      }.tr(' ', '+')
    end
    
  end
end
Something went wrong with that request. Please try again.