Skip to content

Commit

Permalink
First commit for Google Search API
Browse files Browse the repository at this point in the history
  • Loading branch information
wiseleyb committed Mar 20, 2012
0 parents commit b9e5130
Show file tree
Hide file tree
Showing 15 changed files with 643 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
pkg/*
rdoc/*
*.gem
.bundle
2 changes: 2 additions & 0 deletions CHANGELOG.rdoc
@@ -0,0 +1,2 @@
= Changelog

4 changes: 4 additions & 0 deletions Gemfile
@@ -0,0 +1,4 @@
source "http://rubygems.org"

# Specify your gem's dependencies in importers.gemspec
gemspec
38 changes: 38 additions & 0 deletions Gemfile.lock
@@ -0,0 +1,38 @@
PATH
remote: .
specs:
google_custom_search_api (0.0.1)
httparty

GEM
remote: http://rubygems.org/
specs:
addressable (2.2.7)
crack (0.3.1)
diff-lcs (1.1.3)
httparty (0.8.1)
multi_json
multi_xml
json (1.6.5)
multi_json (1.1.0)
multi_xml (0.4.2)
rspec (2.9.0)
rspec-core (~> 2.9.0)
rspec-expectations (~> 2.9.0)
rspec-mocks (~> 2.9.0)
rspec-core (2.9.0)
rspec-expectations (2.9.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.9.0)
webmock (1.8.3)
addressable (>= 2.2.7)
crack (>= 0.1.7)

PLATFORMS
ruby

DEPENDENCIES
google_custom_search_api!
json
rspec
webmock
20 changes: 20 additions & 0 deletions LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2012 Ben Wiseley

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
80 changes: 80 additions & 0 deletions README.md
@@ -0,0 +1,80 @@
# Google Custom Search

This project is a Ruby lib for Google's Custom Search ENgine API (http://www.google.com/cse). There seem to be quite a few cse libs out there that don't work so I rolled this up quickly.

Questions/comments, etc: wiseleyb@gmail.com

## Install

Add to your Gemfile:

gem "google_custom_search_api"

then

bundle install

## Configure

You need to configure ``GOOGLE_SEARCH_CX`` and ```GOOGLE_API_KEY``` to ```config/initializers/google_cse_api.rb```:

```
GOOGLE_API_KEY = "..."
GOOGLE_SEARCH_CX = "..."
```

## Use

To perform a search:

```
results = GoogleCustomSearchApi.search("poker")
```
Results now contains a raw version and a class'ed version of the data show in ```Sample results``` below.

This means you can do:

```
results["items"].each do |item|
puts item["title"], item["link"]
end
```

or

```
results.items.each do |item|
puts item.title, item.link
end
```

See [Custom Search](http://code.google.com/apis/customsearch/v1/using_rest.html) documentation for an explanation of all fields available.

## Contributing - Running tests

Pull requests welcome.

To run tests
```
git clone git@github.com:wiseleyb/google_custom_search_api.git
cd google_custom_search_api
bundle install
bundle exec rspec spec
```

## Credits
* Based largely on the gem https://github.com/alexreisner/google_custom_search
* Awesome ResponseData class from https://github.com/mikedemers/rbing
* Work done while working on a project for the company http://reInteractive.net in sunny Sydney. A great ruby shop should need help with something.

## TODO
* pretty light on the tests
* support paging (will be doing this week)
* add how-to for key and cx to README

## Sample results

See spec/fixtures/*.json for examples of data returned


Copyright (c) 2012 Ben Wiseley, released under the MIT license
19 changes: 19 additions & 0 deletions Rakefile
@@ -0,0 +1,19 @@
require 'bundler'
Bundler::GemHelper.install_tasks

require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end

task :default => :test

require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "Google Custom Search API #{GoogleCustomSearch::VERSION}"
rdoc.rdoc_files.include('*.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end
21 changes: 21 additions & 0 deletions google_custom_search_api.gemspec
@@ -0,0 +1,21 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "version"

Gem::Specification.new do |s|
s.name = "google_custom_search_api"
s.version = GoogleCustomSearchApi::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Ben Wiseley"]
s.email = ["wiseleyb@gmail.com"]
s.homepage = "http://github.com/wiseleyb/google_custom_search_api"
s.date = Date.today.to_s
s.summary = "Ruby lib for Google's Custom Search Api."
s.description = "Ruby lib for Google's Custom Search Api."
s.files = `git ls-files`.split("\n") - %w[google_custom_search_api.gemspec Gemfile init.rb]
s.require_paths = ["lib"]
s.add_runtime_dependency "httparty"
s.add_development_dependency "rspec"
s.add_development_dependency "webmock"
s.add_development_dependency "json"
end
2 changes: 2 additions & 0 deletions init.rb
@@ -0,0 +1,2 @@
require "addressable/uri"
require 'google_custom_search_api'
91 changes: 91 additions & 0 deletions lib/google_custom_search_api.rb
@@ -0,0 +1,91 @@
##
# Add search functionality (via Google Custom Search). Protocol reference at:
# http://www.google.com/coop/docs/cse/resultsxml.html
#
module GoogleCustomSearchApi
extend self

##
# Search the site.
#
def search(query, page = 0)
# Get and parse results.
url = url(query, page)
puts url
return nil unless results = fetch(url)
results["items"] ||= []
ResponseData.new(results)
end

# Convenience wrapper for the response Hash.
# Converts keys to Strings. Crawls through all
# member data and converts any other Hashes it
# finds. Provides access to values through
# method calls, which will convert underscored
# to camel case.
#
# Usage:
#
# rd = ResponseData.new("AlphaBeta" => 1, "Results" => {"Gamma" => 2, "delta" => [3, 4]})
# puts rd.alpha_beta
# => 1
# puts rd.alpha_beta.results.gamma
# => 2
# puts rd.alpha_beta.results.delta
# => [3, 4]
#
class ResponseData < Hash
private
def initialize(data={})
data.each_pair {|k,v| self[k.to_s] = deep_parse(v) }
end
def deep_parse(data)
case data
when Hash
self.class.new(data)
when Array
data.map {|v| deep_parse(v) }
else
data
end
end
def method_missing(*args)
name = args[0].to_s
return self[name] if has_key? name
camelname = name.split('_').map {|w| "#{w[0,1].upcase}#{w[1..-1]}" }.join("")
if has_key? camelname
self[camelname]
else
super *args
end
end
end


private # -------------------------------------------------------------------

##
# Build search request URL.
#
def url(query, page = 0)
params = {
:q => query,
:alt => "json"
}
uri = Addressable::URI.new
uri.query_values = params
begin
params.merge!(GOOGLE_SEARCH_PARAMS)
rescue NameError
end
"https://www.googleapis.com/customsearch/v1?key=#{GOOGLE_API_KEY}&cx=#{GOOGLE_SEARCH_CX}&#{uri.query}"
end

##
# Query Google, and make sure it responds.
#
def fetch(url)
return HTTParty.get(url)
end

end
3 changes: 3 additions & 0 deletions lib/version.rb
@@ -0,0 +1,3 @@
module GoogleCustomSearchApi
VERSION = "0.0.1"
end
31 changes: 31 additions & 0 deletions spec/fixtures/empty.json
@@ -0,0 +1,31 @@
{
"kind": "customsearch#search",
"url": {
"type": "application/json",
"template": "https://www.googleapis.com/customsearch/v1?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&safe={safe?}&cx={cx?}&cref={cref?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&orTerms={orTerms?}&relatedSite={relatedSite?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json"
},
"queries": {
"request": [
{
"title": "Google Custom Search - asdfojasoidfjao sdfjaosdfj aosdijfoaisdjfoasjdofijas dofijweo fwoef jwoeifjw",
"totalResults": "0",
"searchTerms": "asdfojasoidfjao sdfjaosdfj aosdijfoaisdjfoasjdofijas dofijweo fwoef jwoeifjw",
"count": 10,
"inputEncoding": "utf8",
"outputEncoding": "utf8",
"safe": "off",
"cx": "002432975944642411257:ztx9u0hzbcw"
}
]
},
"searchInformation": {
"searchTime": 0.121274,
"formattedSearchTime": "0.12",
"totalResults": "0",
"formattedTotalResults": "0"
},
"spelling": {
"correctedQuery": "asdf ojasoidfjao sdfj sdfj aosdijfoaisdjfoasjdofijas dofijweo fwoef jwoeifjw",
"htmlCorrectedQuery": "\u003cb\u003e\u003ci\u003easdf ojasoidfjao\u003c/i\u003e\u003c/b\u003e \u003cb\u003e\u003ci\u003esdfj sdfj\u003c/i\u003e\u003c/b\u003e aosdijfoaisdjfoasjdofijas dofijweo fwoef jwoeifjw"
}
}

0 comments on commit b9e5130

Please sign in to comment.