diff --git a/History.txt b/History.txt index 63ee8bf..13a0afd 100644 --- a/History.txt +++ b/History.txt @@ -1,6 +1,22 @@ -=== 1.0.0 / 2008-05-19 +=== 0.2.0 / 2009-11-17 -* 1 major enhancement +* New features: + * results can use [] and []= for lookup and seting + * allows results to be open structs (default) or hash (by requiring 'googleajax/as_hash' instead) - * Birthday! +* bug fixes: + * Search.web(...).count now returns an int, not a string + * many calls were not passing the args argument to #get + * Feed.load used to return an open struct with only one method called 'feed' that was a hash. It now returns the feed correctly. +* Behind the scene changes: + * specs + * shorter, cleaner code + * links to google api doc in rdoc + * most classes are now modules + * small optimizations + * All #get now requires arg (see bug fix) + +=== 0.1.0 / 2008-05-19 + +* Initial Release \ No newline at end of file diff --git a/Manifest.txt b/Manifest.txt deleted file mode 100644 index 4876264..0000000 --- a/Manifest.txt +++ /dev/null @@ -1,11 +0,0 @@ -History.txt -Manifest.txt -README.txt -Rakefile -lib/googleajax.rb -lib/googleajax/feed.rb -lib/googleajax/language.rb -lib/googleajax/parser.rb -lib/googleajax/search.rb -lib/googleajax/version.rb -test/test_googleajax.rb diff --git a/README.rdoc b/README.rdoc index d0de3ea..2c187df 100644 --- a/README.rdoc +++ b/README.rdoc @@ -55,11 +55,11 @@ Ruby wrapper for Google AJAX API REST interfaces(Feeds, Language and Search). == REQUIREMENTS: -* JSON gem to parse responses +* JSON gem to parse responses, or rails == INSTALL: - sudo gem install googleajax --include-dependencies + sudo gem install googleajax == LICENSE: diff --git a/Rakefile b/Rakefile index 33502c0..cfb7890 100644 --- a/Rakefile +++ b/Rakefile @@ -1,17 +1,46 @@ -# -*- ruby -*- - require 'rubygems' -require 'hoe' -$: << File.dirname(__FILE__) + '/lib' -require './lib/googleajax.rb' +require 'rake' + +begin + require 'jeweler' + Jeweler::Tasks.new do |gem| + gem.name = "googleajax" + gem.summary = %Q{Ruby wrapper to the Google AJAX API REST interfaces(Feeds, Language and Search).} + #gem.description = %Q{TODO: longer description of your gem} + gem.email = "monki@geemus.com" + gem.homepage = "http://github.com/geemus/googleajax" + gem.authors = ["monki(Wesley Beary)", "Marc-Andre Lafortune"] + gem.add_development_dependency "rspec", ">= 1.2.9" + # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings + gem.add_dependency 'json', '>= 1.0.0' + end + Jeweler::GemcutterTasks.new +rescue LoadError + puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" +end + +require 'spec/rake/spectask' +Spec::Rake::SpecTask.new(:spec) do |spec| + spec.libs << 'lib' << 'spec' + spec.spec_files = FileList['spec/**/*_spec.rb'] +end -Hoe.new('GoogleAjax', GoogleAjax::VERSION::STRING) do |p| - p.rubyforge_name = 'googleajax' - p.developer('monki(Wesley Beary)', 'monki@geemus.com') - p.summary = 'Ruby wrapper to the Google AJAX API REST interfaces(Feeds, Language and Search).' - p.url = 'http://googleajax.rubyforge.com' - p.remote_rdoc_dir = '' - p.extra_deps << ['json', '>= 1.0.0'] +Spec::Rake::SpecTask.new(:rcov) do |spec| + spec.libs << 'lib' << 'spec' + spec.pattern = 'spec/**/*_spec.rb' + spec.rcov = true end -# vim: syntax=Ruby \ No newline at end of file +task :spec => :check_dependencies + +task :default => :spec + +require 'rake/rdoctask' +Rake::RDocTask.new do |rdoc| + version = File.exist?('VERSION') ? File.read('VERSION') : "" + + rdoc.rdoc_dir = 'rdoc' + rdoc.title = "googleajax #{version}" + rdoc.rdoc_files.include('README*') + rdoc.rdoc_files.include('lib/**/*.rb') +end diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..0ea3a94 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.2.0 diff --git a/googleajax.gemspec b/googleajax.gemspec deleted file mode 100644 index a9ec0ed..0000000 --- a/googleajax.gemspec +++ /dev/null @@ -1,25 +0,0 @@ -Gem::Specification.new do |s| - s.name = %q{GoogleAjax} - s.version = "0.1.0" - - s.specification_version = 2 if s.respond_to? :specification_version= - - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["monki(Wesley Beary)"] - s.date = %q{2008-05-19} - s.description = %q{Ruby wrapper to the Google AJAX API REST interfaces(Feeds, Language and Search).} - s.email = ["monki@geemus.com"] - s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"] - s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "lib/googleajax.rb", "lib/googleajax/feed.rb", "lib/googleajax/language.rb", "lib/googleajax/parser.rb", "lib/googleajax/search.rb", "lib/googleajax/version.rb", "test/test_googleajax.rb"] - s.has_rdoc = true - s.homepage = %q{http://googleajax.rubyforge.com} - s.rdoc_options = ["--main", "README.txt"] - s.require_paths = ["lib"] - s.rubyforge_project = %q{googleajax} - s.rubygems_version = %q{1.1.1} - s.summary = %q{Ruby wrapper to the Google AJAX API REST interfaces(Feeds, Language and Search).} - s.test_files = ["test/test_googleajax.rb"] - - s.add_dependency(%q, [">= 1.0.0"]) - s.add_dependency(%q, [">= 1.5.1"]) -end diff --git a/lib/googleajax.rb b/lib/googleajax.rb index 25f9acb..c9715c2 100644 --- a/lib/googleajax.rb +++ b/lib/googleajax.rb @@ -4,37 +4,28 @@ require 'rubygems' require 'json' unless defined?(Rails) -require 'googleajax/feed' -require 'googleajax/language' -require 'googleajax/parser' -require 'googleajax/search' -require 'googleajax/version' +module GoogleAjax + require 'googleajax/base' unless const_defined?("Base") + require 'googleajax/feed' + require 'googleajax/language' + require 'googleajax/search' + require 'googleajax/parser' -class GoogleAjax API_BASE = 'http://ajax.googleapis.com/ajax/services/' - @@api_key = @@referer = nil + class << self + attr_accessor :api_key + attr_accessor :referer - def self.api_key - @@api_key - end - def self.api_key=(key) - @@api_key = key - end - def self.referer - @@referer - end - def self.referer=(referer) - @@referer = referer - end - - def self.get(api, method, query, args = nil) - raise "You must assign a value to GoogleAjax.referer" unless @@referer - url = "#{API_BASE}#{api}/" - url += "#{method}?" - url += "&q=#{CGI::escape(query)}" - url += "&key=#{api_key}" if api_key - url += "&" + args.collect {|key, value| "#{key}=#{value}"}.join('&') if args && !args.empty? - data = open(url, "Referer" => @@referer).read - Parser.parse(api, method, data) + # Api doc is at http://code.google.com/apis/ajaxsearch/documentation/reference.html#_intro_fonje + def get(api, method, query, args = nil) + raise "You must assign a value to GoogleAjax.referer" unless referer + url = "#{API_BASE}#{api}/" + url += "#{method}?" + url += "&q=#{CGI::escape(query)}" + url += "&key=#{api_key}" if api_key + url += "&" + args.collect {|key, value| "#{key}=#{value}"}.join('&') if args && !args.empty? + data = open(url, "Referer" => referer).read + Parser.parse(api, method, data) + end end end \ No newline at end of file diff --git a/lib/googleajax/as_hash.rb b/lib/googleajax/as_hash.rb new file mode 100644 index 0000000..e706d1b --- /dev/null +++ b/lib/googleajax/as_hash.rb @@ -0,0 +1,15 @@ +# Include this file instead of googleajax so that results are simply hashes + +module GoogleAjax + class Base < Hash + def initialize(h) + super() + replace(h) + end + + def self.remap(h) + end + end +end + +require 'googleajax' diff --git a/lib/googleajax/base.rb b/lib/googleajax/base.rb new file mode 100644 index 0000000..ae206d5 --- /dev/null +++ b/lib/googleajax/base.rb @@ -0,0 +1,25 @@ +module GoogleAjax + class Base < OpenStruct + def [](key) + send(key) + end + + def []=(key, value) + send("#{key}=", value) + end + + def self.remap(h) + define_method(:initialize) do |arg| + super(arg) + h.each do |key, klass| + case self[key] + when Array + self[key] = self[key].map(&klass.method(:new)) + when Hash + self[key] = klass.new(self[key]) + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/googleajax/feed.rb b/lib/googleajax/feed.rb index ccd5e03..d7663d5 100644 --- a/lib/googleajax/feed.rb +++ b/lib/googleajax/feed.rb @@ -1,38 +1,35 @@ -class GoogleAjax - class Feed +module GoogleAjax + module Feed FEED_VERSION = 1.0 - def self.get(method, query, args = {}) - args = { :v => FEED_VERSION }.merge!(args) + # Arguments: http://code.google.com/apis/ajaxfeeds/documentation/reference.html#_intro_fonje + def self.get(method, query, args) + args = { :v => FEED_VERSION }.merge(args) GoogleAjax::get(:feed, method, query, args) end - + # will return a list of feeds that match the given query + # Arguments: http://code.google.com/apis/ajaxfeeds/documentation/reference.html#_fonje_find def self.find(query, args = {}) - self.get(:find, query) + self.get(:find, query, args) end # downloads this feed from Google's servers - # Optional: args { :num => number of entries to download(default is 4, maximum is 100) } - def self.load(query, args = {}) - self.get(:load, query, args) + # Arguments: http://code.google.com/apis/ajaxfeeds/documentation/reference.html#_fonje_load + def self.load(url, args = {}) + self.get(:load, url, args) end - + # will return the associated feed if it exists for a given url - def self.lookup(query, args = {}) - self.get(:lookup, query) + # Arguments: http://code.google.com/apis/ajaxfeeds/documentation/reference.html#_intro_fonje + def self.lookup(url, args = {}) + self.get(:lookup, url, args) end - - class Entry < OpenStruct - def initialize(data) - super(data) - end + + class Entry < Base end - - class Feed < OpenStruct - def initialize(data) - super(data) - self.entries = entries.collect {|data| Entry.new(data)} if entries - end + + class Feed < Base + remap :entries => Entry end end end \ No newline at end of file diff --git a/lib/googleajax/language.rb b/lib/googleajax/language.rb index 2b189a8..8c9142f 100644 --- a/lib/googleajax/language.rb +++ b/lib/googleajax/language.rb @@ -1,36 +1,29 @@ -class GoogleAjax - class Language +module GoogleAjax + module Language LANGUAGE_VERSION = 1.0 - def self.get(method, query, args = {}) - args = { :v => LANGUAGE_VERSION }.merge!(args) + def self.get(method, query, args) + args = { :v => LANGUAGE_VERSION }.merge(args) GoogleAjax::get(:language, method, query, args) end # will return the language code that describes the language of the given text def self.detect(query, args = {}) - self.get(:detect, query) + self.get(:detect, query, args) end # will return translated text for the given text supplied, matching the destination language. def self.translate(query, source, destination, args = {}) - args = { :langpair => "#{source}%7C#{destination}"}.merge!(args) + args = { :langpair => "#{source}%7C#{destination}"}.merge(args) self.get(:translate, query, args) end - class Language < OpenStruct - def initialize(data) - super(data) - end - + class Language < Base def name - LANGUAGES.invert[self.language] + (@@lang_cache ||= LANGUAGES.invert)[self.language] end end - class Translation < OpenStruct - def initialize(data) - super(data) - end + class Translation < Base end LANGUAGES = @@ -77,6 +70,6 @@ def initialize(data) 'UKRAINIAN' => 'uk', 'VIETNAMESE' => 'vi', 'UNKNOWN' => '' - } + } end end \ No newline at end of file diff --git a/lib/googleajax/parser.rb b/lib/googleajax/parser.rb index d2d393a..e80b700 100644 --- a/lib/googleajax/parser.rb +++ b/lib/googleajax/parser.rb @@ -1,94 +1,38 @@ -class GoogleAjax - class Parser +module GoogleAjax + module Parser def self.parse(api, method, data) - if defined? Rails - data = ActiveSupport::JSON::decode(data) + data = if defined? Rails + ActiveSupport::JSON::decode(data) else - data = JSON.parse(data) + JSON.parse(data) end - Errors.process(data) - parser = Parser::PARSERS[api][method] - parser.process(data['responseData']) + process_errors(data) + parser = PARSERS[api][method] + response = data['responseData'] + parser.is_a?(Symbol) ? send(parser, response) : parser.new(response) if response end - end - class FeedFind < Parser#:nodoc: - def self.process(data) - data['entries'].collect {|data| GoogleAjax::Feed::Feed.new(data)} if data - end - end - class FeedLoad < Parser#:nodoc: - def self.process(data) - GoogleAjax::Feed::Feed.new(data['feed']) if data - end - end - class FeedLookup < Parser#:nodoc: - def self.process(data) - GoogleAjax::Feed::Feed.new(data) if data + def self.process_errors(data) + status = data['responseStatus'] + unless (200..206).include? status + raise StandardError, data['responseDetails'] + end end - end - class LanguageDetect < Parser#:nodoc - def self.process(data) - GoogleAjax::Language::Language.new(data) if data - end - end - class LanguageTranslate < Parser#:nodoc - def self.process(data) - GoogleAjax::Language::Translation.new(data) if data + def self.parse_feed_list(data) + data['entries'].map(&Feed::Feed.method(:new)) end - end - class SearchBlogs < Parser#:nodoc - def self.process(data) - GoogleAjax::Search::Results.new(data) if data - end - end - class SearchBooks < Parser#:nodoc - def self.process(data) - GoogleAjax::Search::Results.new(data) if data - end - end - class SearchImages < Parser#:nodoc - def self.process(data) - GoogleAjax::Search::Results.new(data) if data - end - end - class SearchLocal < Parser#:nodoc - def self.process(data) - GoogleAjax::Search::Results.new(data) if data - end - end - class SearchNews < Parser#:nodoc - def self.process(data) - GoogleAjax::Search::Results.new(data) if data - end - end - class SearchVideo < Parser#:nodoc - def self.process(data) - GoogleAjax::Search::Results.new(data) if data + def self.parse_one_feed(data) + Feed::Feed.new(data['feed']) end - end - class SearchWeb < Parser#:nodoc - def self.process(data) - GoogleAjax::Search::Results.new(data) if data - end - end - class Parser PARSERS = { - :feed => { :find => FeedFind, :load => FeedLoad, :lookup => FeedLookup }, - :language => { :detect => LanguageDetect, :translate => LanguageTranslate, }, - :search => { :blogs => SearchBlogs, :books => SearchBooks, :images => SearchImages, :local => SearchLocal, :news => SearchNews, :video => SearchVideo, :web => SearchWeb } + :feed => { :find => :parse_feed_list, :load => :parse_one_feed, :lookup => Feed::Feed }, + :language => { :detect => Language::Language, :translate => Language::Translation }, + :search => { :blogs => Search::Results, :books => Search::Results, :images => Search::Results, :local => Search::Results, + :news => Search::Results, :video => Search::Results, :web => Search::Results } } - end - class Errors - def self.process(data) - status = data['responseStatus'] - unless [200, 201, 202, 203, 204, 205, 206].include? status - raise StandardError.new(data['responseDetails']) - end - end end end \ No newline at end of file diff --git a/lib/googleajax/search.rb b/lib/googleajax/search.rb index 47078a0..db6acaf 100644 --- a/lib/googleajax/search.rb +++ b/lib/googleajax/search.rb @@ -1,69 +1,66 @@ -class GoogleAjax - class Search +module GoogleAjax + module Search SEARCH_VERSION = 1.0 - def self.get(method, query, args = {}) - args = { :v => SEARCH_VERSION }.merge!(args) + # Arguments: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_web + def self.get(method, query, args) + args = { :v => SEARCH_VERSION }.merge(args) GoogleAjax::get(:search, method, query, args) end + # Arguments: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_blog def self.blogs(query, args = {}) self.get(:blogs, query, args) end + # Arguments: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_book def self.books(query, args = {}) self.get(:books, query, args) end - + + # Arguments: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_image def self.images(query, args = {}) self.get(:images, query, args) end - + + # Arguments: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_local def self.local(query, latitude, longitude, args = {}) - args = { :sll => "#{latitude},#{longitude}" }.merge!(args) + args = { :sll => "#{latitude},#{longitude}" }.merge(args) self.get(:local, query, args) end + # Arguments: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_news def self.news(query, args = {}) self.get(:news, query, args) end - + + # Arguments: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_video def self.video(query, args = {}) self.get(:video, query, args) end - + + # http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_web def self.web(query, args = {}) self.get(:web, query, args) end - - class Results < OpenStruct - def initialize(data) - super(data) - self.results = results.collect {|data| Result.new(data)} - self.cursor = Cursor.new(cursor) if self.cursor - end - - def count - self.cursor.estimatedResultCount - end + + class Result < Base end - - class Result < OpenStruct - def initialize(data) - super(data) - end + + class Page < Base end - - class Cursor < OpenStruct - def initialize(data) - super(data) - self.pages = pages.collect {|data| Page.new(data)} - end + + class Cursor < Base + remap :pages => Page end - - class Page < OpenStruct - def initialize(data) - super(data) + + class Results < Base + remap :results => Result, + :cursor => Cursor + + def count + self['cursor']['estimatedResultCount'].to_i end - end + end + end end \ No newline at end of file diff --git a/lib/googleajax/version.rb b/lib/googleajax/version.rb deleted file mode 100644 index a5c3325..0000000 --- a/lib/googleajax/version.rb +++ /dev/null @@ -1,8 +0,0 @@ -class GoogleAjax - module VERSION #:nodoc: - MAJOR = 0 - MINOR = 1 - TINY = 0 - STRING = [MAJOR, MINOR, TINY].join('.') - end -end \ No newline at end of file diff --git a/spec/googleajax_as_hash_spec.rb b/spec/googleajax_as_hash_spec.rb new file mode 100644 index 0000000..f1deb25 --- /dev/null +++ b/spec/googleajax_as_hash_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path(File.dirname(__FILE__) + '/spec_helper') +require File.expand_path(File.dirname(__FILE__) + '/googleajax_common') + +require 'googleajax/as_hash' + +describe "GoogleAjax/as_hash" do + it_should_behave_like "GoogleAjax" + + it "returns results as a hash" do + GoogleAjax.referer = "http://example.com" + response = GoogleAjax::Search.web("apple", :rsz => :large) + response.is_a? Hash + response['cursor'].is_a? Hash + end +end \ No newline at end of file diff --git a/spec/googleajax_common.rb b/spec/googleajax_common.rb new file mode 100644 index 0000000..1c69d38 --- /dev/null +++ b/spec/googleajax_common.rb @@ -0,0 +1,93 @@ +# encoding:utf-8 +shared_examples_for "GoogleAjax" do + it "requires a referer" do + GoogleAjax.referer = nil + lambda{GoogleAjax::Search.web("testing")}.should raise_error + end + + describe "::Search" do + describe ".web" do + before :each do + GoogleAjax.referer = "http://example.com" + @response = GoogleAjax::Search.web("apple", :rsz => :large) + end + + it "returns the expected results" do + @response['results'].count{|result| result['url'] =~ /http:(.*).apple.com/}.should >= 4 + end + end + + { + :blogs => 100_000, + :books => 10_000, + :images => 10_000, + :video => 10_000, + :web => 1_000_000, + :local => [100, 48.8565, 2.3509] + }.each do |method, (min, *args)| + describe ".#{method}" do + before :each do + GoogleAjax.referer = "http://example.com" + @response_small = GoogleAjax::Search.send(method, "ruby", *args) + end + + it "takes options into account" do + @response_large = GoogleAjax::Search.send(method, "ruby", *(args+[{:rsz => :large}])) + @response_large['results'].size.should > @response_small['results'].size + end unless method == :blogs # Google doesn't seem to support this option for blogs??? + + it "returns an approximate count of hits" do + @response_small.count.should > min + end + end + end + end + + describe "::Language" do + before :each do + GoogleAjax.referer = "http://example.com" + end + + describe ".detect" do + it "returns the right language" do + GoogleAjax::Language.detect("What's up folks")['language'].should == "en" + GoogleAjax::Language.detect("Montréal est une ville incroyable")['language'].should == "fr" + end + end + + describe ".translate" do + it "does an approximate translation" do + GoogleAjax::Language.translate("Ruby rocks", "en", "fr")['translatedText'].should == "Ruby roches" + end + end + end + + describe "::Feed" do + before :each do + GoogleAjax.referer = "http://example.com" + end + + describe ".find" do + it "returns the right feeds" do + feeds = GoogleAjax::Feed.find("Ruby") + feeds.size.should == 10 + feeds.any?{|result| result['url'] == "http://www.ruby-lang.org/en/feeds/news.rss"}.should be_true + end + end + + describe ".load" do + it "loads entries of a feed" do + feed = GoogleAjax::Feed.load('http://digg.com/rss/index.xml') + feed['title'].should == "digg.com: Stories / Popular" + feed['entries'].size.should == 4 + end + end + + describe ".lookup" do + it "returns the feed associated with a URL" do + feed = GoogleAjax::Feed.lookup("http://digg.com/") + feed['url'].should == "http://feeds.digg.com/digg/popular.rss" + end + end + end +end diff --git a/spec/googleajax_spec.rb b/spec/googleajax_spec.rb new file mode 100644 index 0000000..bd76168 --- /dev/null +++ b/spec/googleajax_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path(File.dirname(__FILE__) + '/spec_helper') +require File.expand_path(File.dirname(__FILE__) + '/googleajax_common') + +require 'googleajax' + +describe "GoogleAjax (standard)" do + it_should_behave_like "GoogleAjax" + + it "returns results as an OpenStuct" do + GoogleAjax.referer = "http://example.com" + response = GoogleAjax::Search.web("apple", :rsz => :large) + response.is_a? OpenStruct + response.cursor.is_a? OpenStruct + end +end \ No newline at end of file diff --git a/spec/spec.opts b/spec/spec.opts new file mode 100644 index 0000000..4e1e0d2 --- /dev/null +++ b/spec/spec.opts @@ -0,0 +1 @@ +--color diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..74740dd --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,8 @@ +$LOAD_PATH.unshift(File.dirname(__FILE__)) +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) +require 'spec' +require 'spec/autorun' + +Spec::Runner.configure do |config| + +end diff --git a/test/test_googleajax.rb b/test/test_googleajax.rb deleted file mode 100644 index e69de29..0000000