Checkout of Instiki Trunk 1/21/2007.

This commit is contained in:
Jacques Distler 2007-01-22 07:43:50 -06:00
commit 69b62b6f33
1138 changed files with 139586 additions and 0 deletions

View file

@ -0,0 +1,143 @@
= Google Service example
This example shows how one would implement an API like Google
Search that uses lots of structured types.
There are examples for "Direct" and "Delegated" dispatching
modes.
There is also an example for API definition file autoloading.
= Running the examples
1. Add the files to an Action Web Service enabled Rails project.
"Direct" example:
* Copy direct/search_controller.rb to "app/controllers"
in a Rails project.
* Copy direct/google_search_api.rb to "app/apis"
in a Rails project
"Delegated" example:
* Copy delegated/search_controller.rb to "app/controllers"
in a Rails project.
* Copy delegated/google_search_service.rb to "lib"
in a Rails project.
"Autoloading" example:
* Copy autoloading/google_search_api.rb to "app/apis" (create the directory
if it doesn't exist) in a Rails project.
* Copy autoloading/google_search_controller.rb "app/controllers"
in a Rails project.
2. Go to the WSDL url in a browser, and check that it looks correct.
"Direct" and "Delegated" examples:
http://url_to_project/search/wsdl
"Autoloading" example:
http://url_to_project/google_search/wsdl
You can compare it to Google's hand-coded WSDL at http://api.google.com/GoogleSearch.wsdl
and see how close (or not) the generated version is.
Note that I used GoogleSearch as the canonical "best practice"
interoperable example when implementing WSDL/SOAP support, which might
explain extreme similarities :)
3. Test that it works with .NET (Mono in this example):
$ wget WSDL_URL
$ mv wsdl GoogleSearch.wsdl
$ wsdl -out:GoogleSearch.cs GoogleSearch.wsdl
Add these lines to the GoogleSearchService class body (be mindful of the
wrapping):
public static void Main(string[] args)
{
GoogleSearchResult result;
GoogleSearchService service;
service = new GoogleSearchService();
result = service.doGoogleSearch("myApiKey", "my query", 10, 30, true, "restrict", false, "lr", "ie", "oe");
System.Console.WriteLine("documentFiltering: {0}", result.documentFiltering);
System.Console.WriteLine("searchComments: {0}", result.searchComments);
System.Console.WriteLine("estimatedTotalResultsCount: {0}", result.estimatedTotalResultsCount);
System.Console.WriteLine("estimateIsExact: {0}", result.estimateIsExact);
System.Console.WriteLine("resultElements:");
foreach (ResultElement element in result.resultElements) {
System.Console.WriteLine("\tsummary: {0}", element.summary);
System.Console.WriteLine("\tURL: {0}", element.URL);
System.Console.WriteLine("\tsnippet: {0}", element.snippet);
System.Console.WriteLine("\ttitle: {0}", element.title);
System.Console.WriteLine("\tcachedSize: {0}", element.cachedSize);
System.Console.WriteLine("\trelatedInformationPresent: {0}", element.relatedInformationPresent);
System.Console.WriteLine("\thostName: {0}", element.hostName);
System.Console.WriteLine("\tdirectoryCategory: {0}", element.directoryCategory.fullViewableName);
System.Console.WriteLine("\tdirectoryTitle: {0}", element.directoryTitle);
}
System.Console.WriteLine("searchQuery: {0}", result.searchQuery);
System.Console.WriteLine("startIndex: {0}", result.startIndex);
System.Console.WriteLine("endIndex: {0}", result.endIndex);
System.Console.WriteLine("searchTips: {0}", result.searchTips);
System.Console.WriteLine("directoryCategories:");
foreach (DirectoryCategory cat in result.directoryCategories) {
System.Console.WriteLine("\t{0} ({1})", cat.fullViewableName, cat.specialEncoding);
}
System.Console.WriteLine("searchTime: {0}", result.searchTime);
}
Now compile and run:
$ mcs -reference:System.Web.Services GoogleSearch.cs
$ mono GoogleSearch.exe
If you had the application running (on the same host you got
the WSDL from), you should see something like this:
documentFiltering: True
searchComments:
estimatedTotalResultsCount: 322000
estimateIsExact: False
resultElements:
summary: ONlamp.com: Rolling with Ruby on Rails
URL: http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html
snippet: Curt Hibbs shows off Ruby on Rails by building a simple ...
title: Teh Railz0r
cachedSize: Almost no lines of code!
relatedInformationPresent: True
hostName: rubyonrails.com
directoryCategory: Web Development
directoryTitle:
searchQuery: http://www.google.com/search?q=ruby+on+rails
startIndex: 10
endIndex: 40
searchTips: "on" is a very common word and was not included in your search [details]
directoryCategories:
Web Development (UTF-8)
Programming (US-ASCII)
searchTime: 1E-06
Also, if an API method throws an exception, it will be sent back to the
caller in the protocol's exception format, so they should get an exception
thrown on their side with a meaningful error message.
If you don't like this behaviour, you can do:
class MyController < ActionController::Base
web_service_exception_reporting false
end
4. Crack open a beer. Publishing APIs for working with the same model as
your Rails web app should be easy from now on :)

