├── History.rdoc ├── Manifest ├── README.rdoc ├── Rakefile ├── examples ├── image.rb ├── images.html └── web.rb ├── google-search.gemspec ├── lib ├── google-search.rb └── google-search │ ├── item.rb │ ├── item │ ├── base.rb │ ├── blog.rb │ ├── book.rb │ ├── image.rb │ ├── local.rb │ ├── news.rb │ ├── patent.rb │ ├── video.rb │ └── web.rb │ ├── response.rb │ ├── search.rb │ ├── search │ ├── base.rb │ ├── blog.rb │ ├── book.rb │ ├── image.rb │ ├── local.rb │ ├── mixins.rb │ ├── mixins │ │ ├── filter.rb │ │ ├── order_by.rb │ │ └── safety_level.rb │ ├── news.rb │ ├── patent.rb │ ├── video.rb │ └── web.rb │ └── version.rb ├── spec ├── fixtures │ ├── 400-response.json │ ├── blog-response.json │ ├── book-response.json │ ├── image-response.json │ ├── invalid-response.json │ ├── local-response.json │ ├── news-response.json │ ├── patent-response.json │ ├── video-response.json │ ├── web-2-response.json │ └── web-response.json ├── item_blog_spec.rb ├── item_book_spec.rb ├── item_image_spec.rb ├── item_local_spec.rb ├── item_news_spec.rb ├── item_patent_spec.rb ├── item_spec.rb ├── item_video_spec.rb ├── item_web_spec.rb ├── response_spec.rb ├── search_blog_spec.rb ├── search_book_spec.rb ├── search_image_spec.rb ├── search_local_spec.rb ├── search_news_spec.rb ├── search_patent_spec.rb ├── search_spec.rb ├── search_video_spec.rb ├── search_web_spec.rb ├── spec.opts └── spec_helper.rb └── tasks ├── docs.rake ├── gemspec.rake └── spec.rake /History.rdoc: -------------------------------------------------------------------------------- 1 | 2 | === 1.0.3 / 2012-08-29 3 | 4 | * Support multibyte keyword 5 | 6 | === 1.0.2 / 2009-07-27 7 | 8 | * Defaulting Item#uri to unescapedUrl 9 | 10 | === 1.0.0 / 2009-07-24 11 | 12 | * Initial release -------------------------------------------------------------------------------- /Manifest: -------------------------------------------------------------------------------- 1 | examples/image.rb 2 | examples/images.html 3 | examples/web.rb 4 | google-search.gemspec 5 | History.rdoc 6 | lib/google-search/item/base.rb 7 | lib/google-search/item/blog.rb 8 | lib/google-search/item/book.rb 9 | lib/google-search/item/image.rb 10 | lib/google-search/item/local.rb 11 | lib/google-search/item/news.rb 12 | lib/google-search/item/patent.rb 13 | lib/google-search/item/video.rb 14 | lib/google-search/item/web.rb 15 | lib/google-search/item.rb 16 | lib/google-search/response.rb 17 | lib/google-search/search/base.rb 18 | lib/google-search/search/blog.rb 19 | lib/google-search/search/book.rb 20 | lib/google-search/search/image.rb 21 | lib/google-search/search/local.rb 22 | lib/google-search/search/mixins/filter.rb 23 | lib/google-search/search/mixins/order_by.rb 24 | lib/google-search/search/mixins/safety_level.rb 25 | lib/google-search/search/mixins.rb 26 | lib/google-search/search/news.rb 27 | lib/google-search/search/patent.rb 28 | lib/google-search/search/video.rb 29 | lib/google-search/search/web.rb 30 | lib/google-search/search.rb 31 | lib/google-search/version.rb 32 | lib/google-search.rb 33 | Manifest 34 | Rakefile 35 | README.rdoc 36 | spec/fixtures/400-response.json 37 | spec/fixtures/blog-response.json 38 | spec/fixtures/book-response.json 39 | spec/fixtures/image-response.json 40 | spec/fixtures/invalid-response.json 41 | spec/fixtures/local-response.json 42 | spec/fixtures/news-response.json 43 | spec/fixtures/patent-response.json 44 | spec/fixtures/video-response.json 45 | spec/fixtures/web-2-response.json 46 | spec/fixtures/web-response.json 47 | spec/item_blog_spec.rb 48 | spec/item_book_spec.rb 49 | spec/item_image_spec.rb 50 | spec/item_local_spec.rb 51 | spec/item_news_spec.rb 52 | spec/item_patent_spec.rb 53 | spec/item_spec.rb 54 | spec/item_video_spec.rb 55 | spec/item_web_spec.rb 56 | spec/response_spec.rb 57 | spec/search_blog_spec.rb 58 | spec/search_book_spec.rb 59 | spec/search_image_spec.rb 60 | spec/search_local_spec.rb 61 | spec/search_news_spec.rb 62 | spec/search_patent_spec.rb 63 | spec/search_spec.rb 64 | spec/search_video_spec.rb 65 | spec/search_web_spec.rb 66 | spec/spec.opts 67 | spec/spec_helper.rb 68 | tasks/docs.rake 69 | tasks/gemspec.rake 70 | tasks/spec.rake 71 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | 2 | = Google deprecated their API, this package no longer works 3 | 4 | = Google Search 5 | 6 | Fast, feature rich Google Search client. Search web results, images, 7 | books, local, patents, blogs and news using an elegant Ruby API with 8 | only a single dependency. 9 | 10 | 11 | == Dependencies 12 | 13 | * json gem 14 | 15 | == Features 16 | 17 | * Fast 18 | * Lazy Enumeration (requests more results from google, not all at once) 19 | * Populates Google::Search::Item's blog, book, image, local, news, patent, video, and web 20 | * Clean, well documented source code 21 | * Full test coverage 22 | 23 | == Classes 24 | 25 | - Google::Search | superclass of all other search classes. Is Enumerable 26 | - Google::Search::Web | web search. 27 | - Google::Search::Blog | blog search. 28 | - Google::Search::Book | book search. 29 | - Google::Search::Image | image search. 30 | - Google::Search::Local | local search. 31 | - Google::Search::News | news search. 32 | - Google::Search::Patent | patent search. 33 | - Google::Search::Video | video search. 34 | - Google::Search::Response | single request response, containing items. Is Enumerable 35 | - Google::Search::Item | single item, superclass of all other items. 36 | - Google::Search::Item::Web | single item containing web specific data. 37 | - Google::Search::Item::Blog | single item containing blog specific data. 38 | - Google::Search::Item::Book | single item containing book specific data. 39 | - Google::Search::Item::Image | single item containing image specific data. 40 | - Google::Search::Item::Local | single item containing local specific data. 41 | - Google::Search::Item::News | single item containing news specific data. 42 | - Google::Search::Item::Patent | single item containing patent specific data. 43 | - Google::Search::Item::Video | single item containing video specific data. 44 | 45 | == Arbitrary Query String Support 46 | 47 | Arbitrary key / value pairs may be passed to Google::Search.new, all options 48 | passed that are not assigned (deleted) will pass on to be part of the query string. 49 | 50 | For argument reference visit: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_web 51 | 52 | == Items 53 | 54 | Each item type (web, image, etc) is a subclass of Google::Search::Item, 55 | whose constant is Google::Search::Item::{Web, Image, Blog, etc}. Every aspect 56 | of google-search is well documented, so poke around to learn more or view 57 | spec/item_spec.rb for item attributes. 58 | 59 | == Image example 60 | 61 | Below we simply create a new :image search, and pass a :query string. 62 | Query strings are urlencoded when Search#get_uri is called, so there 63 | is no reason to do so manually. 64 | 65 | File.open('path/to/images.html', 'w+') do |file| 66 | Google::Search::Image.new(:query => 'Cookies').each do |image| 67 | file.write %() 68 | end 69 | end 70 | 71 | == More Information 72 | 73 | * View the source :) 74 | 75 | == License: 76 | 77 | (The MIT License) 78 | 79 | Copyright (c) 2009 TJ Holowaychuk 80 | 81 | Permission is hereby granted, free of charge, to any person obtaining 82 | a copy of this software and associated documentation files (the 83 | 'Software'), to deal in the Software without restriction, including 84 | without limitation the rights to use, copy, modify, merge, publish, 85 | distribute, sublicense, an d/or sell copies of the Software, and to 86 | permit persons to whom the Software is furnished to do so, subject to 87 | the following conditions: 88 | 89 | The above copyright notice and this permission notice shall be 90 | included in all copies or substantial portions of the Software. 91 | 92 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 93 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 94 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 95 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 96 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 97 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 98 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 99 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | 2 | $:.unshift 'lib' 3 | require 'google-search' 4 | require 'rubygems' 5 | require 'rake' 6 | require 'echoe' 7 | 8 | Echoe.new "google-search", Google::Search::VERSION do |p| 9 | p.author = "TJ Holowaychuk" 10 | p.email = "tj@vision-media.ca" 11 | p.summary = "Google Search API" 12 | p.url = "http://github.com/visionmedia/google-search" 13 | p.runtime_dependencies << 'json' 14 | end 15 | 16 | Dir['tasks/**/*.rake'].sort.each { |f| load f } -------------------------------------------------------------------------------- /examples/image.rb: -------------------------------------------------------------------------------- 1 | 2 | $:.unshift File.dirname(__FILE__) + '/../lib' 3 | require 'rubygems' 4 | require 'google-search' 5 | require 'rext/all' 6 | 7 | HTML = File.dirname(__FILE__) + '/images.html' 8 | 9 | File.open(HTML, 'w+') do |file| 10 | Google::Search::Image.new(:query => 'Cookies', :image_size => :icon).each do |image| 11 | file.write %() 12 | end 13 | end 14 | 15 | begin 16 | `open -a Safari #{HTML}` 17 | rescue 18 | puts "Not on OSX? wtf? open #{HTML} in your browser to view the images" 19 | end 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/images.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/web.rb: -------------------------------------------------------------------------------- 1 | 2 | $:.unshift File.dirname(__FILE__) + '/../lib' 3 | require 'rubygems' 4 | require 'google-search' 5 | 6 | def find_item uri, query 7 | search = Google::Search::Web.new do |search| 8 | search.query = query 9 | search.size = :large 10 | search.each_response { print '.'; $stdout.flush } 11 | end 12 | search.find { |item| item.uri =~ uri } 13 | end 14 | 15 | def rank_for query 16 | print "%35s " % query 17 | if item = find_item(/vision\-media\.ca/, query) 18 | puts " #%d" % (item.index + 1) 19 | else 20 | puts " Not found" 21 | end 22 | end 23 | 24 | rank_for 'Victoria Web Training' 25 | rank_for 'Victoria Web School' 26 | rank_for 'Victoria Web Design' 27 | rank_for 'Victoria Drupal' 28 | rank_for 'Victoria Drupal Development' -------------------------------------------------------------------------------- /google-search.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.name = %q{google-search} 5 | s.version = "1.0.3" 6 | 7 | s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version= 8 | s.authors = ["TJ Holowaychuk"] 9 | s.date = %q{2012-08-29} 10 | s.description = %q{Google Search API} 11 | s.email = %q{tj@vision-media.ca} 12 | s.extra_rdoc_files = ["lib/google-search/item/base.rb", "lib/google-search/item/blog.rb", "lib/google-search/item/book.rb", "lib/google-search/item/image.rb", "lib/google-search/item/local.rb", "lib/google-search/item/news.rb", "lib/google-search/item/patent.rb", "lib/google-search/item/video.rb", "lib/google-search/item/web.rb", "lib/google-search/item.rb", "lib/google-search/response.rb", "lib/google-search/search/base.rb", "lib/google-search/search/blog.rb", "lib/google-search/search/book.rb", "lib/google-search/search/image.rb", "lib/google-search/search/local.rb", "lib/google-search/search/mixins/filter.rb", "lib/google-search/search/mixins/order_by.rb", "lib/google-search/search/mixins/safety_level.rb", "lib/google-search/search/mixins.rb", "lib/google-search/search/news.rb", "lib/google-search/search/patent.rb", "lib/google-search/search/video.rb", "lib/google-search/search/web.rb", "lib/google-search/search.rb", "lib/google-search/version.rb", "lib/google-search.rb", "README.rdoc", "tasks/docs.rake", "tasks/gemspec.rake", "tasks/spec.rake"] 13 | s.files = ["examples/image.rb", "examples/images.html", "examples/web.rb", "google-search.gemspec", "History.rdoc", "lib/google-search/item/base.rb", "lib/google-search/item/blog.rb", "lib/google-search/item/book.rb", "lib/google-search/item/image.rb", "lib/google-search/item/local.rb", "lib/google-search/item/news.rb", "lib/google-search/item/patent.rb", "lib/google-search/item/video.rb", "lib/google-search/item/web.rb", "lib/google-search/item.rb", "lib/google-search/response.rb", "lib/google-search/search/base.rb", "lib/google-search/search/blog.rb", "lib/google-search/search/book.rb", "lib/google-search/search/image.rb", "lib/google-search/search/local.rb", "lib/google-search/search/mixins/filter.rb", "lib/google-search/search/mixins/order_by.rb", "lib/google-search/search/mixins/safety_level.rb", "lib/google-search/search/mixins.rb", "lib/google-search/search/news.rb", "lib/google-search/search/patent.rb", "lib/google-search/search/video.rb", "lib/google-search/search/web.rb", "lib/google-search/search.rb", "lib/google-search/version.rb", "lib/google-search.rb", "Manifest", "Rakefile", "README.rdoc", "spec/fixtures/400-response.json", "spec/fixtures/blog-response.json", "spec/fixtures/book-response.json", "spec/fixtures/image-response.json", "spec/fixtures/invalid-response.json", "spec/fixtures/local-response.json", "spec/fixtures/news-response.json", "spec/fixtures/patent-response.json", "spec/fixtures/video-response.json", "spec/fixtures/web-2-response.json", "spec/fixtures/web-response.json", "spec/item_blog_spec.rb", "spec/item_book_spec.rb", "spec/item_image_spec.rb", "spec/item_local_spec.rb", "spec/item_news_spec.rb", "spec/item_patent_spec.rb", "spec/item_spec.rb", "spec/item_video_spec.rb", "spec/item_web_spec.rb", "spec/response_spec.rb", "spec/search_blog_spec.rb", "spec/search_book_spec.rb", "spec/search_image_spec.rb", "spec/search_local_spec.rb", "spec/search_news_spec.rb", "spec/search_patent_spec.rb", "spec/search_spec.rb", "spec/search_video_spec.rb", "spec/search_web_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/docs.rake", "tasks/gemspec.rake", "tasks/spec.rake"] 14 | s.homepage = %q{http://github.com/visionmedia/google-search} 15 | s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Google-search", "--main", "README.rdoc"] 16 | s.require_paths = ["lib"] 17 | s.rubyforge_project = %q{google-search} 18 | s.rubygems_version = %q{1.3.5} 19 | s.summary = %q{Google Search API} 20 | 21 | if s.respond_to? :specification_version then 22 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 23 | s.specification_version = 3 24 | 25 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then 26 | s.add_runtime_dependency(%q, [">= 0"]) 27 | else 28 | s.add_dependency(%q, [">= 0"]) 29 | end 30 | else 31 | s.add_dependency(%q, [">= 0"]) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/google-search.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2009 2009 TJ Holowaychuk 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining 5 | # a copy of this software and associated documentation files (the 6 | # "Software"), to deal in the Software without restriction, including 7 | # without limitation the rights to use, copy, modify, merge, publish, 8 | # distribute, sublicense, and/or sell copies of the Software, and to 9 | # permit persons to whom the Software is furnished to do so, subject to 10 | # the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be 13 | # included in all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | #++ 23 | 24 | require 'json' 25 | require 'open-uri' 26 | require 'google-search/version' 27 | require 'google-search/item' 28 | require 'google-search/response' 29 | require 'google-search/search' -------------------------------------------------------------------------------- /lib/google-search/item.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'google-search/item/base' 3 | require 'google-search/item/web' 4 | require 'google-search/item/blog' 5 | require 'google-search/item/book' 6 | require 'google-search/item/image' 7 | require 'google-search/item/local' 8 | require 'google-search/item/news' 9 | require 'google-search/item/video' 10 | require 'google-search/item/patent' 11 | -------------------------------------------------------------------------------- /lib/google-search/item/base.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | 6 | ## 7 | # Index. 8 | 9 | attr_reader :index 10 | 11 | ## 12 | # Unformatted page title. 13 | 14 | attr_reader :title 15 | 16 | ## 17 | # Absolute uri. 18 | 19 | attr_reader :uri 20 | 21 | ## 22 | # Visible uri. 23 | 24 | attr_reader :visible_uri 25 | 26 | ## 27 | # Thumbnail uri. 28 | 29 | attr_reader :thumbnail_uri 30 | 31 | ## 32 | # Thumbnail width in pixels. 33 | 34 | attr_reader :thumbnail_width 35 | 36 | ## 37 | # Thumbnail height in pixels. 38 | 39 | attr_reader :thumbnail_height 40 | 41 | ## 42 | # Contents. 43 | 44 | attr_reader :content 45 | 46 | ## 47 | # Initialize with _hash_. 48 | 49 | def initialize hash 50 | @index = hash['index'] 51 | @title = hash['titleNoFormatting'] || hash['title'] 52 | @uri = hash['unescapedUrl'] || hash['url'] || hash['postUrl'] 53 | @content = hash['contentNoFormatting'] || hash['content'] 54 | @thumbnail_uri = hash['tbUrl'] 55 | @thumbnail_width = hash['tbWidth'].to_i 56 | @thumbnail_height = hash['tbHeight'].to_i 57 | @visible_uri = hash['visibleUrl'] 58 | end 59 | 60 | ## 61 | # Return class for _google_class_ string formatted 62 | # as 'GwebSearch', 'GbookSearch', etc. 63 | 64 | def self.class_for google_class 65 | case google_class 66 | when 'GwebSearch' ; Web 67 | when 'GlocalSearch' ; Local 68 | when 'GbookSearch' ; Book 69 | when 'GimageSearch' ; Image 70 | when 'GvideoSearch' ; Video 71 | when 'GpatentSearch' ; Patent 72 | when 'GnewsSearch' ; News 73 | when 'GblogSearch' ; Blog 74 | end 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/google-search/item/blog.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | class Blog < self 6 | 7 | ## 8 | # Blog base uri. 9 | 10 | attr_reader :blog_uri 11 | 12 | ## 13 | # Author name. 14 | 15 | attr_reader :author 16 | 17 | ## 18 | # Published DateTime. 19 | 20 | attr_reader :published 21 | 22 | ## 23 | # Initialize with _hash_. 24 | 25 | def initialize hash 26 | super 27 | @author = hash['author'] 28 | @blog_uri = hash['blogUrl'] 29 | @published = DateTime.parse hash['publishedDate'] 30 | end 31 | end 32 | end 33 | end 34 | end -------------------------------------------------------------------------------- /lib/google-search/item/book.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | class Book < self 6 | 7 | ## 8 | # Book id. 9 | 10 | attr_reader :id 11 | 12 | ## 13 | # Author name(s). 14 | 15 | attr_reader :author 16 | 17 | ## 18 | # Published year. 19 | 20 | attr_reader :published_year 21 | 22 | ## 23 | # Page count. 24 | 25 | attr_reader :pages 26 | 27 | ## 28 | # Initialize with _hash_. 29 | 30 | def initialize hash 31 | super 32 | @author = hash['authors'] 33 | @published_year = hash['publishedYear'].to_i 34 | @id = hash['bookId'] 35 | @pages = hash['pageCount'].to_i 36 | end 37 | end 38 | end 39 | end 40 | end -------------------------------------------------------------------------------- /lib/google-search/item/image.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | class Image < self 6 | 7 | ## 8 | # Image id. 9 | 10 | attr_reader :id 11 | 12 | ## 13 | # Context uri. 14 | 15 | attr_reader :context_uri 16 | 17 | ## 18 | # Image width in pixels. 19 | 20 | attr_reader :width 21 | 22 | ## 23 | # Image height in pixels. 24 | 25 | attr_reader :height 26 | 27 | ## 28 | # Initialize with _hash_. 29 | 30 | def initialize hash 31 | super 32 | @id = hash['imageId'] 33 | @context_uri = hash['originalContextUrl'] 34 | @width = hash['width'].to_i 35 | @height = hash['height'].to_i 36 | end 37 | end 38 | end 39 | end 40 | end -------------------------------------------------------------------------------- /lib/google-search/item/local.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | class Local < self 6 | 7 | ## 8 | # Country. 9 | 10 | attr_reader :country 11 | 12 | ## 13 | # Region. 14 | 15 | attr_reader :region 16 | 17 | ## 18 | # City. 19 | 20 | attr_reader :city 21 | 22 | ## 23 | # Type. 24 | 25 | attr_reader :type 26 | 27 | ## 28 | # Accuracy. 29 | 30 | attr_reader :accuracy 31 | 32 | ## 33 | # Max age in seconds. 34 | 35 | attr_reader :max_age 36 | 37 | ## 38 | # Google maps directions uri. 39 | 40 | attr_reader :directions_uri 41 | 42 | ## 43 | # Google maps directions to here uri. 44 | 45 | attr_reader :directions_to_here_uri 46 | 47 | ## 48 | # Google maps directions from here uri. 49 | 50 | attr_reader :directions_from_here_uri 51 | 52 | ## 53 | # Longitude float. 54 | 55 | attr_reader :long 56 | 57 | ## 58 | # Latitude float. 59 | 60 | attr_reader :lat 61 | 62 | ## 63 | # Viewport mode. 64 | 65 | attr_reader :viewport_mode 66 | 67 | ## 68 | # Phone numbers array. 69 | 70 | attr_reader :phone_numbers 71 | 72 | ## 73 | # Street address. 74 | 75 | attr_reader :street_address 76 | 77 | ## 78 | # Address lines array. 79 | 80 | attr_reader :address_lines 81 | 82 | ## 83 | # Initialize with _hash_. 84 | 85 | def initialize hash 86 | super 87 | @country = hash['country'] 88 | @region = hash['region'] 89 | @city = hash['city'] 90 | @type = hash['listingType'] 91 | @accuracy = hash['accuracy'].to_i 92 | @max_age = hash['maxAge'] 93 | @directions_uri = hash['ddUrl'] 94 | @directions_to_here_uri = hash['ddUrlToHere'] 95 | @directions_from_here_uri = hash['ddUrlFromHere'] 96 | @thumbnail_uri = hash['staticMapUrl'] 97 | @long = hash['lng'].to_f 98 | @lat = hash['lat'].to_f 99 | @viewport_mode = hash['viewportmode'] 100 | @phone_numbers = hash['phoneNumbers'].map { |phone| phone['number'] } 101 | @street_address = hash['streetAddress'] 102 | @address_lines = hash['addressLines'] 103 | end 104 | end 105 | end 106 | end 107 | end -------------------------------------------------------------------------------- /lib/google-search/item/news.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | class News < self 6 | 7 | ## 8 | # Published DateTime. 9 | 10 | attr_reader :published 11 | 12 | ## 13 | # Publisher. 14 | 15 | attr_reader :publisher 16 | 17 | ## 18 | # Location. 19 | 20 | attr_reader :location 21 | 22 | ## 23 | # Language. 24 | 25 | attr_reader :language 26 | 27 | ## 28 | # Redirect uri. 29 | 30 | attr_reader :redirect_uri 31 | 32 | ## 33 | # Initialize with _hash_. 34 | 35 | def initialize hash 36 | super 37 | @location = hash['location'] 38 | @published = DateTime.parse hash['publishedDate'] 39 | @language = hash['language'] 40 | @publisher = hash['publisher'] 41 | @redirect_uri = hash['signedRedirectUrl'] 42 | end 43 | end 44 | end 45 | end 46 | end -------------------------------------------------------------------------------- /lib/google-search/item/patent.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | class Patent < self 6 | 7 | ## 8 | # Patent id. 9 | 10 | attr_reader :id 11 | 12 | ## 13 | # Patent status. 14 | 15 | attr_reader :status 16 | 17 | ## 18 | # Assignee. 19 | 20 | attr_reader :assignee 21 | 22 | ## 23 | # Application DateTime. 24 | 25 | attr_reader :application_date 26 | 27 | ## 28 | # Initialize with _hash_. 29 | 30 | def initialize hash 31 | super 32 | @id = hash['patentNumber'].to_i 33 | @application_date = DateTime.parse hash['applicationDate'] 34 | @assignee = hash['assignee'] 35 | @status = hash['patentStatus'] 36 | end 37 | end 38 | end 39 | end 40 | end -------------------------------------------------------------------------------- /lib/google-search/item/video.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | class Video < self 6 | 7 | ## 8 | # Rating float. 9 | 10 | attr_reader :rating 11 | 12 | ## 13 | # Video type. 14 | 15 | attr_reader :type 16 | 17 | ## 18 | # Publisher. 19 | 20 | attr_reader :publisher 21 | 22 | ## 23 | # Published DateTime. 24 | 25 | attr_reader :published 26 | 27 | ## 28 | # Duration in seconds. 29 | 30 | attr_reader :duration 31 | 32 | ## 33 | # Initialize with _hash_. 34 | 35 | def initialize hash 36 | super 37 | @rating = hash['rating'].to_f 38 | @type = hash['videoType'] 39 | @publisher = hash['publisher'] 40 | @published = DateTime.parse hash['published'] 41 | @duration = hash['duration'].to_i 42 | end 43 | end 44 | end 45 | end 46 | end -------------------------------------------------------------------------------- /lib/google-search/item/web.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Item 5 | class Web < self 6 | 7 | ## 8 | # Cached uri. 9 | 10 | attr_reader :cache_uri 11 | 12 | ## 13 | # Initialize with _hash_. 14 | 15 | def initialize hash 16 | super 17 | @cache_uri = hash['cacheUrl'] 18 | end 19 | end 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/google-search/response.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Response 5 | 6 | #-- 7 | # Mixins 8 | #++ 9 | 10 | include Enumerable 11 | 12 | ## 13 | # Response status code. 14 | 15 | attr_reader :status 16 | 17 | ## 18 | # Response details. 19 | 20 | attr_reader :details 21 | 22 | ## 23 | # Raw JSON string. 24 | 25 | attr_accessor :raw 26 | 27 | ## 28 | # Hash parsed from raw JSON string. 29 | 30 | attr_reader :hash 31 | 32 | ## 33 | # Items populated by the JSON hash. 34 | 35 | attr_reader :items 36 | 37 | ## 38 | # Estimated number of results. 39 | 40 | attr_reader :estimated_count 41 | 42 | ## 43 | # Current page index. 44 | 45 | attr_reader :page 46 | 47 | ## 48 | # Size of response. 49 | 50 | attr_reader :size 51 | 52 | ## 53 | # Initialize with _hash_. 54 | 55 | def initialize hash 56 | @page = 0 57 | @hash = hash 58 | @size = (hash['responseSize'] || :large).to_sym 59 | @items = [] 60 | @status = hash['responseStatus'] 61 | @details = hash['responseDetails'] 62 | if valid? 63 | if hash['responseData'].include? 'cursor' 64 | @estimated_count = hash['responseData']['cursor']['estimatedResultCount'].to_i 65 | @page = hash['responseData']['cursor']['currentPageIndex'].to_i 66 | end 67 | @hash['responseData']['results'].each_with_index do |result, i| 68 | item_class = Google::Search::Item.class_for result['GsearchResultClass'] 69 | result['index'] = i + Google::Search.size_for(size) * page 70 | items << item_class.new(result) 71 | end 72 | end 73 | end 74 | 75 | ## 76 | # Iterate each item with _block_. 77 | 78 | def each_item &block 79 | items.each { |item| yield item } 80 | end 81 | alias :each :each_item 82 | 83 | ## 84 | # Check if the response is valid. 85 | 86 | def valid? 87 | hash['responseStatus'] == 200 88 | end 89 | 90 | end 91 | end 92 | end -------------------------------------------------------------------------------- /lib/google-search/search.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'google-search/search/mixins' 3 | require 'google-search/search/base' 4 | require 'google-search/search/web' 5 | require 'google-search/search/blog' 6 | require 'google-search/search/image' 7 | require 'google-search/search/local' 8 | require 'google-search/search/news' 9 | require 'google-search/search/video' 10 | require 'google-search/search/book' 11 | require 'google-search/search/patent' 12 | -------------------------------------------------------------------------------- /lib/google-search/search/base.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | 5 | #-- 6 | # Mixins 7 | #++ 8 | 9 | include Enumerable 10 | 11 | #-- 12 | # Constants 13 | #++ 14 | 15 | URI = 'https://www.google.com/uds' 16 | 17 | #-- 18 | # Exceptions 19 | #++ 20 | 21 | class Error < StandardError; end 22 | 23 | ## 24 | # Version. Defaults to 1.0 25 | 26 | attr_accessor :version 27 | 28 | ## 29 | # Search type symbol. 30 | 31 | attr_accessor :type 32 | 33 | ## 34 | # Offset. Defaults to 0 35 | 36 | attr_accessor :offset 37 | 38 | ## 39 | # Language. Defaults to :en 40 | 41 | attr_accessor :language 42 | 43 | ## 44 | # Weither or not a search request has been sent. 45 | 46 | attr_accessor :sent 47 | 48 | ## 49 | # Query. Defaults to nil 50 | 51 | attr_accessor :query 52 | 53 | ## 54 | # API Key. Defaults to :notsupplied 55 | 56 | attr_accessor :api_key 57 | 58 | ## 59 | # Size. Defaults to :large 60 | # 61 | # - :small = 4 62 | # - :large = 8 63 | # 64 | 65 | attr_accessor :size 66 | 67 | ## 68 | # Additional options. All those listed above 69 | # are deleted. The remaining represent query 70 | # string key / value pairs. 71 | 72 | attr_reader :options 73 | 74 | ## 75 | # Initialize search _type_ with _options_. Optionally 76 | # a block may be passed, and the Search instance will 77 | # be yielded to it. 78 | 79 | def initialize options = {}, &block 80 | @type = self.class.to_s.split('::').last.downcase.to_sym 81 | @version = options.delete(:version) || 1.0 82 | @offset = options.delete(:offset) || 0 83 | @size = options.delete(:size) || :large 84 | @language = options.delete(:language) || :en 85 | @query = options.delete(:query) 86 | @api_key = options.delete(:api_key) || :notsupplied 87 | @options = options 88 | raise Error, 'Do not initialize Google::Search; Use a subclass such as Google::Search::Web' if @type == :search 89 | yield self if block 90 | end 91 | 92 | ## 93 | # Set a response _block_ which is called every time 94 | # #get_response is called. Useful for reporting etc. 95 | 96 | def each_response &block 97 | @each_response = block 98 | end 99 | 100 | ## 101 | # Iterate each item with _block_. 102 | 103 | def each_item &block 104 | response = self.next.response 105 | if response.valid? 106 | response.each { |item| yield item } 107 | each_item &block 108 | end 109 | end 110 | alias :each :each_item 111 | 112 | ## 113 | # Return all items. 114 | 115 | def all_items 116 | select { true } 117 | end 118 | alias :all :all_items 119 | 120 | ## 121 | # Return uri. 122 | 123 | def get_uri 124 | URI + "/G#{@type}Search?" + 125 | (get_uri_params + options.to_a). 126 | map { |key, value| "#{key}=#{Search.url_encode(value)}" unless value.nil? }.compact.join('&') 127 | end 128 | 129 | #:nodoc: 130 | 131 | def get_uri_params 132 | validate(:query) { |query| query.respond_to?(:to_str) && !query.to_str.empty? } 133 | validate(:version) { |version| Numeric === version } 134 | [[:start, offset], 135 | [:rsz, size], 136 | [:hl, language], 137 | [:key, api_key], 138 | [:v, version], 139 | [:q, query]] 140 | end 141 | 142 | ## 143 | # Prepare for next request. 144 | 145 | def next 146 | @offset += Search.size_for(size) if sent 147 | self 148 | end 149 | 150 | ## 151 | # Return raw JSON response string. 152 | 153 | def get_raw 154 | @sent = true 155 | open(get_uri).read 156 | end 157 | 158 | ## 159 | # Return hash parsed from the raw JSON response. 160 | 161 | def get_hash 162 | Search.json_decode get_raw 163 | end 164 | 165 | ## 166 | # Return Response object wrapping the JSON 167 | # response hash. 168 | 169 | def get_response 170 | raw = get_raw 171 | hash = Search.json_decode raw 172 | hash['responseSize'] = size 173 | response = Response.new hash 174 | response.raw = raw 175 | @each_response.call response if @each_response 176 | response 177 | end 178 | alias :response :get_response 179 | 180 | ## 181 | # Return int for size _sym_. 182 | 183 | def self.size_for sym 184 | { :small => 4, 185 | :large => 8 }[sym] 186 | end 187 | 188 | #:nodoc: 189 | 190 | def validate meth, &block 191 | value = send meth 192 | raise Error, "invalid #{type} #{meth} #{value.inspect}", caller unless yield value 193 | end 194 | 195 | ## 196 | # Decode JSON _string_. 197 | 198 | def self.json_decode string 199 | JSON.parse string 200 | end 201 | 202 | ## 203 | # Url encode _string_. 204 | 205 | def self.url_encode string 206 | string.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/) { 207 | '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase 208 | }.tr(' ', '+') 209 | end 210 | 211 | end 212 | end 213 | -------------------------------------------------------------------------------- /lib/google-search/search/blog.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Blog < self 5 | 6 | #-- 7 | # Mixins 8 | #++ 9 | 10 | include OrderBy 11 | 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/google-search/search/book.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Book < self 5 | # Nothing book specific 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /lib/google-search/search/image.rb: -------------------------------------------------------------------------------- 1 | module Google 2 | class Search 3 | class Image < self 4 | 5 | #-- 6 | # Mixins 7 | #++ 8 | 9 | include SafetyLevel 10 | 11 | #-- 12 | # Constants 13 | #++ 14 | 15 | SIZES = :icon, :small, :medium, :large, :xlarge, :xxlarge, :huge 16 | TYPES = :face, :photo, :clipart, :lineart 17 | EXTENSIONS = :jpg, :png, :gif, :bmp 18 | 19 | ## 20 | # Image size: 21 | # 22 | # - :icon 23 | # - :small 24 | # - :medium 25 | # - :large 26 | # - :xlarge 27 | # - :xxlarge 28 | # - :huge 29 | # 30 | 31 | attr_accessor :image_size 32 | 33 | ## 34 | # Image type: 35 | # 36 | # - :face 37 | # - :photo 38 | # - :clipart 39 | # - :lineart 40 | # 41 | 42 | attr_accessor :image_type 43 | 44 | ## 45 | # File type: 46 | # 47 | # - :jpg 48 | # - :gif 49 | # - :png 50 | # - :bmp 51 | # 52 | 53 | attr_accessor :file_type 54 | 55 | ## 56 | # Image color. 57 | 58 | attr_accessor :color 59 | 60 | ## 61 | # Specific uri to fetch images from. 62 | 63 | attr_accessor :uri 64 | 65 | #:nodoc: 66 | 67 | def initialize options = {}, &block 68 | @color = options.delete :color 69 | @image_size = options.delete :image_size 70 | @image_type = options.delete :image_type 71 | @file_type = options.delete :file_type 72 | @uri = options.delete :uri 73 | super 74 | end 75 | 76 | #:nodoc: 77 | 78 | def get_uri_params 79 | validate(:image_size) { |size| size.nil? || size.is_a?(Array) || SIZES.include?(size) } 80 | validate(:image_type) { |type| type.nil? || TYPES.include?(type) } 81 | validate(:file_type) { |ext| ext.nil? || EXTENSIONS.include?(ext) } 82 | super + [ 83 | [:safe, safety_level], 84 | [:imgsz, image_size.is_a?(Array) ? image_size.join('|') : image_size], 85 | [:imgcolor, color], 86 | [:imgtype, image_type], 87 | [:as_filetype, file_type], 88 | [:as_sitesearch, uri] 89 | ] 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/google-search/search/local.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Local < self 5 | # Nothing local specific 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /lib/google-search/search/mixins.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'google-search/search/mixins/order_by' 3 | require 'google-search/search/mixins/safety_level' 4 | require 'google-search/search/mixins/filter' 5 | -------------------------------------------------------------------------------- /lib/google-search/search/mixins/filter.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | module Filter 5 | 6 | ## 7 | # Weither or not to filter duplicate results. 8 | # Defaults to true. 9 | 10 | attr_accessor :filter 11 | 12 | #:nodoc: 13 | 14 | def initialize options = {}, &block 15 | @filter = options.delete(:filter) || 1 16 | super 17 | end 18 | 19 | #:nodoc: 20 | 21 | def get_uri_params 22 | super + [[:filter, filter ? 1 : 0]] 23 | end 24 | end 25 | end 26 | end -------------------------------------------------------------------------------- /lib/google-search/search/mixins/order_by.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | module OrderBy 5 | 6 | #-- 7 | # Constants 8 | #++ 9 | 10 | ORDER_BY = :relevance, :date 11 | 12 | ## 13 | # Order by. Defaults to :relevance 14 | # 15 | # - :relevance 16 | # - :date 17 | # 18 | 19 | attr_accessor :order_by 20 | 21 | #:nodoc: 22 | 23 | def initialize options = {}, &block 24 | @order_by = options.delete :order_by 25 | super 26 | end 27 | 28 | #:nodoc: 29 | 30 | def get_uri_params 31 | validate(:order_by) { |order| order.nil? || ORDER_BY.include?(order) } 32 | super + [[:scoring, order_by ? 'd' : nil]] 33 | end 34 | end 35 | end 36 | end -------------------------------------------------------------------------------- /lib/google-search/search/mixins/safety_level.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | module SafetyLevel 5 | 6 | #-- 7 | # Constants 8 | #++ 9 | 10 | SAFETY_LEVELS = :active, :moderate, :off 11 | 12 | ## 13 | # Safety level: 14 | # 15 | # - :active | :high 16 | # - :moderate | :medium 17 | # - :off 18 | # 19 | 20 | attr_accessor :safety_level 21 | 22 | #:nodoc: 23 | 24 | def initialize options = {}, &block 25 | @safety_level = options.delete :safety_level 26 | super 27 | end 28 | 29 | #:nodoc: 30 | 31 | def get_uri_params 32 | @safety_level = :off if @safety_level == :none 33 | @safety_level = :moderate if @safety_level == :medium 34 | @safety_level = :active if @safety_level == :high 35 | validate(:safety_level) { |level| level.nil? || SAFETY_LEVELS.include?(level) } 36 | super + [[:safe, safety_level]] 37 | end 38 | end 39 | end 40 | end -------------------------------------------------------------------------------- /lib/google-search/search/news.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class News < self 5 | 6 | #-- 7 | # Mixins 8 | #++ 9 | 10 | include OrderBy 11 | 12 | #-- 13 | # Constants 14 | #++ 15 | 16 | TOPICS = :headlines, :world, :business, :nation, :science, 17 | :elections, :politics, :entertainment, :sports, :health 18 | 19 | ## 20 | # Relative to city, state, province, zipcode, etc. 21 | 22 | attr_accessor :relative_to 23 | 24 | ## 25 | # Topic: 26 | # 27 | # - :headlines 28 | # - :world 29 | # - :business 30 | # - :nation 31 | # - :science 32 | # - :elections 33 | # - :politics 34 | # - :entertainment 35 | # - :sports 36 | # - :health 37 | # 38 | 39 | attr_accessor :topic 40 | 41 | ## 42 | # Edition, such as :us, :uk, :fr_ca, etc. 43 | 44 | attr_accessor :edition 45 | 46 | #:nodoc: 47 | 48 | def initialize options = {}, &block 49 | @relative_to = options.delete :relative_to 50 | @edition = options.delete :edition 51 | super 52 | end 53 | 54 | #:nodoc: 55 | 56 | def get_uri_params 57 | validate(:topic) { |topic| topic.nil? || TOPICS.include?(topic) } 58 | super + [ 59 | [:geo, relative_to], 60 | [:topic, topic], 61 | [:ned, edition] 62 | ] 63 | end 64 | end 65 | end 66 | end -------------------------------------------------------------------------------- /lib/google-search/search/patent.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Patent < self 5 | 6 | #-- 7 | # Mixins 8 | #++ 9 | 10 | include OrderBy 11 | 12 | ## 13 | # When nil all parents are returned. 14 | # When true only issued will be returned, 15 | # otherwise when false only filed but NOT 16 | # issued patents will be returned. 17 | 18 | attr_accessor :issued_only 19 | 20 | #:nodoc: 21 | 22 | def initialize options = {}, &block 23 | @issued_only = options.delete :issued_only 24 | super 25 | end 26 | 27 | #:nodoc: 28 | 29 | def get_uri_params 30 | super + [ 31 | [:as_psrg, issued_only ? 1 : nil], 32 | [:as_psra, issued_only === false ? 1 : nil] 33 | ] 34 | end 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /lib/google-search/search/video.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Video < self 5 | 6 | #-- 7 | # Mixins 8 | #++ 9 | 10 | include OrderBy 11 | include Filter 12 | 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/google-search/search/web.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | class Web < self 5 | 6 | #-- 7 | # Mixins 8 | #++ 9 | 10 | include Filter 11 | include SafetyLevel 12 | 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/google-search/version.rb: -------------------------------------------------------------------------------- 1 | 2 | module Google 3 | class Search 4 | VERSION = '1.0.2' 5 | end 6 | end -------------------------------------------------------------------------------- /spec/fixtures/400-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[],"cursor":{"moreResultsUrl":"http://www.google.com/search?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003den\u0026q"}}, "responseDetails": "invalid start range", "responseStatus": 400} -------------------------------------------------------------------------------- /spec/fixtures/blog-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GblogSearch","title":"\u003cb\u003eFoo\u003c/b\u003e (51), Amsterdam (4-12) shine for Guyana on opening day \u003cb\u003e...\u003c/b\u003e","titleNoFormatting":"Foo (51), Amsterdam (4-12) shine for Guyana on opening day ...","postUrl":"http://www.kaieteurnewsonline.com/2009/07/22/foo-51-amsterdam-4-12-shine-for-guyana-on-opening-day/","content":"Jonathon \u003cb\u003eFoo\u003c/b\u003e hit a confident 51 and Alex Amsterdam took 4 last session wickets to keep Guyana in the hunt for first innings points against defending champions Barbados at the Jamalco ground yesterday in their 4 ...","author":"KNews","blogUrl":"http://www.kaieteurnewsonline.com/","publishedDate":"Wed, 22 Jul 2009 02:16:11 -0700"},{"GsearchResultClass":"GblogSearch","title":"Web News Site » Blog Archive » AS220 Annual \u003cb\u003eFoo\u003c/b\u003e Fest","titleNoFormatting":"Web News Site » Blog Archive » AS220 Annual Foo Fest","postUrl":"http://webnewssite.com/2009/07/22/as220-annual-foo-fest/","content":"AS220 is holding their annual \u003cb\u003eFoo\u003c/b\u003e Fest in Providence, Rhode Island. It looks like a really interesting event, especially for kids. Check out the link for the complete schedule of artists, musicians, and other activities. ...","author":"Marc de Vinck","blogUrl":"http://webnewssite.com/","publishedDate":"Wed, 22 Jul 2009 03:00:00 -0700"},{"GsearchResultClass":"GblogSearch","title":"Dnx Rockabilly Zone: \u003cb\u003eFoo\u003c/b\u003e Fighters","titleNoFormatting":"Dnx Rockabilly Zone: Foo Fighters","postUrl":"http://danangfadian.blogspot.com/2009/07/foo-fighters.html","content":"Those tapes would become the foundation of the \u003cb\u003eFoo\u003c/b\u003e Fighters, the band he formed in 1995, after the death of Kurt Cobain. Like Nirvana, the \u003cb\u003eFoo\u003c/b\u003e Fighters melded loud, heavy guitars with pretty melodies and mixed punk sensibilities with a ...","author":"Danang Fadian","blogUrl":"http://danangfadian.blogspot.com/","publishedDate":"Tue, 21 Jul 2009 19:22:00 -0700"},{"GsearchResultClass":"GblogSearch","title":"Fruit Rojak \u0026amp; Tau \u003cb\u003eFoo\u003c/b\u003e Fah @ Damansara Jaya | MICHAEL YIP | 叶志坚","titleNoFormatting":"Fruit Rojak \u0026amp; Tau Foo Fah @ Damansara Jaya | MICHAEL YIP | 叶志坚","postUrl":"http://mikeyip.com/fruit-rojak-tau-foo-fah-damansara-jaya/","content":"Located on a vacant parking space just in front of the Atria main entrance (the entrance in front of KFC is actually the side entrance), it's flanked by the Tau \u003cb\u003eFoo\u003c/b\u003e Fah seller that sells piping hot tau \u003cb\u003efoo\u003c/b\u003e fah to anyone who visits the ...","author":"Michael Yip","blogUrl":"http://mikeyip.com/","publishedDate":"Tue, 21 Jul 2009 21:14:27 -0700"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"2231827","currentPageIndex":0,"moreResultsUrl":"http://blogsearch.google.com/blogsearch?oe\u003dutf8\u0026ie\u003dutf8\u0026safe\u003dactive\u0026source\u003duds\u0026start\u003d0\u0026hl\u003den\u0026q\u003dfoo"}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/book-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GbookSearch","unescapedUrl":"http://books.google.com/books?id\u003dvUoCAgAACAAJ\u0026dq\u003dfoo\u0026client\u003dinternal-uds\u0026source\u003duds","url":"http://books.google.com/books%3Fid%3DvUoCAgAACAAJ%26dq%3Dfoo%26client%3Dinternal-uds%26source%3duds","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters","titleNoFormatting":"Foo Fighters","authors":"\u003cb\u003eFoo\u003c/b\u003e Fighters (CRT), Hal Leonard Publishing Corporation","bookId":"ISBN1423404580","publishedYear":"2006","tbUrl":"http://bks6.books.google.com/books?id\u003dvUoCAgAACAAJ\u0026printsec\u003dfrontcover\u0026img\u003d1\u0026zoom\u003d5\u0026sig\u003dACfU3U1NHHhXuERH30Xfn0GC3A0BW5nMPg","tbHeight":"80","tbWidth":"60","pageCount":"69"},{"GsearchResultClass":"GbookSearch","unescapedUrl":"http://books.google.com/books?id\u003dYY_JyNscCYAC\u0026printsec\u003dfrontcover\u0026dq\u003dfoo\u0026client\u003dinternal-uds\u0026source\u003duds","url":"http://books.google.com/books%3Fid%3DYY_JyNscCYAC%26printsec%3Dfrontcover%26dq%3Dfoo%26client%3Dinternal-uds%26source%3duds","title":"Advanced topics in artificial intelligence: 12th Australian Joint Conference on Artificial Intelligence, AI'99, Sydney, Australia, December 6-10, 1999 : proceedings","titleNoFormatting":"Advanced topics in artificial intelligence: 12th Australian Joint Conference on Artificial Intelligence, AI'99, Sydney, Australia, December 6-10, 1999 : proceedings","authors":"Norman Y. \u003cb\u003eFoo\u003c/b\u003e","bookId":"ISBN3540668225","publishedYear":"1999","tbUrl":"http://bks7.books.google.com/books?id\u003dYY_JyNscCYAC\u0026printsec\u003dfrontcover\u0026img\u003d1\u0026zoom\u003d5\u0026sig\u003dACfU3U3iaZtJ35pPqFz4RUmmDBg2JSwwGA","tbHeight":"80","tbWidth":"60","pageCount":"500"},{"GsearchResultClass":"GbookSearch","unescapedUrl":"http://books.google.com/books?id\u003dj2QrFVEd2GUC\u0026printsec\u003dfrontcover\u0026dq\u003dfoo\u0026client\u003dinternal-uds\u0026source\u003duds","url":"http://books.google.com/books%3Fid%3Dj2QrFVEd2GUC%26printsec%3Dfrontcover%26dq%3Dfoo%26client%3Dinternal-uds%26source%3duds","title":"PRICAI '96: topics in artificial intelligence : 4th Pacific Rim International Conference on Artificial Intelligence, Cairns, Australia, August 26-30, 1996 : proceedings","titleNoFormatting":"PRICAI '96: topics in artificial intelligence : 4th Pacific Rim International Conference on Artificial Intelligence, Cairns, Australia, August 26-30, 1996 : proceedings","authors":"Norman Y. \u003cb\u003eFoo\u003c/b\u003e, Randy Goebel","bookId":"ISBN3540615326","publishedYear":"1996","tbUrl":"http://bks8.books.google.com/books?id\u003dj2QrFVEd2GUC\u0026printsec\u003dfrontcover\u0026img\u003d1\u0026zoom\u003d5\u0026sig\u003dACfU3U1-btY8opiKptNpoXupyChBvPeFDA","tbHeight":"80","tbWidth":"60","pageCount":"658"},{"GsearchResultClass":"GbookSearch","unescapedUrl":"http://books.google.com/books?id\u003db6wnl81RguwC\u0026printsec\u003dfrontcover\u0026dq\u003dfoo\u0026client\u003dinternal-uds\u0026source\u003duds","url":"http://books.google.com/books%3Fid%3Db6wnl81RguwC%26printsec%3Dfrontcover%26dq%3Dfoo%26client%3Dinternal-uds%26source%3duds","title":"Advanced topics in artificial intelligence: 12th Australian Joint Conference on Artificial Intelligence, AI'99, Sydney, Australia, December 6-10, 1999 : proceedings","titleNoFormatting":"Advanced topics in artificial intelligence: 12th Australian Joint Conference on Artificial Intelligence, AI'99, Sydney, Australia, December 6-10, 1999 : proceedings","authors":"Norman Y. \u003cb\u003eFoo\u003c/b\u003e","bookId":"ISBN3540668225","publishedYear":"1999","tbUrl":"http://bks9.books.google.com/books?id\u003db6wnl81RguwC\u0026printsec\u003dfrontcover\u0026img\u003d1\u0026zoom\u003d5\u0026sig\u003dACfU3U2h-nHauxIQbFENKR-njj5hRpe9lg","tbHeight":"80","tbWidth":"60","pageCount":"500"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"11242","currentPageIndex":0,"moreResultsUrl":"http://books.google.com/books?source\u003duds\u0026start\u003d0\u0026hl\u003den\u0026q\u003dfoo"}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/image-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GimageSearch","width":"883","height":"891","imageId":"IYlLzX-w4vX2AM","tbWidth":"145","tbHeight":"146","unescapedUrl":"http://tomdiaz.files.wordpress.com/2009/06/foo_fighters.jpg","url":"http://tomdiaz.files.wordpress.com/2009/06/foo_fighters.jpg","visibleUrl":"tomdiaz.wordpress.com","title":"\u003cb\u003efoo\u003c/b\u003e_fighters.jpg","titleNoFormatting":"foo_fighters.jpg","originalContextUrl":"http://tomdiaz.wordpress.com/2009/06/","content":"Not FBI Agents--\u003cb\u003eFoo\u003c/b\u003e Fighters","contentNoFormatting":"Not FBI Agents--Foo Fighters","tbUrl":"http://images.google.com/images?q\u003dtbn:IYlLzX-w4vX2AM:tomdiaz.files.wordpress.com/2009/06/foo_fighters.jpg"},{"GsearchResultClass":"GimageSearch","width":"720","height":"580","imageId":"iHzf4CaizHK8TM","tbWidth":"140","tbHeight":"113","unescapedUrl":"http://community.post-gazette.com/cfs-file.ashx/__key/CommunityServer.Components.UserFiles/00.00.00.48.45/FooFighters1A.jpg","url":"http://community.post-gazette.com/cfs-file.ashx/__key/CommunityServer.Components.UserFiles/00.00.00.48.45/FooFighters1A.jpg","visibleUrl":"community.post-gazette.com","title":"\u003cb\u003eFoo\u003c/b\u003eFighters1A.jpg","titleNoFormatting":"FooFighters1A.jpg","originalContextUrl":"http://community.post-gazette.com/blogs/popnoise/archive/2008/10/08/foo-fighters-to-mccain-don-t-use-my-hero.aspx","content":"\u003cb\u003e...\u003c/b\u003e \u003cb\u003eFoo\u003c/b\u003e Fighters publicist contacted \u003cb\u003e...\u003c/b\u003e","contentNoFormatting":"... Foo Fighters publicist contacted ...","tbUrl":"http://images.google.com/images?q\u003dtbn:iHzf4CaizHK8TM:community.post-gazette.com/cfs-file.ashx/__key/CommunityServer.Components.UserFiles/00.00.00.48.45/FooFighters1A.jpg"},{"GsearchResultClass":"GimageSearch","width":"360","height":"302","imageId":"-eufEAEMWQ8_-M","tbWidth":"121","tbHeight":"102","unescapedUrl":"http://www.gotdogsonline.com/chinese-foo-dog-pictures-breeders-puppies-rescue/pictures/chinese-foo-dog-0003.jpg","url":"http://www.gotdogsonline.com/chinese-foo-dog-pictures-breeders-puppies-rescue/pictures/chinese-foo-dog-0003.jpg","visibleUrl":"www.gotdogsonline.com","title":"chinese-\u003cb\u003efoo\u003c/b\u003e-dog-0003.jpg","titleNoFormatting":"chinese-foo-dog-0003.jpg","originalContextUrl":"http://www.gotdogsonline.com/pictures/gallery/chinese-foo-dogs/chinese-foo-dog-0003/","content":"Chinese \u003cb\u003eFoo\u003c/b\u003e Dogs","contentNoFormatting":"Chinese Foo Dogs","tbUrl":"http://images.google.com/images?q\u003dtbn:-eufEAEMWQ8_-M:www.gotdogsonline.com/chinese-foo-dog-pictures-breeders-puppies-rescue/pictures/chinese-foo-dog-0003.jpg"},{"GsearchResultClass":"GimageSearch","width":"300","height":"299","imageId":"ZFPGVM71yQ-k_M","tbWidth":"116","tbHeight":"116","unescapedUrl":"http://www.aeg-tv.com/assets/events/Foo%20Fighter%20Logo%20Small.jpg","url":"http://www.aeg-tv.com/assets/events/Foo%2520Fighter%2520Logo%2520Small.jpg","visibleUrl":"djocean.wordpress.com","title":"\u003cb\u003eFoo\u003c/b\u003e Fighter Logo Small.jpg","titleNoFormatting":"Foo Fighter Logo Small.jpg","originalContextUrl":"http://djocean.wordpress.com/2008/07/28/foo-fighters-to-release-dvd-featuring-led-zeppelin-jam/","content":"\u003cb\u003eFoo\u003c/b\u003e Fighters to Release DVD \u003cb\u003e...\u003c/b\u003e","contentNoFormatting":"Foo Fighters to Release DVD ...","tbUrl":"http://images.google.com/images?q\u003dtbn:ZFPGVM71yQ-k_M:www.aeg-tv.com/assets/events/Foo%2520Fighter%2520Logo%2520Small.jpg"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"5120000","currentPageIndex":0,"moreResultsUrl":"http://www.google.com/images?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003den\u0026q\u003dfoo"}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/invalid-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[],"cursor":{"moreResultsUrl":"http://www.google.com/search?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003den\u0026q"}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/local-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GlocalSearch","viewportmode":"computed","listingType":"local","lat":"37.795279","lng":"-122.407451","accuracy":"8","title":"\u003cb\u003eFoo\u003c/b\u003e Wah Cheung","titleNoFormatting":"Foo Wah Cheung","ddUrl":"http://www.google.com/maps?source\u003duds\u0026daddr\u003d852+Washington+St%2C+San+Francisco%2C+CA+%28Foo+Wah+Cheung%29+%4037.795279%2C-122.407451\u0026saddr","ddUrlToHere":"http://www.google.com/maps?source\u003duds\u0026daddr\u003d852+Washington+St%2C+San+Francisco%2C+CA+%28Foo+Wah+Cheung%29+%4037.795279%2C-122.407451\u0026iwstate1\u003ddir%3Ato","ddUrlFromHere":"http://www.google.com/maps?source\u003duds\u0026saddr\u003d852+Washington+St%2C+San+Francisco%2C+CA+%28Foo+Wah+Cheung%29+%4037.795279%2C-122.407451\u0026iwstate1\u003ddir%3Afrom","streetAddress":"852 Washington St","city":"San Francisco","region":"CA","country":"United States","staticMapUrl":"http://mt.google.com/mapdata?cc\u003dus\u0026tstyp\u003d5\u0026Point\u003db\u0026Point.latitude_e6\u003d37795279\u0026Point.longitude_e6\u003d-122407451\u0026Point.iconid\u003d15\u0026Point\u003de\u0026w\u003d150\u0026h\u003d100\u0026zl\u003d4","url":"http://www.google.com/local?source\u003duds\u0026q\u003dfoo\u0026sll\u003d37.795279%2C-122.407451\u0026latlng\u003d37795279%2C-122407451%2C1909516875098392575\u0026near\u003d37.795279%2C-122.407451","content":"","maxAge":604800,"phoneNumbers":[{"type":"","number":"(415) 391-4067"}],"addressLines":["852 Washington St","San Francisco, CA"]},{"GsearchResultClass":"GlocalSearch","viewportmode":"computed","listingType":"local","lat":"37.794836","lng":"-122.403527","accuracy":"8","title":"Timothy \u003cb\u003eFoo\u003c/b\u003e \u0026amp; Co","titleNoFormatting":"Timothy Foo \u0026 Co","ddUrl":"http://www.google.com/maps?source\u003duds\u0026daddr\u003d601+Montgomery+St%2C+San+Francisco%2C+CA+%28Timothy+Foo+%26+Co%29+%4037.794836%2C-122.403527\u0026saddr","ddUrlToHere":"http://www.google.com/maps?source\u003duds\u0026daddr\u003d601+Montgomery+St%2C+San+Francisco%2C+CA+%28Timothy+Foo+%26+Co%29+%4037.794836%2C-122.403527\u0026iwstate1\u003ddir%3Ato","ddUrlFromHere":"http://www.google.com/maps?source\u003duds\u0026saddr\u003d601+Montgomery+St%2C+San+Francisco%2C+CA+%28Timothy+Foo+%26+Co%29+%4037.794836%2C-122.403527\u0026iwstate1\u003ddir%3Afrom","streetAddress":"601 Montgomery St","city":"San Francisco","region":"CA","country":"United States","staticMapUrl":"http://mt.google.com/mapdata?cc\u003dus\u0026tstyp\u003d5\u0026Point\u003db\u0026Point.latitude_e6\u003d37794836\u0026Point.longitude_e6\u003d-122403527\u0026Point.iconid\u003d15\u0026Point\u003de\u0026w\u003d150\u0026h\u003d100\u0026zl\u003d4","url":"http://www.google.com/local?source\u003duds\u0026q\u003dfoo\u0026sll\u003d37.794836%2C-122.403527\u0026latlng\u003d37794836%2C-122403527%2C6926352342283831233\u0026near\u003d37.794836%2C-122.403527","content":"","maxAge":604800,"phoneNumbers":[{"type":"","number":"(415) 986-5215"}],"addressLines":["601 Montgomery St","San Francisco, CA"]},{"GsearchResultClass":"GlocalSearch","viewportmode":"computed","listingType":"local","lat":"37.90716","lng":"-122.305244","accuracy":"8","title":"Yuet \u003cb\u003eFoo\u003c/b\u003e Seafood Restaurant","titleNoFormatting":"Yuet Foo Seafood Restaurant","ddUrl":"http://www.google.com/maps?source\u003duds\u0026daddr\u003d10350+San+Pablo+Ave%2C+El+Cerrito%2C+CA+%28Yuet+Foo+Seafood+Restaurant%29+%4037.90716%2C-122.305244\u0026saddr","ddUrlToHere":"http://www.google.com/maps?source\u003duds\u0026daddr\u003d10350+San+Pablo+Ave%2C+El+Cerrito%2C+CA+%28Yuet+Foo+Seafood+Restaurant%29+%4037.90716%2C-122.305244\u0026iwstate1\u003ddir%3Ato","ddUrlFromHere":"http://www.google.com/maps?source\u003duds\u0026saddr\u003d10350+San+Pablo+Ave%2C+El+Cerrito%2C+CA+%28Yuet+Foo+Seafood+Restaurant%29+%4037.90716%2C-122.305244\u0026iwstate1\u003ddir%3Afrom","streetAddress":"10350 San Pablo Ave","city":"El Cerrito","region":"CA","country":"United States","staticMapUrl":"http://mt.google.com/mapdata?cc\u003dus\u0026tstyp\u003d5\u0026Point\u003db\u0026Point.latitude_e6\u003d37907160\u0026Point.longitude_e6\u003d-122305244\u0026Point.iconid\u003d15\u0026Point\u003de\u0026w\u003d150\u0026h\u003d100\u0026zl\u003d4","url":"http://www.google.com/local?source\u003duds\u0026q\u003dfoo\u0026sll\u003d37.90716%2C-122.305244\u0026latlng\u003d37907160%2C-122305244%2C4537340541452700142\u0026near\u003d37.90716%2C-122.305244","content":"","maxAge":604800,"phoneNumbers":[{"type":"","number":"(510) 528-6847"}],"addressLines":["10350 San Pablo Ave","El Cerrito, CA"]},{"GsearchResultClass":"GlocalSearch","viewportmode":"computed","listingType":"local","lat":"37.901572","lng":"-122.527484","accuracy":"8","title":"\u003cb\u003eFoo\u003c/b\u003e-\u003cb\u003eFoo\u003c/b\u003e Design","titleNoFormatting":"Foo-Foo Design","ddUrl":"http://www.google.com/maps?source\u003duds\u0026daddr\u003d250+Camino+Alto+%23+250%2C+Mill+Valley%2C+CA+%28Foo-Foo+Design%29+%4037.901572%2C-122.527484\u0026saddr","ddUrlToHere":"http://www.google.com/maps?source\u003duds\u0026daddr\u003d250+Camino+Alto+%23+250%2C+Mill+Valley%2C+CA+%28Foo-Foo+Design%29+%4037.901572%2C-122.527484\u0026iwstate1\u003ddir%3Ato","ddUrlFromHere":"http://www.google.com/maps?source\u003duds\u0026saddr\u003d250+Camino+Alto+%23+250%2C+Mill+Valley%2C+CA+%28Foo-Foo+Design%29+%4037.901572%2C-122.527484\u0026iwstate1\u003ddir%3Afrom","streetAddress":"250 Camino Alto # 250","city":"Mill Valley","region":"CA","country":"United States","staticMapUrl":"http://mt.google.com/mapdata?cc\u003dus\u0026tstyp\u003d5\u0026Point\u003db\u0026Point.latitude_e6\u003d37901572\u0026Point.longitude_e6\u003d-122527484\u0026Point.iconid\u003d15\u0026Point\u003de\u0026w\u003d150\u0026h\u003d100\u0026zl\u003d4","url":"http://www.google.com/local?source\u003duds\u0026q\u003dfoo\u0026sll\u003d37.901572%2C-122.527484\u0026latlng\u003d37901572%2C-122527484%2C5970323968344476572\u0026near\u003d37.901572%2C-122.527484","content":"","maxAge":604800,"phoneNumbers":[{"type":"","number":"(415) 888-8337"}],"addressLines":["250 Camino Alto # 250","Mill Valley, CA"]}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4}],"estimatedResultCount":"574","currentPageIndex":0,"moreResultsUrl":"http://www.google.com/local?oe\u003dutf8\u0026ie\u003dutf8\u0026num\u003d4\u0026mrt\u003dyp%2Cloc\u0026sll\u003d37.779160%2C-122.420090\u0026start\u003d0\u0026hl\u003den\u0026q\u003dfoo"},"viewport":{"center":{"lat":"37.78722","lng":"-122.41377"},"span":{"lat":"0.019343","lng":"0.015167"},"sw":{"lat":"37.77755","lng":"-122.42136"},"ne":{"lat":"37.79689","lng":"-122.40619"}}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/news-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GnewsSearch","clusterUrl":"","content":"After all, it was Susanna \u003cb\u003eFoo\u003c/b\u003e Chinese Cuisine, a major player in Philadelphia\u0026#39;s restaurant renaissance, an innovative dining palace where Chinese-French \u003cb\u003e...\u003c/b\u003e","unescapedUrl":"http://www.montgomerynews.com/articles/2009/07/22/entertainment/doc4a672746b0941650009917.txt","url":"http%3A%2F%2Fwww.montgomerynews.com%2Farticles%2F2009%2F07%2F22%2Fentertainment%2Fdoc4a672746b0941650009917.txt","title":"TICKET TO DINING: Susanna \u003cb\u003eFoo\u003c/b\u003e Gourmet Kitchen — elegant suburban \u003cb\u003e...\u003c/b\u003e","titleNoFormatting":"TICKET TO DINING: Susanna Foo Gourmet Kitchen — elegant suburban ...","location":"Fort Washington,PA,USA","publisher":"Montgomery Newspapers","publishedDate":"Wed, 22 Jul 2009 09:02:05 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-0-0\u0026fd\u003dS\u0026url\u003dhttp://www.montgomerynews.com/articles/2009/07/22/entertainment/doc4a672746b0941650009917.txt\u0026cid\u003d0\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNGsuTniL5DY24lNJ8qy204ZWjQoKA","language":"en"},{"GsearchResultClass":"GnewsSearch","clusterUrl":"http://news.google.com/news/story?ncl\u003ddwbAwUnuy6hZgRMiTb6kcoks-EKYM\u0026hl\u003den\u0026ned\u003dus","content":"After holing up in their native Denmark this spring to lay down new material, members Sune Rose Wagner and Sharin \u003cb\u003eFoo\u003c/b\u003e are now reflecting on the final \u003cb\u003e...\u003c/b\u003e","unescapedUrl":"http://www.billboard.com/bbcom/news/the-raveonettes-unveil-album-release-and-1003995751.story","url":"http%3A%2F%2Fwww.billboard.com%2Fbbcom%2Fnews%2Fthe-raveonettes-unveil-album-release-and-1003995751.story","title":"The Raveonettes Unveil Album Release And Tour Dates","titleNoFormatting":"The Raveonettes Unveil Album Release And Tour Dates","location":"New York,NY,USA","publisher":"Billboard","publishedDate":"Tue, 21 Jul 2009 09:29:52 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-1-0\u0026fd\u003dS\u0026url\u003dhttp://www.billboard.com/bbcom/news/the-raveonettes-unveil-album-release-and-1003995751.story\u0026cid\u003d1281523655\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNGP0OBMkBW5SOT9Q_ndo2Jfk2iOYQ","language":"en","image":{"url":"http://www.chartattack.com/files/imagecache/180x150_SC/chart_global/news/20050606-raveonettes.jpg","tbUrl":"http://nt0.ggpht.com/news/tbn/hOEaRUeyqeQJ","originalContextUrl":"http://www.chartattack.com/news/72405/the-raveonettes-are-out-of-control","publisher":"ChartAttack","tbWidth":80,"tbHeight":67},"relatedStories":[{"unescapedUrl":"http://consequenceofsound.net/2009/07/22/the-raveonettes-are-in-and-out-of-control-hit-the-road-to-figure-it-out/","url":"http%3A%2F%2Fconsequenceofsound.net%2F2009%2F07%2F22%2Fthe-raveonettes-are-in-and-out-of-control-hit-the-road-to-figure-it-out%2F","title":"The Raveonettes are In and Out of Control, hit the road to figure \u003cb\u003e...\u003c/b\u003e","titleNoFormatting":"The Raveonettes are In and Out of Control, hit the road to figure ...","location":"Manassas,VA,USA","publisher":"Consequence of Sound","publishedDate":"Wed, 22 Jul 2009 11:37:22 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-1-1\u0026fd\u003dS\u0026url\u003dhttp://consequenceofsound.net/2009/07/22/the-raveonettes-are-in-and-out-of-control-hit-the-road-to-figure-it-out/\u0026cid\u003d1281523655\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNGKYS-UQiltq5-JH8qOBl_ZQ6Cvaw","language":"en"},{"unescapedUrl":"http://www.clashmusic.com/news/raveonettes-album-details","url":"http%3A%2F%2Fwww.clashmusic.com%2Fnews%2Fraveonettes-album-details","title":"Raveonettes Album Details","titleNoFormatting":"Raveonettes Album Details","location":"London,London,UK","publisher":"ClashMusic.com","publishedDate":"Wed, 22 Jul 2009 08:01:50 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-1-2\u0026fd\u003dS\u0026url\u003dhttp://www.clashmusic.com/news/raveonettes-album-details\u0026cid\u003d1281523655\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNE0yNOkyeqoWNAw58Z4PwRjO56FhQ","language":"en"},{"unescapedUrl":"http://www.exclaim.ca/articles/generalarticlesynopsfullart.aspx?csid1\u003d134\u0026csid2\u003d844\u0026fid1\u003d40047","url":"http%3A%2F%2Fwww.exclaim.ca%2Farticles%2Fgeneralarticlesynopsfullart.aspx%3Fcsid1%3D134%26csid2%3D844%26fid1%3D40047","title":"Raveonettes Ready “the Ultimate Light Album,” Play Toronto \u003cb\u003e...\u003c/b\u003e","titleNoFormatting":"Raveonettes Ready “the Ultimate Light Album,” Play Toronto ...","location":"Toronto,Ontario,Canada","publisher":"Exclaim!","publishedDate":"Wed, 22 Jul 2009 14:01:54 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-1-3\u0026fd\u003dS\u0026url\u003dhttp://www.exclaim.ca/articles/generalarticlesynopsfullart.aspx%3Fcsid1%3D134%26csid2%3D844%26fid1%3D40047\u0026cid\u003d1281523655\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNH-tOgy53FR5RD-zmnbqJuPRgDIqQ","language":"en"},{"unescapedUrl":"http://www.spinner.com/2009/07/20/the-raveonettes-will-lighten-up-on-new-album-sort-of/","url":"http%3A%2F%2Fwww.spinner.com%2F2009%2F07%2F20%2Fthe-raveonettes-will-lighten-up-on-new-album-sort-of%2F","title":"The Raveonettes Will Lighten Up on New Album ... Sort Of","titleNoFormatting":"The Raveonettes Will Lighten Up on New Album ... Sort Of","location":"New York,NY,USA","publisher":"Spinner","publishedDate":"Mon, 20 Jul 2009 13:15:48 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-1-4\u0026fd\u003dS\u0026url\u003dhttp://www.spinner.com/2009/07/20/the-raveonettes-will-lighten-up-on-new-album-sort-of/\u0026cid\u003d1281523655\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNE8hqFExKou1mwaMJIoQjXFNXQVpw","language":"en"},{"unescapedUrl":"http://music.rightcelebrity.com/?p\u003d2781","url":"http%3A%2F%2Fmusic.rightcelebrity.com%2F%3Fp%3D2781","title":"Raveonettes Tour Dates: Raveonettes Announce New Album","titleNoFormatting":"Raveonettes Tour Dates: Raveonettes Announce New Album","location":"USA","publisher":"Right On Music","publishedDate":"Wed, 22 Jul 2009 10:24:29 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-1-5\u0026fd\u003dS\u0026url\u003dhttp://music.rightcelebrity.com/%3Fp%3D2781\u0026cid\u003d1281523655\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNEZCc_Q_Os4fkrfEPjO4_q2EAQRcA","language":"en"},{"unescapedUrl":"http://www.prefixmag.com/news/raveonettes-announce-tour-and-title-and-release-da/31033/","url":"http%3A%2F%2Fwww.prefixmag.com%2Fnews%2Fraveonettes-announce-tour-and-title-and-release-da%2F31033%2F","title":"Raveonettes Announce In \u0026amp; Out of Control, Tour Dates","titleNoFormatting":"Raveonettes Announce In \u0026amp; Out of Control, Tour Dates","location":"Brooklyn,NY,USA","publisher":"Prefixmag","publishedDate":"Tue, 21 Jul 2009 13:37:27 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-1-6\u0026fd\u003dS\u0026url\u003dhttp://www.prefixmag.com/news/raveonettes-announce-tour-and-title-and-release-da/31033/\u0026cid\u003d1281523655\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNFhxoOl0O5tTTyxNOKxoP9iGzXkrQ","language":"en"}]},{"GsearchResultClass":"GnewsSearch","clusterUrl":"http://news.google.com/news/story?ncl\u003dd2XBGfzTDmHIzvMtwfSQCiP0uifIM\u0026hl\u003den\u0026ned\u003dus","content":"\u003cb\u003eFoo\u003c/b\u003e Fighters have been on hiatus since their last tour wrapped, but they\u0026#39;ll play one show this weekend out of civic duty. Rolling Stone reports that the \u003cb\u003e...\u003c/b\u003e","unescapedUrl":"http://newsroom.mtv.com/2009/07/02/foo-fighters-white-house/","url":"http%3A%2F%2Fnewsroom.mtv.com%2F2009%2F07%2F02%2Ffoo-fighters-white-house%2F","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters Should Avoid Playing These Songs At The White House","titleNoFormatting":"Foo Fighters Should Avoid Playing These Songs At The White House","location":"USA","publisher":"MTV.com","publishedDate":"Thu, 02 Jul 2009 12:55:45 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-2-0\u0026fd\u003dS\u0026url\u003dhttp://newsroom.mtv.com/2009/07/02/foo-fighters-white-house/\u0026cid\u003d1271271606\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNGN9FGKFakqx9jyfqgWrf3-K5JMYw","language":"en","image":{"url":"http://akamai-static.nme.com/images/article/0925_172010_foofighterspaphotosL50209.jpg","tbUrl":"http://nt3.ggpht.com/news/tbn/92Q2RWIx1j4J","originalContextUrl":"http://www.nme.com/news/foo-fighters/45873","publisher":"NME.com","tbWidth":80,"tbHeight":49},"relatedStories":[{"unescapedUrl":"http://www.rollingstone.com/rockdaily/index.php/2009/07/02/foo-fighters-head-to-dc-to-rock-white-house-fourth-of-july-bash/","url":"http%3A%2F%2Fwww.rollingstone.com%2Frockdaily%2Findex.php%2F2009%2F07%2F02%2Ffoo-fighters-head-to-dc-to-rock-white-house-fourth-of-july-bash%2F","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters Head to DC to Rock White House Fourth of July Bash","titleNoFormatting":"Foo Fighters Head to DC to Rock White House Fourth of July Bash","location":"USA","publisher":"Rolling Stone","publishedDate":"Thu, 02 Jul 2009 05:15:17 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-2-1\u0026fd\u003dS\u0026url\u003dhttp://www.rollingstone.com/rockdaily/index.php/2009/07/02/foo-fighters-head-to-dc-to-rock-white-house-fourth-of-july-bash/\u0026cid\u003d1271271606\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNEXWRX1jpi8R9LFOkAfZ_rjdfApjw","language":"en"},{"unescapedUrl":"http://www.nme.com/news/foo-fighters/45873","url":"http%3A%2F%2Fwww.nme.com%2Fnews%2Ffoo-fighters%2F45873","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters debut new song \u0026#39;Wheels\u0026#39; for Barack Obama - video","titleNoFormatting":"Foo Fighters debut new song \u0026#39;Wheels\u0026#39; for Barack Obama - video","location":"UK","publisher":"NME.com","publishedDate":"Mon, 06 Jul 2009 10:40:05 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-2-2\u0026fd\u003dS\u0026url\u003dhttp://www.nme.com/news/foo-fighters/45873\u0026cid\u003d1271271606\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNHdxKm3ogufQMZmAnBDVp1QOEyKcg","language":"en"},{"unescapedUrl":"http://www.examiner.com/x-14721-Boston-Music-Examiner~y2009m7d4-Foo-Fighters-to-rock-the-White-House-4th-of-July-Celebration","url":"http%3A%2F%2Fwww.examiner.com%2Fx-14721-Boston-Music-Examiner%7Ey2009m7d4-Foo-Fighters-to-rock-the-White-House-4th-of-July-Celebration","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters to rock the White House 4th of July Celebration","titleNoFormatting":"Foo Fighters to rock the White House 4th of July Celebration","location":"USA","publisher":"Examiner.com","publishedDate":"Sat, 04 Jul 2009 07:03:43 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-2-3\u0026fd\u003dS\u0026url\u003dhttp://www.examiner.com/x-14721-Boston-Music-Examiner~y2009m7d4-Foo-Fighters-to-rock-the-White-House-4th-of-July-Celebration\u0026cid\u003d1271271606\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNHWyu9L0ofwoifhR0h6ZntpX3b3hg","language":"en"},{"unescapedUrl":"http://consequenceofsound.net/2009/07/06/watch-foo-fighters-wheels-new-song/","url":"http%3A%2F%2Fconsequenceofsound.net%2F2009%2F07%2F06%2Fwatch-foo-fighters-wheels-new-song%2F","title":"Watch: \u003cb\u003eFoo\u003c/b\u003e Fighters - “Wheels” (New Song)","titleNoFormatting":"Watch: Foo Fighters - “Wheels” (New Song)","location":"Manassas,VA,USA","publisher":"Consequence of Sound","publishedDate":"Mon, 06 Jul 2009 10:04:34 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-2-4\u0026fd\u003dS\u0026url\u003dhttp://consequenceofsound.net/2009/07/06/watch-foo-fighters-wheels-new-song/\u0026cid\u003d1271271606\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNEoW_3LrrZh-UgRwWU5tS6HYyGq9A","language":"en"},{"unescapedUrl":"http://www.filter-mag.com/index.php?id\u003d19307\u0026c\u003d1","url":"http%3A%2F%2Fwww.filter-mag.com%2Findex.php%3Fid%3D19307%26c%3D1","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters Debut New Material At The White House","titleNoFormatting":"Foo Fighters Debut New Material At The White House","location":"Los Angeles,CA,USA","publisher":"Filter Magazine","publishedDate":"Mon, 06 Jul 2009 11:10:13 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-2-5\u0026fd\u003dS\u0026url\u003dhttp://www.filter-mag.com/index.php%3Fid%3D19307%26c%3D1\u0026cid\u003d1271271606\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNGIb4EFWS7jPneMOMP7aS9yePcxaA","language":"en"},{"unescapedUrl":"http://newsroom.mtv.com/2009/07/06/foo-fighters-white-house-2/","url":"http%3A%2F%2Fnewsroom.mtv.com%2F2009%2F07%2F06%2Ffoo-fighters-white-house-2%2F","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters Debut New Song On White House Lawn","titleNoFormatting":"Foo Fighters Debut New Song On White House Lawn","location":"USA","publisher":"MTV.com","publishedDate":"Mon, 06 Jul 2009 07:42:56 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-2-6\u0026fd\u003dS\u0026url\u003dhttp://newsroom.mtv.com/2009/07/06/foo-fighters-white-house-2/\u0026cid\u003d1271271606\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNFkB54hu4JmcWmjRmo4BcTDCTfAAA","language":"en"}]},{"GsearchResultClass":"GnewsSearch","clusterUrl":"","content":"\u003cb\u003eFoo\u003c/b\u003e Fighters- On a hiatus but just played the White House on the 4th of July \u0026amp; Sunny Day tour with Nate doesn\u0026#39;t start til Sept. \u003cb\u003eFoo\u003c/b\u003e have a Greatest Hits \u003cb\u003e...\u003c/b\u003e","unescapedUrl":"http://www.signonsandiego.com/entertainment/street/2009/07/street_scene_nogo_beastie_boys.html","url":"http%3A%2F%2Fwww.signonsandiego.com%2Fentertainment%2Fstreet%2F2009%2F07%2Fstreet_scene_nogo_beastie_boys.html","title":"NO TOUR : Beastie Boys cancel gigs, including Street Scene","titleNoFormatting":"NO TOUR : Beastie Boys cancel gigs, including Street Scene","location":"San Diego,CA,USA","publisher":"San Diego Union Tribune","publishedDate":"Mon, 20 Jul 2009 10:45:46 -0700","signedRedirectUrl":"http://news.google.com/news/url?sa\u003dT\u0026ct\u003dus/0-3-0\u0026fd\u003dS\u0026url\u003dhttp://www.signonsandiego.com/entertainment/street/2009/07/street_scene_nogo_beastie_boys.html\u0026cid\u003d1281239545\u0026ei\u003d4aFnSpnxK474rQPutNHRAQ\u0026usg\u003dAFQjCNFyvSZc8q6a7VZlFBqDckNtVlVpdQ","language":"en"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"2071","currentPageIndex":0,"moreResultsUrl":"http://news.google.com/nwshp?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026q\u003dfoo\u0026hl\u003den\u0026start\u003d0"}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/patent-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GpatentSearch","unescapedUrl":"http://www.google.com/patents/about?id\u003dGDMdAAAAEBAJ\u0026dq\u003dfoo\u0026client\u003dinternal-uds\u0026source\u003duds","url":"http://www.google.com/patents/about%3Fid%3DGDMdAAAAEBAJ%26dq%3Dfoo%26client%3Dinternal-uds%26source%3duds","title":"SZE-\u003cb\u003eFOO\u003c/b\u003e CHIEN","titleNoFormatting":"SZE-FOO CHIEN","applicationDate":"Tue, 26 Mar 1968 00:00:00 -0800","patentNumber":"3468158","patentStatus":"issued","content":"\u003cb\u003e...\u003c/b\u003e 3468158 METHOD OF AND APPARATUS FOR DETERMINING RHEOLOGICAL PROPERTIES OF NON-NEWTONIAN FLUIDS SUCH AS DRILLING FLUIDS OR THE LIKE Filed March 26, r .£. \u003cb\u003e...\u003c/b\u003e","assignee":"","tbUrl":"http://bks9.books.google.com/patents?id\u003dGDMdAAAAEBAJ\u0026printsec\u003ddrawing\u0026img\u003d1\u0026zoom\u003d1\u0026sig\u003dACfU3U10b3w-4hMfKTEykPmtqnoObaLhaA"},{"GsearchResultClass":"GpatentSearch","unescapedUrl":"http://www.google.com/patents/about?id\u003d9YMBAAAAEBAJ\u0026dq\u003dfoo\u0026client\u003dinternal-uds\u0026source\u003duds","url":"http://www.google.com/patents/about%3Fid%3D9YMBAAAAEBAJ%26dq%3Dfoo%26client%3Dinternal-uds%26source%3duds","title":"Method for dynamically linking definable program elements of an interactive data processing system","titleNoFormatting":"Method for dynamically linking definable program elements of an interactive data processing system","applicationDate":"Thu, 28 May 1992 00:00:00 -0700","patentNumber":"5381547","patentStatus":"issued","content":"\u003cb\u003eFOO\u003c/b\u003e is provided in this exemplary embodiment, to which a link header l.FAULT is chained. \u003cb\u003e...\u003c/b\u003e 35 The input symbol \u0026quot;\u003cb\u003eFOO\u003c/b\u003e\u0026quot; is not yet defined at this tune. \u003cb\u003e...\u003c/b\u003e","assignee":"Siemens Aktiengesellschaft","tbUrl":"http://bks0.books.google.com/patents?id\u003d9YMBAAAAEBAJ\u0026printsec\u003ddrawing\u0026img\u003d1\u0026zoom\u003d1\u0026sig\u003dACfU3U1L7MxCO4Y8TeU4eV3PgABUfcPMLA"},{"GsearchResultClass":"GpatentSearch","unescapedUrl":"http://www.google.com/patents/about?id\u003dchwfAAAAEBAJ\u0026dq\u003dfoo\u0026client\u003dinternal-uds\u0026source\u003duds","url":"http://www.google.com/patents/about%3Fid%3DchwfAAAAEBAJ%26dq%3Dfoo%26client%3Dinternal-uds%26source%3duds","title":"Method for analyzing calls of application program by inserting monitoring routines into the executable version and redirecting calls to the monitoring routines","titleNoFormatting":"Method for analyzing calls of application program by inserting monitoring routines into the executable version and redirecting calls to the monitoring routines","applicationDate":"Tue, 18 Sep 1990 00:00:00 -0700","patentNumber":"5313616","patentStatus":"issued","content":"The -u option 40 routines can be tested by running \u003cb\u003efoo\u003c/b\u003e under another test causes the names of unexecuted user and system proce- harness as follows: \u003cb\u003e...\u003c/b\u003e","assignee":"88Open Consortium, Ltd.","tbUrl":"http://bks1.books.google.com/patents?id\u003dchwfAAAAEBAJ\u0026printsec\u003ddrawing\u0026img\u003d1\u0026zoom\u003d1\u0026sig\u003dACfU3U08WiFfgEBRGJy3374yvDbEyPZYdQ"},{"GsearchResultClass":"GpatentSearch","unescapedUrl":"http://www.google.com/patents/about?id\u003dDK4aAAAAEBAJ\u0026dq\u003dfoo\u0026client\u003dinternal-uds\u0026source\u003duds","url":"http://www.google.com/patents/about%3Fid%3DDK4aAAAAEBAJ%26dq%3Dfoo%26client%3Dinternal-uds%26source%3duds","title":"System and methods for optimizing object-oriented compilations","titleNoFormatting":"System and methods for optimizing object-oriented compilations","applicationDate":"Fri, 05 Jun 1992 00:00:00 -0700","patentNumber":"5481708","patentStatus":"issued","content":"\u003cb\u003e...\u003c/b\u003e constant \u003d 8 Cast to \u0026#39;\u003cb\u003efoo\u003c/b\u003e *\u0026#39; Expression at ccp.cpp(ll) \u003cb\u003e...\u003c/b\u003e [338f:0562] O_ID \u0026#39;func(\u003cb\u003efoo\u003c/b\u003e) \u0026#39; Cast to \u0026#39;long\u0026#39; Expression at ccp.cpp{5): \u003cb\u003e...\u003c/b\u003e","assignee":"Borland International, Inc.","tbUrl":"http://bks2.books.google.com/patents?id\u003dDK4aAAAAEBAJ\u0026printsec\u003ddrawing\u0026img\u003d1\u0026zoom\u003d1\u0026sig\u003dACfU3U06zm0mTVrpBKve5JQrUe0AlQuseA"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"1226","currentPageIndex":0,"moreResultsUrl":"http://www.google.com/patents?source\u003duds\u0026start\u003d0\u0026hl\u003den\u0026q\u003dfoo"}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/video-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GvideoSearch","title":"Foo Fighters - The Pretender","titleNoFormatting":"Foo Fighters - The Pretender","published":"Tue, 28 Aug 2007 16:36:23 PDT","content":"Foo Fighters The Pretender\n(C) 2007 Roswell Records, Inc.","publisher":"www.youtube.com","tbUrl":"http://0.gvt0.com/vi/TVboOdX9icA/default.jpg","tbWidth":"320","tbHeight":"240","videoType":"YouTube","url":"http://www.google.com/url?q\u003dhttp://www.youtube.com/watch%3Fv%3DTVboOdX9icA\u0026source\u003dvideo\u0026vgc\u003drss\u0026usg\u003dAFQjCNFvZftyyTO-IswoCPWEmmQbskBMRA","playUrl":"http://www.youtube.com/v/TVboOdX9icA\u0026fs\u003d1\u0026hl\u003den\u0026source\u003duds\u0026autoplay\u003d1","rating":"4.9076071","duration":"269"},{"GsearchResultClass":"GvideoSearch","title":"foo fighters-my hero","titleNoFormatting":"foo fighters-my hero","published":"Sat, 27 Jan 2007 06:18:10 PST","content":"one of foo fighters best songs - My Hero..definately one of my favourite songs.\nwell enjoy :D","publisher":"www.youtube.com","tbUrl":"http://2.gvt0.com/vi/KVKDQgT_b-Y/default.jpg","tbWidth":"320","tbHeight":"240","videoType":"YouTube","url":"http://www.google.com/url?q\u003dhttp://www.youtube.com/watch%3Fv%3DKVKDQgT_b-Y\u0026source\u003dvideo\u0026vgc\u003drss\u0026usg\u003dAFQjCNH9KepO3M5PVNSefDJylTfTGRB8uA","playUrl":"http://www.youtube.com/v/KVKDQgT_b-Y\u0026fs\u003d1\u0026hl\u003den\u0026source\u003duds\u0026autoplay\u003d1","rating":"4.8932624","duration":"247"},{"GsearchResultClass":"GvideoSearch","title":"Foo Fighters: DOA","titleNoFormatting":"Foo Fighters: DOA","published":"Sat, 25 Feb 2006 05:32:29 PST","content":"New Video Of Foo Fighters","publisher":"www.youtube.com","tbUrl":"http://1.gvt0.com/vi/O2_0Rx780Uk/default.jpg","tbWidth":"320","tbHeight":"240","videoType":"YouTube","url":"http://www.google.com/url?q\u003dhttp://www.youtube.com/watch%3Fv%3DO2_0Rx780Uk\u0026source\u003dvideo\u0026vgc\u003drss\u0026usg\u003dAFQjCNE8yH30j_kHOErl8CpozDRNDgEp5w","playUrl":"http://www.youtube.com/v/O2_0Rx780Uk\u0026fs\u003d1\u0026hl\u003den\u0026source\u003duds\u0026autoplay\u003d1","rating":"4.8958893","duration":"269"},{"GsearchResultClass":"GvideoSearch","title":"Foo Fighters Everywhere But Home Tour","titleNoFormatting":"Foo Fighters Everywhere But Home Tour","published":"Sun, 27 Aug 2006 10:46:57 PDT","content":"Foo Fighters Live In Concert\nDave Grohl","publisher":"video.google.com","tbUrl":"http://0.gvt0.com/ThumbnailServer2?app\u003dvss\u0026contentid\u003d6ea5cd582915b590\u0026offsetms\u003d355000\u0026itag\u003dw160\u0026hl\u003den\u0026sigh\u003dqz4THQ9cvyMcp56d6aiQfkG3lLI","tbWidth":"320","tbHeight":"240","videoType":"Google","url":"http://www.google.com/url?q\u003dhttp://video.google.com/videoplay%3Fdocid%3D2671671510864672199\u0026source\u003dvideo\u0026vgc\u003drss\u0026usg\u003dAFQjCNHcl0UGo5jSWXPwzjMgxTMVEfBdHw","playUrl":"http://video.google.com/s/MyL2xl0HXdE/googleplayer.swf?videoUrl\u003dhttp://v1.lscache2.googlevideo.com/videoplayback%3Fid%3D6ea5cd582915b590%26itag%3D5%26begin%3D0%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1248327186%26sparams%3Dip,ipbits,expire,id,itag%26signature%3D1E3D85F02EF1EEB2EF09512BD9441AFAAD979067.0431C0158A8EA1E0523BF2E0F92436398840B178%26key%3Dck1\u0026thumbnailUrl\u003dhttp://0.gvt0.com/ThumbnailServer2%3Fapp%3Dvss%26contentid%3D6ea5cd582915b590%26offsetms%3D355000%26itag%3Dw160%26hl%3Den%26sigh%3Dqz4THQ9cvyMcp56d6aiQfkG3lLI\u0026docid\u003d2671671510864672199\u0026hl\u003den\u0026q\u003dfoo+(site:video.google.com+OR+site:youtube.com)\u0026source\u003duds\u0026playerMode\u003dsimple\u0026autoPlay\u003dtrue","rating":"4.977778","duration":"5568"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"12851","currentPageIndex":0,"moreResultsUrl":"http://video.google.com/videosearch?source\u003duds\u0026type\u003dsearch\u0026q\u003dfoo+%28site%3Avideo.google.com+OR+site%3Ayoutube.com%29\u0026hl\u003den\u0026start\u003d0"}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/web-2-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://en.wikipedia.org/wiki/Foobar","url":"http://en.wikipedia.org/wiki/Foobar","visibleUrl":"en.wikipedia.org","cacheUrl":"http://www.google.com/search?q\u003dcache:4styY9qq7tYJ:en.wikipedia.org","title":"foobar - Wikipedia, the free encyclopedia","titleNoFormatting":"foobar - Wikipedia, the free encyclopedia","content":"Foobar is often used alone; \u003cb\u003efoo\u003c/b\u003e, bar, and baz are usually used in that order, \u003cb\u003e...\u003c/b\u003e \u003cb\u003eFoo\u003c/b\u003e has entered the English language as a neologism and is considered by \u003cb\u003e...\u003c/b\u003e"},{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://www.foo.com/","url":"http://www.foo.com/","visibleUrl":"www.foo.com","cacheUrl":"http://www.google.com/search?q\u003dcache:aJjqmBB29rgJ:www.foo.com","title":"Get the 411 on Anyone with Free White Pages, People Search and \u003cb\u003e...\u003c/b\u003e","titleNoFormatting":"Get the 411 on Anyone with Free White Pages, People Search and ...","content":"Get the free 411, white pages and people search with a new way to search for people."},{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://www.foofighters.com/","url":"http://www.foofighters.com/","visibleUrl":"www.foofighters.com","cacheUrl":"http://www.google.com/search?q\u003dcache:mH6Rsc-rjNgJ:www.foofighters.com","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters","titleNoFormatting":"Foo Fighters","content":"Jul 8, 2009 \u003cb\u003e...\u003c/b\u003e Official \u003cb\u003eFoo\u003c/b\u003e Fighters guitar, drum, and bass tabs."},{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://searchcio-midmarket.techtarget.com/sDefinition/0,,sid183_gci212139,00.html","url":"http://searchcio-midmarket.techtarget.com/sDefinition/0,,sid183_gci212139,00.html","visibleUrl":"searchcio-midmarket.techtarget.com","cacheUrl":"http://www.google.com/search?q\u003dcache:mdfUdBXGcm0J:searchcio-midmarket.techtarget.com","title":"What is \u003cb\u003efoo\u003c/b\u003e? - a definition from Whatis.com","titleNoFormatting":"What is foo? - a definition from Whatis.com","content":"Dec 14, 2006 \u003cb\u003e...\u003c/b\u003e Just as economists sometimes use the term \u0026#39;widget\u0026#39; as the ultimate substitute for \u0026#39;something\u0026#39; that is being measured, programmers tend to \u003cb\u003e...\u003c/b\u003e"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"33400000","currentPageIndex":1,"moreResultsUrl":"http://www.google.com/search?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003den\u0026q\u003dfoo"}}, "responseSize" : "small", "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/fixtures/web-response.json: -------------------------------------------------------------------------------- 1 | {"responseData": {"results":[{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://en.wikipedia.org/wiki/Foobar","url":"http://en.wikipedia.org/wiki/Foobar","visibleUrl":"en.wikipedia.org","cacheUrl":"http://www.google.com/search?q\u003dcache:4styY9qq7tYJ:en.wikipedia.org","title":"foobar - Wikipedia, the free encyclopedia","titleNoFormatting":"foobar - Wikipedia, the free encyclopedia","content":"Foobar is often used alone; \u003cb\u003efoo\u003c/b\u003e, bar, and baz are usually used in that order, \u003cb\u003e...\u003c/b\u003e \u003cb\u003eFoo\u003c/b\u003e has entered the English language as a neologism and is considered by \u003cb\u003e...\u003c/b\u003e"},{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://www.foo.com/","url":"http://www.foo.com/","visibleUrl":"www.foo.com","cacheUrl":"http://www.google.com/search?q\u003dcache:aJjqmBB29rgJ:www.foo.com","title":"Get the 411 on Anyone with Free White Pages, People Search and \u003cb\u003e...\u003c/b\u003e","titleNoFormatting":"Get the 411 on Anyone with Free White Pages, People Search and ...","content":"Get the free 411, white pages and people search with a new way to search for people."},{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://www.foofighters.com/","url":"http://www.foofighters.com/","visibleUrl":"www.foofighters.com","cacheUrl":"http://www.google.com/search?q\u003dcache:mH6Rsc-rjNgJ:www.foofighters.com","title":"\u003cb\u003eFoo\u003c/b\u003e Fighters","titleNoFormatting":"Foo Fighters","content":"Jul 8, 2009 \u003cb\u003e...\u003c/b\u003e Official \u003cb\u003eFoo\u003c/b\u003e Fighters guitar, drum, and bass tabs."},{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://searchcio-midmarket.techtarget.com/sDefinition/0,,sid183_gci212139,00.html","url":"http://searchcio-midmarket.techtarget.com/sDefinition/0,,sid183_gci212139,00.html","visibleUrl":"searchcio-midmarket.techtarget.com","cacheUrl":"http://www.google.com/search?q\u003dcache:mdfUdBXGcm0J:searchcio-midmarket.techtarget.com","title":"What is \u003cb\u003efoo\u003c/b\u003e? - a definition from Whatis.com","titleNoFormatting":"What is foo? - a definition from Whatis.com","content":"Dec 14, 2006 \u003cb\u003e...\u003c/b\u003e Just as economists sometimes use the term \u0026#39;widget\u0026#39; as the ultimate substitute for \u0026#39;something\u0026#39; that is being measured, programmers tend to \u003cb\u003e...\u003c/b\u003e"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"33400000","currentPageIndex":0,"moreResultsUrl":"http://www.google.com/search?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003den\u0026q\u003dfoo"}}, "responseDetails": null, "responseStatus": 200} -------------------------------------------------------------------------------- /spec/item_blog_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item::Blog do 5 | describe "#initialize" do 6 | it "should populate attributes" do 7 | hash = json_fixture('blog-response')['responseData']['results'].first 8 | item = Google::Search::Item::Blog.new hash 9 | item.title.should include('Foo (51)') 10 | item.author.should == 'KNews' 11 | item.uri.should == 'http://www.kaieteurnewsonline.com/2009/07/22/foo-51-amsterdam-4-12-shine-for-guyana-on-opening-day/' 12 | item.blog_uri.should == 'http://www.kaieteurnewsonline.com/' 13 | item.content.should include('Jonathon') 14 | item.published.should be_a(DateTime) 15 | end 16 | end 17 | end 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /spec/item_book_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item::Book do 5 | describe "#initialize" do 6 | it "should populate attributes" do 7 | hash = json_fixture('book-response')['responseData']['results'].first 8 | item = Google::Search::Item::Book.new hash 9 | item.title.should include('Foo Fighters') 10 | item.author.should == 'Foo Fighters (CRT), Hal Leonard Publishing Corporation' 11 | item.id.should == 'ISBN1423404580' 12 | item.uri.should == 'http://books.google.com/books?id=vUoCAgAACAAJ&dq=foo&client=internal-uds&source=uds' 13 | item.thumbnail_uri.should == 'http://bks6.books.google.com/books?id=vUoCAgAACAAJ&printsec=frontcover&img=1&zoom=5&sig=ACfU3U1NHHhXuERH30Xfn0GC3A0BW5nMPg' 14 | item.published_year.should == 2006 15 | item.pages.should == 69 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /spec/item_image_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item::Image do 5 | describe "#initialize" do 6 | it "should populate attributes" do 7 | hash = json_fixture('image-response')['responseData']['results'].first 8 | item = Google::Search::Item::Image.new hash 9 | item.id.should == 'IYlLzX-w4vX2AM' 10 | item.title.should == 'foo_fighters.jpg' 11 | item.uri.should == 'http://tomdiaz.files.wordpress.com/2009/06/foo_fighters.jpg' 12 | item.content.should include('Not FBI Agents') 13 | item.context_uri.should == 'http://tomdiaz.wordpress.com/2009/06/' 14 | item.width.should == 883 15 | item.height.should == 891 16 | item.thumbnail_uri.should == 'http://images.google.com/images?q=tbn:IYlLzX-w4vX2AM:tomdiaz.files.wordpress.com/2009/06/foo_fighters.jpg' 17 | item.thumbnail_width.should == 145 18 | item.thumbnail_height.should == 146 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/item_local_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item::Local do 5 | describe "#initialize" do 6 | it "should populate attributes" do 7 | hash = json_fixture('local-response')['responseData']['results'].first 8 | item = Google::Search::Item::Local.new hash 9 | item.city.should == 'San Francisco' 10 | item.type.should == 'local' 11 | item.region.should == 'CA' 12 | item.uri.should == 'http://www.google.com/local?source=uds&q=foo&sll=37.795279%2C-122.407451&latlng=37795279%2C-122407451%2C1909516875098392575&near=37.795279%2C-122.407451' 13 | item.directions_from_here_uri.should == 'http://www.google.com/maps?source=uds&saddr=852+Washington+St%2C+San+Francisco%2C+CA+%28Foo+Wah+Cheung%29+%4037.795279%2C-122.407451&iwstate1=dir%3Afrom' 14 | item.directions_to_here_uri.should == 'http://www.google.com/maps?source=uds&daddr=852+Washington+St%2C+San+Francisco%2C+CA+%28Foo+Wah+Cheung%29+%4037.795279%2C-122.407451&iwstate1=dir%3Ato' 15 | item.directions_uri.should == 'http://www.google.com/maps?source=uds&daddr=852+Washington+St%2C+San+Francisco%2C+CA+%28Foo+Wah+Cheung%29+%4037.795279%2C-122.407451&saddr' 16 | item.title.should == 'Foo Wah Cheung' 17 | item.accuracy.should == 8 18 | item.country.should == 'United States' 19 | item.max_age.should == 604800 20 | item.thumbnail_uri.should == 'http://mt.google.com/mapdata?cc=us&tstyp=5&Point=b&Point.latitude_e6=37795279&Point.longitude_e6=-122407451&Point.iconid=15&Point=e&w=150&h=100&zl=4' 21 | item.long.should == -122.407451 22 | item.street_address.should == '852 Washington St' 23 | item.content.should == '' 24 | item.lat.should == 37.795279 25 | item.viewport_mode.should == 'computed' 26 | item.phone_numbers.should == ['(415) 391-4067'] 27 | item.address_lines.should == ['852 Washington St', 'San Francisco, CA'] 28 | end 29 | end 30 | end -------------------------------------------------------------------------------- /spec/item_news_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item::News do 5 | describe "#initialize" do 6 | it "should populate attributes" do 7 | hash = json_fixture('news-response')['responseData']['results'].first 8 | item = Google::Search::Item::News.new hash 9 | item.title.should include('TICKET TO DINING') 10 | item.language.should == 'en' 11 | item.uri.should == 'http://www.montgomerynews.com/articles/2009/07/22/entertainment/doc4a672746b0941650009917.txt' 12 | item.published.should be_a(DateTime) 13 | item.publisher.should == 'Montgomery Newspapers' 14 | item.content.should include('After all, it was') 15 | item.redirect_uri.should == 'http://news.google.com/news/url?sa=T&ct=us/0-0-0&fd=S&url=http://www.montgomerynews.com/articles/2009/07/22/entertainment/doc4a672746b0941650009917.txt&cid=0&ei=4aFnSpnxK474rQPutNHRAQ&usg=AFQjCNGsuTniL5DY24lNJ8qy204ZWjQoKA' 16 | item.location.should == 'Fort Washington,PA,USA' 17 | end 18 | end 19 | end -------------------------------------------------------------------------------- /spec/item_patent_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item::Patent do 5 | describe "#initialize" do 6 | it "should populate attributes" do 7 | hash = json_fixture('patent-response')['responseData']['results'].first 8 | item = Google::Search::Item::Patent.new hash 9 | item.title.should == 'SZE-FOO CHIEN' 10 | item.id.should == 3468158 11 | item.content.should include('METHOD OF AND APPARATUS FOR DETERMINING RH') 12 | item.assignee.should == '' 13 | item.application_date.should be_a(DateTime) 14 | item.uri.should == 'http://www.google.com/patents/about?id=GDMdAAAAEBAJ&dq=foo&client=internal-uds&source=uds' 15 | item.thumbnail_uri.should == 'http://bks9.books.google.com/patents?id=GDMdAAAAEBAJ&printsec=drawing&img=1&zoom=1&sig=ACfU3U10b3w-4hMfKTEykPmtqnoObaLhaA' 16 | item.status.should == 'issued' 17 | end 18 | end 19 | end -------------------------------------------------------------------------------- /spec/item_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item do 5 | describe ".class_for" do 6 | it "should return a constant" do 7 | Google::Search::Item.class_for('GwebSearch').should == Google::Search::Item::Web 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /spec/item_video_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item::Video do 5 | describe "#initialize" do 6 | it "should populate attributes" do 7 | hash = json_fixture('video-response')['responseData']['results'].first 8 | item = Google::Search::Item::Video.new hash 9 | item.title.should include('Foo Fighters') 10 | item.content.should include('Foo Fighters') 11 | item.uri.should == 'http://www.google.com/url?q=http://www.youtube.com/watch%3Fv%3DTVboOdX9icA&source=video&vgc=rss&usg=AFQjCNFvZftyyTO-IswoCPWEmmQbskBMRA' 12 | item.rating.should == 4.9076071 13 | item.type.should == 'YouTube' 14 | item.published.should be_a(DateTime) 15 | item.thumbnail_uri.should == 'http://0.gvt0.com/vi/TVboOdX9icA/default.jpg' 16 | item.publisher.should == 'www.youtube.com' 17 | item.duration.should == 269 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /spec/item_web_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Item::Web do 5 | describe "#initialize" do 6 | it "should populate attributes" do 7 | hash = json_fixture('web-response')['responseData']['results'].first 8 | hash['index'] = 0 9 | item = Google::Search::Item::Web.new hash 10 | item.index.should == 0 11 | item.title.should include('foobar - Wikipedia') 12 | item.content.should include('Foobar is often used') 13 | item.uri.should == 'http://en.wikipedia.org/wiki/Foobar' 14 | item.cache_uri.should == 'http://www.google.com/search?q=cache:4styY9qq7tYJ:en.wikipedia.org' 15 | item.visible_uri.should == 'en.wikipedia.org' 16 | end 17 | end 18 | end -------------------------------------------------------------------------------- /spec/response_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Response do 5 | before :each do 6 | @response = Google::Search::Response.new json_fixture('web-response') 7 | end 8 | 9 | describe "#initialize" do 10 | it "should not throw an error when invalid" do 11 | lambda { Google::Search::Response.new json_fixture('invalid-response') }.should_not raise_error 12 | end 13 | 14 | it "should set #items" do 15 | @response.items.first.should be_a(Google::Search::Item) 16 | @response.items.length.should == 4 17 | end 18 | 19 | it "should set item indices" do 20 | @response.items[0].index.should == 0 21 | @response.items[1].index.should == 1 22 | @response.items[2].index.should == 2 23 | end 24 | 25 | it "should set item indices when page is present" do 26 | @response = Google::Search::Response.new json_fixture('web-2-response') 27 | @response.size.should == :small 28 | @response.page.should == 1 29 | @response.items[0].index.should == 4 30 | @response.items[1].index.should == 5 31 | @response.items[2].index.should == 6 32 | end 33 | 34 | it "should set #estimated_count" do 35 | @response.estimated_count.should == 33400000 36 | end 37 | 38 | it "should set #page" do 39 | @response.page.should == 0 40 | end 41 | 42 | it "should set #status" do 43 | @response.status.should == 200 44 | end 45 | 46 | it "should set #details" do 47 | @response.details.should be_nil 48 | end 49 | end 50 | 51 | describe "#valid?" do 52 | it "should return false when response status is not 200" do 53 | response = Google::Search::Response.new 'responseStatus' => 400 54 | response.should_not be_valid 55 | end 56 | 57 | it "should return true when status is 200" do 58 | response = Google::Search::Response.new 'responseStatus' => 200, 'responseData' => { 'results' => [] } 59 | response.should be_valid 60 | end 61 | end 62 | 63 | end -------------------------------------------------------------------------------- /spec/search_blog_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visionmedia/google-search/47982299426aabe52b2a9d909b3ae8d7c2eaa8ef/spec/search_blog_spec.rb -------------------------------------------------------------------------------- /spec/search_book_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visionmedia/google-search/47982299426aabe52b2a9d909b3ae8d7c2eaa8ef/spec/search_book_spec.rb -------------------------------------------------------------------------------- /spec/search_image_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Image do 5 | before :each do 6 | @search = Google::Search::Image.new :query => 'foo' 7 | end 8 | 9 | describe "#get_uri" do 10 | describe "safety_level" do 11 | it "should validate" do 12 | @search.safety_level = :active 13 | @search.get_uri.should include('safe=active') 14 | @search.safety_level = :foo 15 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /safety_level/) 16 | end 17 | 18 | it "should provide alternative naming :none, :medium, :high" do 19 | @search.safety_level = :none 20 | @search.get_uri.should include('safe=off') 21 | @search.safety_level = :medium 22 | @search.get_uri.should include('safe=moderate') 23 | @search.safety_level = :high 24 | @search.get_uri.should include('safe=active') 25 | end 26 | end 27 | 28 | describe "image_size" do 29 | it "should validate" do 30 | @search.image_size = :icon 31 | @search.get_uri.should include('imgsz=icon') 32 | @search.image_size = :small, :medium, :large 33 | @search.get_uri.should include('imgsz=small%7Cmedium%7Clarge') 34 | @search.image_size = :foo 35 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /image_size/) 36 | end 37 | end 38 | 39 | describe "color" do 40 | it "should validate" do 41 | @search.color = 'blue' 42 | @search.get_uri.should include('imgcolor=blue') 43 | end 44 | end 45 | 46 | describe "image_type" do 47 | it "should validate" do 48 | @search.image_type = :lineart 49 | @search.get_uri.should include('imgtype=lineart') 50 | @search.image_type = :foo 51 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /image_type/) 52 | end 53 | end 54 | 55 | describe "file_type" do 56 | it "should validate" do 57 | @search.file_type = :jpg 58 | @search.get_uri.should include('as_filetype=jpg') 59 | @search.file_type = :foo 60 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /file_type/) 61 | end 62 | end 63 | 64 | describe "uri" do 65 | it "should validate" do 66 | @search.uri = 'http://foo.com' 67 | @search.get_uri.should include('as_sitesearch=http%3A%2F%2Ffoo.com') 68 | end 69 | end 70 | end 71 | end -------------------------------------------------------------------------------- /spec/search_local_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visionmedia/google-search/47982299426aabe52b2a9d909b3ae8d7c2eaa8ef/spec/search_local_spec.rb -------------------------------------------------------------------------------- /spec/search_news_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::News do 5 | before :each do 6 | @search = Google::Search::News.new :query => 'foo' 7 | end 8 | 9 | describe "#get_uri" do 10 | describe "relative_to" do 11 | it "should set relative to geo" do 12 | @search.relative_to = 'Edmonton Alberta' 13 | @search.get_uri.should_not include('geo=Edmonton%20Alberta') 14 | end 15 | end 16 | 17 | describe "topic" do 18 | it "should validate" do 19 | @search.topic = :world 20 | @search.get_uri.should include('topic=world') 21 | @search.topic = :foo 22 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /topic/) 23 | end 24 | end 25 | 26 | describe "edition" do 27 | it "should set edition" do 28 | @search.edition = :en 29 | @search.get_uri.should include('ned=en') 30 | end 31 | end 32 | end 33 | end -------------------------------------------------------------------------------- /spec/search_patent_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Patent do 5 | before :each do 6 | @search = Google::Search::Patent.new :query => 'foo' 7 | end 8 | 9 | describe "#get_uri" do 10 | describe "issued_only" do 11 | it "should return issued and filed by default" do 12 | @search.get_uri.should_not include('as_psrg') 13 | @search.get_uri.should_not include('as_psra') 14 | end 15 | 16 | it "should return issued when true" do 17 | @search.issued_only = true 18 | @search.get_uri.should include('as_psrg=1') 19 | end 20 | 21 | it "should return filed when false" do 22 | @search.issued_only = false 23 | @search.get_uri.should include('as_psra=1') 24 | end 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /spec/search_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search do 5 | before :each do 6 | @search = Google::Search::Web.new :query => 'foo' 7 | end 8 | 9 | it "should throw an error when trying to initialize the base class" do 10 | lambda { Google::Search.new :query => 'foo' }.should raise_error(Google::Search::Error) 11 | end 12 | 13 | it "should be enumerable" do 14 | @search.to_a.first.should be_a(Google::Search::Item) 15 | end 16 | 17 | describe "#get_uri" do 18 | it "should return a uri" do 19 | @search.get_uri.should == 'http://www.google.com/uds/GwebSearch?start=0&rsz=large&hl=en&key=notsupplied&v=1.0&q=foo&filter=1' 20 | end 21 | 22 | it "should allow arbitrary key/value pairs" do 23 | search = Google::Search::Web.new :query => 'foo', :foo => 'bar' 24 | search.get_uri.should == 'http://www.google.com/uds/GwebSearch?start=0&rsz=large&hl=en&key=notsupplied&v=1.0&q=foo&filter=1&foo=bar' 25 | end 26 | 27 | describe "query" do 28 | it "should raise an error when no query string is present" do 29 | @search.query = nil 30 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /query/) 31 | @search.query = '' 32 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /query/) 33 | end 34 | end 35 | 36 | describe "version" do 37 | it "should raise an error when it is not present" do 38 | @search.version = nil 39 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /version/) 40 | end 41 | end 42 | end 43 | 44 | describe "#get_raw" do 45 | it "should return JSON string" do 46 | @search.get_raw.should be_a(String) 47 | end 48 | end 49 | 50 | describe "#get_hash" do 51 | it "should return JSON converted to a hash" do 52 | @search.stub!(:get_raw).and_return fixture('web-response.json') 53 | @search.get_hash.should be_a(Hash) 54 | end 55 | end 56 | 57 | describe "#get_response" do 58 | it "should return a Response object" do 59 | @search.stub!(:get_raw).and_return fixture('web-response.json') 60 | @search.get_response.should be_a(Google::Search::Response) 61 | end 62 | 63 | it "should populate #raw" do 64 | @search.stub!(:get_raw).and_return fixture('web-response.json') 65 | @search.get_response.raw.should be_a(String) 66 | end 67 | end 68 | 69 | describe "#next" do 70 | it "should prepare offset" do 71 | @search.size = :small 72 | @search.next.offset.should == 0; @search.get_raw 73 | @search.next.offset.should == 4; @search.get_raw 74 | @search.next.offset.should == 8 75 | @search.size = :large 76 | @search.sent = false 77 | @search.offset = 0 78 | @search.next.offset.should == 0; @search.get_raw 79 | @search.next.offset.should == 8; @search.get_raw 80 | @search.next.offset.should == 16 81 | end 82 | end 83 | 84 | describe "#response" do 85 | it "should alias #get_response" do 86 | @search.next.response.should be_a(Google::Search::Response) 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /spec/search_video_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Google::Search::Video do 5 | before :each do 6 | @search = Google::Search::Video.new :query => 'foo' 7 | end 8 | 9 | describe "#get_uri" do 10 | describe "order_by" do 11 | it "should validate" do 12 | @search.order_by = :date 13 | lambda { @search.get_uri }.should_not raise_error 14 | end 15 | 16 | it "should raise an error when invalid" do 17 | @search.order_by = :foo 18 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /order/) 19 | end 20 | end 21 | 22 | describe "filter" do 23 | it "should default to 1" do 24 | @search.get_uri.should include('filter=1') 25 | end 26 | 27 | it "should consider anything positive as 1" do 28 | @search.filter = true 29 | @search.get_uri.should include('filter=1') 30 | @search.filter = 123 31 | @search.get_uri.should include('filter=1') 32 | end 33 | 34 | it "should consider anything negative as 0" do 35 | @search.filter = false 36 | @search.get_uri.should include('filter=0') 37 | @search.filter = nil 38 | @search.get_uri.should include('filter=0') 39 | end 40 | end 41 | end 42 | end -------------------------------------------------------------------------------- /spec/search_web_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | require File.dirname(__FILE__) + '/spec_helper' 4 | 5 | describe Google::Search::Web do 6 | before :each do 7 | @search = Google::Search::Web.new :query => 'foo' 8 | end 9 | 10 | describe "#get_uri" do 11 | describe "safety_level" do 12 | it "should validate" do 13 | @search.safety_level = :moderate 14 | lambda { @search.get_uri }.should_not raise_error 15 | end 16 | 17 | it "should raise an error when invalid" do 18 | @search.safety_level = :foo 19 | lambda { @search.get_uri }.should raise_error(Google::Search::Error, /safety/) 20 | end 21 | end 22 | 23 | describe "filter" do 24 | it "should default to 1" do 25 | @search.get_uri.should include('filter=1') 26 | end 27 | 28 | it "should consider anything positive as 1" do 29 | @search.filter = true 30 | @search.get_uri.should include('filter=1') 31 | @search.filter = 123 32 | @search.get_uri.should include('filter=1') 33 | end 34 | 35 | it "should consider anything negative as 0" do 36 | @search.filter = false 37 | @search.get_uri.should include('filter=0') 38 | @search.filter = nil 39 | @search.get_uri.should include('filter=0') 40 | end 41 | end 42 | describe "multibyte string query" do 43 | it "url encoding" do 44 | @search.query = "日本語" 45 | @search.get_uri.should include('%E6%97%A5%E6%9C%AC%E8%AA%9E') 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --color 2 | --format specdoc -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | $:.unshift File.dirname(__FILE__) + '/../lib' 3 | require 'pp' 4 | require 'rubygems' 5 | require 'google-search' 6 | 7 | def fixture path 8 | File.read File.dirname(__FILE__) + "/fixtures/#{path}" 9 | end 10 | 11 | def json_fixture name 12 | Google::Search.json_decode fixture("#{name}.json") 13 | end -------------------------------------------------------------------------------- /tasks/docs.rake: -------------------------------------------------------------------------------- 1 | 2 | namespace :docs do 3 | 4 | desc 'Build sdoc' 5 | task :build do 6 | sh "sdoc -d -N -i lib -x spec -x examples -x doc -x Manifest -x Rakefile" 7 | end 8 | 9 | desc 'Remove rdoc products' 10 | task :remove => [:clobber_docs] 11 | 12 | desc 'Build docs, and open in browser for viewing (specify BROWSER)' 13 | task :open => ['docs:build'] do 14 | browser = ENV["BROWSER"] || "safari" 15 | sh "open -a #{browser} doc/index.html" 16 | end 17 | 18 | end -------------------------------------------------------------------------------- /tasks/gemspec.rake: -------------------------------------------------------------------------------- 1 | 2 | desc 'Build gemspec file' 3 | task :gemspec => [:build_gemspec] -------------------------------------------------------------------------------- /tasks/spec.rake: -------------------------------------------------------------------------------- 1 | 2 | require 'spec/rake/spectask' 3 | 4 | desc "Run all specifications" 5 | Spec::Rake::SpecTask.new(:spec) do |t| 6 | t.libs << "lib" 7 | t.spec_opts = ["--color", "--require", "spec/spec_helper.rb"] 8 | end 9 | 10 | namespace :spec do 11 | 12 | desc "Run all specifications verbosely" 13 | Spec::Rake::SpecTask.new(:verbose) do |t| 14 | t.libs << "lib" 15 | t.spec_opts = ["--color", "--format", "specdoc", "--require", "spec/spec_helper.rb"] 16 | end 17 | 18 | desc "Run specific specification verbosely (specify SPEC)" 19 | Spec::Rake::SpecTask.new(:select) do |t| 20 | t.libs << "lib" 21 | t.spec_files = [ENV["SPEC"]] 22 | t.spec_opts = ["--color", "--format", "specdoc", "--require", "spec/spec_helper.rb"] 23 | end 24 | 25 | end --------------------------------------------------------------------------------