Cover | Table of Contents | Colophon
amazon.com responds by serving a
second document in HTML format. This document contains a description of your
search results, links to additional search options, and miscellaneous
commercial enticements (see ). Again,
your browser renders the document in graphical form, and you look at it and
decide what to do from there.oreilly.com. I’ve truncated two lines to
make the text fit on the printed page.GET /index.html HTTP/1.1 Host: www.oreilly.com User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12)... Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,... Accept-Language: us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive
www.oreilly.com).#!/usr/bin/ruby -w
# flickr-photo-search.rb
require 'open-uri'
require 'rexml/document'
# Returns the URI to a small version of a Flickr photo.
def small_photo_uri(photo)
server = photo.attribute('server')
id = photo.attribute('id')
secret = photo.attribute('secret')
return "http://static.flickr.com/#{server}/#{id}_#{secret}_m.jpg"
end
# Searches Flickr for photos matching a certain tag, and prints a URI
# for each search result.
def print_each_photo(api_key, tag)
# Build the URI
uri = "http://www.flickr.com/services/rest?method=flickr.photos.search" +
"&api_key=#{api_key}&tags=#{tag}"
# Make the HTTP request and get the entity-body.
response = open(uri).read
# Parse the entity-body as an XML document.
doc = REXML::Document.new(response)
# For each photo found...
REXML::XPath.each(doc, '//photo') do |photo|
# ...generate and print its URI
puts small_photo_uri(photo) if photo
end
end
# Main program
#
if ARGV.size < 2
puts "Usage: #{$0} [Flickr API key] [search term]"
exit
end
api_key, tag = ARGV
print_each_photo(api_key, tag)tags=penguin scopes the
flickr.photos.search method so it only searches for
photos tagged with “penguin.” In a service where the method information
defines a method in the programming language sense, the scoping
information can be seen as a set of arguments to that method. You could
reasonably expect to see
flickr.photos.search(tags=penguin) as a line of code in
some programming language.q tag whose
contents are the string “REST.” That’s the scoping information, nestled
conveniently inside the doGoogleSearch tag that provides the method
information.#!/usr/bin/ruby
# yahoo-web-search.rb
require 'open-uri'
require 'rexml/document'
require 'cgi'
BASE_URI = 'http://api.search.yahoo.com/WebSearchService/V1/webSearch'
def print_page_titles(term)
# Fetch a resource: an XML document full of search results.
term = CGI::escape(term)
xml = open(BASE_URI + "?appid=restbook&query=#{term}").read
# Parse the XML document into a data structure.
document = REXML::Document.new(xml)
# Use XPath to find the interesting parts of the data structure.
REXML::XPath.each(document, '/ResultSet/Result/Title/[]') do |title|
puts title
end
end
(puts "Usage: #{$0} [search term]"; exit) if ARGV.empty?
print_page_titles(ARGV.join(' '))#!/usr/bin/ruby
# yahoo-web-search.rb
require 'open-uri'
require 'rexml/document'
require 'cgi'
BASE_URI = 'http://api.search.yahoo.com/WebSearchService/V1/webSearch'
def print_page_titles(term)
# Fetch a resource: an XML document full of search results.
term = CGI::escape(term)
xml = open(BASE_URI + "?appid=restbook&query=#{term}").read
# Parse the XML document into a data structure.
document = REXML::Document.new(xml)
# Use XPath to find the interesting parts of the data structure.
REXML::XPath.each(document, '/ResultSet/Result/Title/[]') do |title|
puts title
end
end
(puts "Usage: #{$0} [search term]"; exit) if ARGV.empty?
print_page_titles(ARGV.join(' '))
https://del.icio.us/{your-username}.
To get your recent bookmarks from the web service, you fetch open-uri), but both interfaces do the same thing. There’s only one
kind of HTTP client library.[3, "three"]
<value> <array> <data> <value><i4>3</i4></value> <value><string>three</string></value> </data> </array> </value>
<!-- json-demo.html -->
<!-- In a real application, you would save json.js locally
instead of fetching it from json.org every time. -->
<script type="text/javascript" src="http://www.json.org/json.js">
</script>
<script type="text/javascript">
array = [3, "three"]
alert("Converted array into JSON string: '" + array.toJSONString() + "'")
json = "[4, \"four\"]"
alert("Converted JSON '" + json + "' into array:")
array2 = json.parseJSON()
for (i=0; i < array2.length; i++)
{
alert("Element #" + i + " is " + array2[i])
}
</script>#!/usr/bin/ruby
# delicious-wadl-ruby.rb
require 'wadl'
if ARGV.size != 2
puts "Usage: #{$0} [username] [password]"
exit
end
username, password = ARGV
# Load an application from the WADL file
delicious = WADL::Application.from_wadl(open("delicious.wadl"))
# Give authentication information to the application
service = delicious.v1.with_basic_auth(username, password)
begin
# Find the "recent posts" functionality
recent_posts = service.posts.recent
# For every recent post...
recent_posts.get.representation.each_by_param('post') do |post|
# Print its description and URI.
puts "#{post.attributes['description']}: #{post.attributes['href']}"
end
rescue WADL::Faults::AuthorizationRequired
puts "Invalid authentication information!"
endContent-Type and
Content-Disposition.Content-Type metadata value
set to text/html, so that people browsing the site
would be served these objects as HTML documents, as opposed to XML or
plain text.https://s3.amazonaws.com/). There’s only one
resource of this type.https://s3.amazonaws.com/{name-of-bucket}/).
There can be up to 100 resources of this type.https://s3.amazonaws.com/{name-of-bucket}/{name-of-object}).
There can be infinitely many resources of this type.https://s3.amazonaws.com/crummy.com/nonexistent/object).
The response code is 404 (“Not Found”).404 Not Found Content-Type: application/xml Date: Fri, 10 Nov 2006 20:04:45 GMT Server: AmazonS3 Transfer-Encoding: chunked X-amz-id-2: /sBIPQxHJCsyRXJwGWNzxuL5P+K96/Wvx4FhvVACbjRfNbhbDyBH5RC511sIz0w0 X-amz-request-id: ED2168503ABB7BF4 <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>NoSuchKey</Code> <Message>The specified key does not exist.</Message> <Key>nonexistent/object</Key> <RequestId>ED2168503ABB7BF4</RequestId> <HostId>/sBIPQxHJCsyRXJwGWNzxuL5P+K96/Wvx4FhvVACbjRfNbhbDyBH5RC511sIz0w0</HostId> </Error>
getObjects, I’ll
try to use names that reflect the underlying RESTful interface:
get, put, and so on.#!/usr/bin/ruby -w
# S3lib.rb
# Libraries neccessary for making HTTP requests and parsing responses.
require 'rubygems'
require 'rest-open-uri'
require 'rexml/document'
# Libraries neccessary for request signing
require 'openssl'
require 'digest/sha1'
require 'base64'
require 'uri'
module S3 # This is the beginning of a big, all-encompassing module.
module Authorized
# Enter your public key (Amazon calls it an "Access Key ID") and
# your private key (Amazon calls it a "Secret Access Key"). This is
# so you can sign your S3 requests and Amazon will know who to
# charge.
@@public_key = ''
@@private_key = ''
if @@public_key.empty? or @@private_key.empty?
raise "You need to set your S3 keys."
end
# You shouldn't need to change this unless you're using an S3 clone like
# Park Place.
HOST = 'https://s3.amazonaws.com/'
endAuthorization header. S3 has no proof that you’re the owner of your own buckets.
Remember, Amazon charges you for the data stored on their servers and the
bandwidth used in transferring that data. If S3 accepted requests to your
buckets with no authorization, anyone could store data in your buckets and
you’d get charged for it.#!/usr/bin/ruby -w
# s3-sample-client.rb
require 'S3lib'
# Gather command-line arguments
bucket_name, object_name, object_value = ARGV
unless bucket_name
puts "Usage: #{$0} [bucket name] [object name] [object value]"
exit
end
# Find or create the bucket.
buckets = S3::BucketList.new.get # GET /
bucket = buckets.detect { |b| b.name == bucket_name }
if bucket
puts "Found bucket #{bucket_name}."
else
puts "Could not find bucket #{bucket_name}, creating it."
bucket = S3::Bucket.new(bucket_name)
bucket.put # PUT /{bucket}
end
# Create the object.
object = S3::Object.new(bucket, object_name)
object.metadata['content-type'] = 'text/plain'
object.value = object_value
object.put # PUT /{bucket}/{object}
# For each object in the bucket...
bucket.get[0].each do |o| # GET /{bucket}
# ...print out information about the object.
puts "Name: #{o.name}"
puts "Value: #{o.value}" # GET /{bucket}/{object}
puts "Metadata hash: #{o.metadata.inspect}"
puts
end