View file

@ -0,0 +1,50 @@
class DirectoryCategory < ActionWebService::Struct
member :fullViewableName, :string
member :specialEncoding, :string
end
class ResultElement < ActionWebService::Struct
member :summary, :string
member :URL, :string
member :snippet, :string
member :title, :string
member :cachedSize, :string
member :relatedInformationPresent, :bool
member :hostName, :string
member :directoryCategory, DirectoryCategory
member :directoryTitle, :string
end
class GoogleSearchResult < ActionWebService::Struct
member :documentFiltering, :bool
member :searchComments, :string
member :estimatedTotalResultsCount, :int
member :estimateIsExact, :bool
member :resultElements, [ResultElement]
member :searchQuery, :string
member :startIndex, :int
member :endIndex, :int
member :searchTips, :string
member :directoryCategories, [DirectoryCategory]
member :searchTime, :float
end
class GoogleSearchAPI < ActionWebService::API::Base
inflect_names false
api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
{:key=>:string},
{:q=>:string},
{:start=>:int},
{:maxResults=>:int},
{:filter=>:bool},
{:restrict=>:string},
{:safeSearch=>:bool},
{:lr=>:string},
{:ie=>:string},
{:oe=>:string}
]
end

View file

@ -0,0 +1,57 @@
class GoogleSearchController < ApplicationController
wsdl_service_name 'GoogleSearch'
def doGetCachedPage
"<html><body>i am a cached page. my key was %s, url was %s</body></html>" % [@params['key'], @params['url']]
end
def doSpellingSuggestion
"%s: Did you mean '%s'?" % [@params['key'], @params['phrase']]
end
def doGoogleSearch
resultElement = ResultElement.new
resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
"almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
resultElement.title = "Teh Railz0r"
resultElement.cachedSize = "Almost no lines of code!"
resultElement.relatedInformationPresent = true
resultElement.hostName = "rubyonrails.com"
resultElement.directoryCategory = category("Web Development", "UTF-8")
result = GoogleSearchResult.new
result.documentFiltering = @params['filter']
result.searchComments = ""
result.estimatedTotalResultsCount = 322000
result.estimateIsExact = false
result.resultElements = [resultElement]
result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
result.startIndex = @params['start']
result.endIndex = @params['start'] + @params['maxResults']
result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
result.searchTime = 0.000001
# For Mono, we have to clone objects if they're referenced by more than one place, otherwise
# the Ruby SOAP collapses them into one instance and uses references all over the
# place, confusing Mono.
#
# This has recently been fixed:
# http://bugzilla.ximian.com/show_bug.cgi?id=72265
result.directoryCategories = [
category("Web Development", "UTF-8"),
category("Programming", "US-ASCII"),
]
result
end
private
def category(name, encoding)
cat = DirectoryCategory.new
cat.fullViewableName = name.dup
cat.specialEncoding = encoding.dup
cat
end
end

View file

@ -0,0 +1,108 @@
class DirectoryCategory < ActionWebService::Struct
member :fullViewableName, :string
member :specialEncoding, :string
end
class ResultElement < ActionWebService::Struct
member :summary, :string
member :URL, :string
member :snippet, :string
member :title, :string
member :cachedSize, :string
member :relatedInformationPresent, :bool
member :hostName, :string
member :directoryCategory, DirectoryCategory
member :directoryTitle, :string
end
class GoogleSearchResult < ActionWebService::Struct
member :documentFiltering, :bool
member :searchComments, :string
member :estimatedTotalResultsCount, :int
member :estimateIsExact, :bool
member :resultElements, [ResultElement]
member :searchQuery, :string
member :startIndex, :int
member :endIndex, :int
member :searchTips, :string
member :directoryCategories, [DirectoryCategory]
member :searchTime, :float
end
class GoogleSearchAPI < ActionWebService::API::Base
inflect_names false
api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
{:key=>:string},
{:q=>:string},
{:start=>:int},
{:maxResults=>:int},
{:filter=>:bool},
{:restrict=>:string},
{:safeSearch=>:bool},
{:lr=>:string},
{:ie=>:string},
{:oe=>:string}
]
end
class GoogleSearchService < ActionWebService::Base
web_service_api GoogleSearchAPI
def doGetCachedPage(key, url)
"<html><body>i am a cached page</body></html>"
end
def doSpellingSuggestion(key, phrase)
"Did you mean 'teh'?"
end
def doGoogleSearch(key, q, start, maxResults, filter, restrict, safeSearch, lr, ie, oe)
resultElement = ResultElement.new
resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
"almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
resultElement.title = "Teh Railz0r"
resultElement.cachedSize = "Almost no lines of code!"
resultElement.relatedInformationPresent = true
resultElement.hostName = "rubyonrails.com"
resultElement.directoryCategory = category("Web Development", "UTF-8")
result = GoogleSearchResult.new
result.documentFiltering = filter
result.searchComments = ""
result.estimatedTotalResultsCount = 322000
result.estimateIsExact = false
result.resultElements = [resultElement]
result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
result.startIndex = start
result.endIndex = start + maxResults
result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
result.searchTime = 0.000001
# For Mono, we have to clone objects if they're referenced by more than one place, otherwise
# the Ruby SOAP collapses them into one instance and uses references all over the
# place, confusing Mono.
#
# This has recently been fixed:
# http://bugzilla.ximian.com/show_bug.cgi?id=72265
result.directoryCategories = [
category("Web Development", "UTF-8"),
category("Programming", "US-ASCII"),
]
result
end
private
def category(name, encoding)
cat = DirectoryCategory.new
cat.fullViewableName = name.dup
cat.specialEncoding = encoding.dup
cat
end
end

View file

@ -0,0 +1,7 @@
require 'google_search_service'
class SearchController < ApplicationController
wsdl_service_name 'GoogleSearch'
web_service_dispatching_mode :delegated
web_service :beta3, GoogleSearchService.new
end

View file

@ -0,0 +1,50 @@
class DirectoryCategory < ActionWebService::Struct
member :fullViewableName, :string
member :specialEncoding, :string
end
class ResultElement < ActionWebService::Struct
member :summary, :string
member :URL, :string
member :snippet, :string
member :title, :string
member :cachedSize, :string
member :relatedInformationPresent, :bool
member :hostName, :string
member :directoryCategory, DirectoryCategory
member :directoryTitle, :string
end
class GoogleSearchResult < ActionWebService::Struct
member :documentFiltering, :bool
member :searchComments, :string
member :estimatedTotalResultsCount, :int
member :estimateIsExact, :bool
member :resultElements, [ResultElement]
member :searchQuery, :string
member :startIndex, :int
member :endIndex, :int
member :searchTips, :string
member :directoryCategories, [DirectoryCategory]
member :searchTime, :float
end
class GoogleSearchAPI < ActionWebService::API::Base
inflect_names false
api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
{:key=>:string},
{:q=>:string},
{:start=>:int},
{:maxResults=>:int},
{:filter=>:bool},
{:restrict=>:string},
{:safeSearch=>:bool},
{:lr=>:string},
{:ie=>:string},
{:oe=>:string}
]
end

View file

@ -0,0 +1,58 @@
class SearchController < ApplicationController
web_service_api :google_search
wsdl_service_name 'GoogleSearch'
def doGetCachedPage
"<html><body>i am a cached page. my key was %s, url was %s</body></html>" % [@params['key'], @params['url']]
end
def doSpellingSuggestion
"%s: Did you mean '%s'?" % [@params['key'], @params['phrase']]
end
def doGoogleSearch
resultElement = ResultElement.new
resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
"almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
resultElement.title = "Teh Railz0r"
resultElement.cachedSize = "Almost no lines of code!"
resultElement.relatedInformationPresent = true
resultElement.hostName = "rubyonrails.com"
resultElement.directoryCategory = category("Web Development", "UTF-8")
result = GoogleSearchResult.new
result.documentFiltering = @params['filter']
result.searchComments = ""
result.estimatedTotalResultsCount = 322000
result.estimateIsExact = false
result.resultElements = [resultElement]
result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
result.startIndex = @params['start']
result.endIndex = @params['start'] + @params['maxResults']
result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
result.searchTime = 0.000001
# For Mono, we have to clone objects if they're referenced by more than one place, otherwise
# the Ruby SOAP collapses them into one instance and uses references all over the
# place, confusing Mono.
#
# This has recently been fixed:
# http://bugzilla.ximian.com/show_bug.cgi?id=72265
result.directoryCategories = [
category("Web Development", "UTF-8"),
category("Programming", "US-ASCII"),
]
result
end
private
def category(name, encoding)
cat = DirectoryCategory.new
cat.fullViewableName = name.dup
cat.specialEncoding = encoding.dup
cat
end
end

View file

@ -0,0 +1,17 @@
= metaWeblog example
This example shows how one might begin to go about adding metaWeblog
(http://www.xmlrpc.com/metaWeblogApi) API support to a Rails-based
blogging application.
The example APIs are more verbose than you may want to make them, for documentation
reasons.
= Running
1. Copy the "apis" directory and its files into "app" in a Rails project.
2. Copy the "controllers" directory and its files into "app" in a Rails project
3. Fire up a desktop blogging application (such as w.bloggar, MarsEdit, or BloGTK),
point it at http://localhost:3000/xmlrpc/api, and try creating or editing blog posts.

View file

@ -0,0 +1,60 @@
#
# see the blogger API spec at http://www.blogger.com/developers/api/1_docs/
# note that the method signatures are subtly different to metaWeblog, they
# are not identical. take care to ensure you handle the different semantics
# properly if you want to support blogger API too, to get maximum compatibility.
#
module Blog
class Blog < ActionWebService::Struct
member :url, :string
member :blogid, :string
member :blogName, :string
end
class User < ActionWebService::Struct
member :nickname, :string
member :userid, :string
member :url, :string
member :email, :string
member :lastname, :string
member :firstname, :string
end
end
#
# blogger
#
class BloggerAPI < ActionWebService::API::Base
inflect_names false
api_method :newPost, :returns => [:string], :expects => [
{:appkey=>:string},
{:blogid=>:string},
{:username=>:string},
{:password=>:string},
{:content=>:string},
{:publish=>:bool}
]
api_method :editPost, :returns => [:bool], :expects => [
{:appkey=>:string},
{:postid=>:string},
{:username=>:string},
{:password=>:string},
{:content=>:string},
{:publish=>:bool}
]
api_method :getUsersBlogs, :returns => [[Blog::Blog]], :expects => [
{:appkey=>:string},
{:username=>:string},
{:password=>:string}
]
api_method :getUserInfo, :returns => [Blog::User], :expects => [
{:appkey=>:string},
{:username=>:string},
{:password=>:string}
]
end

View file

@ -0,0 +1,34 @@
require 'blogger_api'
class BloggerService < ActionWebService::Base
web_service_api BloggerAPI
def initialize
@postid = 0
end
def newPost(key, id, user, pw, content, publish)
$stderr.puts "id=#{id} user=#{user} pw=#{pw}, content=#{content.inspect} [#{publish}]"
(@postid += 1).to_s
end
def editPost(key, post_id, user, pw, content, publish)
$stderr.puts "id=#{post_id} user=#{user} pw=#{pw} content=#{content.inspect} [#{publish}]"
true
end
def getUsersBlogs(key, user, pw)
$stderr.puts "getting blogs for #{user}"
blog = Blog::Blog.new(
:url =>'http://blog',
:blogid => 'myblog',
:blogName => 'My Blog'
)
[blog]
end
def getUserInfo(key, user, pw)
$stderr.puts "getting user info for #{user}"
Blog::User.new(:nickname => 'user', :email => 'user@test.com')
end
end

View file

@ -0,0 +1,67 @@
#
# here lie structures, cousins of those on http://www.xmlrpc.com/metaWeblog
# but they don't necessarily the real world reflect
# so if you do, find that your client complains:
# please tell, of problems you suffered through
#
module Blog
class Post < ActionWebService::Struct
member :title, :string
member :link, :string
member :description, :string
member :author, :string
member :category, :string
member :comments, :string
member :guid, :string
member :pubDate, :string
end
class Category < ActionWebService::Struct
member :description, :string
member :htmlUrl, :string
member :rssUrl, :string
end
end
#
# metaWeblog
#
class MetaWeblogAPI < ActionWebService::API::Base
inflect_names false
api_method :newPost, :returns => [:string], :expects => [
{:blogid=>:string},
{:username=>:string},
{:password=>:string},
{:struct=>Blog::Post},
{:publish=>:bool}
]
api_method :editPost, :returns => [:bool], :expects => [
{:postid=>:string},
{:username=>:string},
{:password=>:string},
{:struct=>Blog::Post},
{:publish=>:bool},
]
api_method :getPost, :returns => [Blog::Post], :expects => [
{:postid=>:string},
{:username=>:string},
{:password=>:string},
]
api_method :getCategories, :returns => [[Blog::Category]], :expects => [
{:blogid=>:string},
{:username=>:string},
{:password=>:string},
]
api_method :getRecentPosts, :returns => [[Blog::Post]], :expects => [
{:blogid=>:string},
{:username=>:string},
{:password=>:string},
{:numberOfPosts=>:int},
]
end

View file

@ -0,0 +1,48 @@
require 'meta_weblog_api'
class MetaWeblogService < ActionWebService::Base
web_service_api MetaWeblogAPI
def initialize
@postid = 0
end
def newPost(id, user, pw, struct, publish)
$stderr.puts "id=#{id} user=#{user} pw=#{pw}, struct=#{struct.inspect} [#{publish}]"
(@postid += 1).to_s
end
def editPost(post_id, user, pw, struct, publish)
$stderr.puts "id=#{post_id} user=#{user} pw=#{pw} struct=#{struct.inspect} [#{publish}]"
true
end
def getPost(post_id, user, pw)
$stderr.puts "get post #{post_id}"
Blog::Post.new(:title => 'hello world', :description => 'first post!')
end
def getCategories(id, user, pw)
$stderr.puts "categories for #{user}"
cat = Blog::Category.new(
:description => 'Tech',
:htmlUrl => 'http://blog/tech',
:rssUrl => 'http://blog/tech.rss')
[cat]
end
def getRecentPosts(id, user, pw, num)
$stderr.puts "recent #{num} posts for #{user} on blog #{id}"
post1 = Blog::Post.new(
:title => 'first post!',
:link => 'http://blog.xeraph.org/testOne.html',
:description => 'this is the first post'
)
post2 = Blog::Post.new(
:title => 'second post!',
:link => 'http://blog.xeraph.org/testTwo.html',
:description => 'this is the second post'
)
[post1, post2]
end
end

View file

@ -0,0 +1,16 @@
#
# example controller implementing both blogger and metaWeblog APIs
# in a way that should be compatible with clients supporting both/either.
#
# test by pointing your client at http://URL/xmlrpc/api
#
require 'meta_weblog_service'
require 'blogger_service'
class XmlrpcController < ApplicationController
web_service_dispatching_mode :layered
web_service :metaWeblog, MetaWeblogService.new
web_service :blogger, BloggerService.new
end