hollownest-ruby-aws 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +138 -0
- data/lib/amazon.rb +153 -0
- data/lib/amazon/aws.rb +1339 -0
- data/lib/amazon/aws/cache.rb +141 -0
- data/lib/amazon/aws/search.rb +356 -0
- data/lib/amazon/aws/shoppingcart.rb +504 -0
- data/lib/amazon/locale.rb +102 -0
- data/test/local/cache_files/books.xml +802 -0
- data/test/local/cache_files/electronics.xml +842 -0
- data/test/local/cache_files/movies.xml +1056 -0
- data/test/local/cache_files/music.xml +744 -0
- data/test/local/tc_local_search.rb +31 -0
- data/test/network/tc_amazon.rb +17 -0
- data/test/network/tc_aws.rb +136 -0
- data/test/network/tc_item_search.rb +25 -0
- data/test/network/tc_multiple_operation.rb +70 -0
- data/test/network/tc_operation_request.rb +62 -0
- data/test/network/tc_serialisation.rb +107 -0
- data/test/network/tc_shopping_cart.rb +217 -0
- data/test/network/tc_vehicle_operations.rb +109 -0
- data/test/test_helper.rb +16 -0
- metadata +79 -0
data/README.rdoc
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#--
|
2
|
+
# $Id: README.rdoc,v 1.21 2009/02/20 00:45:17 ianmacd Exp $
|
3
|
+
#++
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# = Ruby/AWS - A Ruby interface to the Amazon Associates Web Services API.
|
7
|
+
#
|
8
|
+
# == Introduction
|
9
|
+
#
|
10
|
+
# Ruby/AWS is a Ruby language library that allows programmatic access to
|
11
|
+
# the popular Amazon Web sites via the AWS v4 API. It is the successor to the
|
12
|
+
# now obsolete Ruby/Amazon.
|
13
|
+
#
|
14
|
+
# In addition to the original
|
15
|
+
# amazon.com[http://www.amazon.com/exec/obidos/redirect-home/calibanorg-20]
|
16
|
+
# site, the local sites
|
17
|
+
# amazon.co.uk[http://www.amazon.co.uk/exec/obidos/redirect-home/caliban-21],
|
18
|
+
# amazon.de[http://www.amazon.de/exec/obidos/redirect-home/calibanorg0a-21],
|
19
|
+
# amazon.fr[http://www.amazon.fr/exec/obidos/redirect-home/caliban08-21],
|
20
|
+
# amazon.ca[http://www.amazon.ca/exec/obidos/redirect-home/caliban-20] and
|
21
|
+
# amazon.co.jp[http://www.amazon.co.jp/exec/obidos/redirect-home/calibanorg-20]
|
22
|
+
# are also supported.
|
23
|
+
#
|
24
|
+
# Although the library is still in development, the AWS v4 API is now more or
|
25
|
+
# less fully supported, with only tiny gaps in the functionality of some
|
26
|
+
# operations.
|
27
|
+
#
|
28
|
+
# The following operations are supported:
|
29
|
+
#
|
30
|
+
# BrowseNodeLookup
|
31
|
+
# CustomerContentLookup
|
32
|
+
# CustomerContentSearch
|
33
|
+
# Help
|
34
|
+
# ItemLookup
|
35
|
+
# ItemSearch
|
36
|
+
# ListLookup
|
37
|
+
# ListSearch
|
38
|
+
# SellerListingLookup
|
39
|
+
# SellerListingSearch
|
40
|
+
# SellerLookup
|
41
|
+
# SimilarityLookup
|
42
|
+
# TagLookup
|
43
|
+
# TransactionLookup
|
44
|
+
# VehiclePartLookup
|
45
|
+
# VehiclePartSearch
|
46
|
+
# VehicleSearch
|
47
|
+
#
|
48
|
+
# Remote shopping-carts are also supported. This adds the following operations:
|
49
|
+
#
|
50
|
+
# CartCreate
|
51
|
+
# CartAdd
|
52
|
+
# CartModify
|
53
|
+
# CartClear
|
54
|
+
# CartGet
|
55
|
+
#
|
56
|
+
# In addition, multiple operations and batch requests are also supported.
|
57
|
+
#
|
58
|
+
# Ruby/AWS also offers advanced features not directly available in the AWS
|
59
|
+
# API, such as the ability to retrieve *all* results pages for a particular
|
60
|
+
# search, rather than having to manually deal with AWS responses of 10 results
|
61
|
+
# per page.
|
62
|
+
#
|
63
|
+
# You can also retrieve product images and optionally overlay them with
|
64
|
+
# percentage discount icons.
|
65
|
+
#
|
66
|
+
# Another advanced feature is the ability to cache responses returned by AWS.
|
67
|
+
# If the cache is used (as it is by default), the results of each unique
|
68
|
+
# query will be cached and used for 24 hours. The cache can be manually
|
69
|
+
# flushed of all or just the expired entries.
|
70
|
+
#
|
71
|
+
# One other useful advanced feature is the ability to determine the
|
72
|
+
# appropriate Amazon locale for a given client, based on its IP address or
|
73
|
+
# host name. This allows you to perform AWS operations using the correct
|
74
|
+
# geographical Amazon site for any given client. German and Austrian clients
|
75
|
+
# can be made to interact with amazon.de, British and Irish clients with
|
76
|
+
# amazon.co.uk, etc.
|
77
|
+
#
|
78
|
+
#
|
79
|
+
# == Installation
|
80
|
+
#
|
81
|
+
# Please see the +INSTALL+ file supplied with the software for details of how
|
82
|
+
# to install Ruby/AWS. You can choose between an installation script and a
|
83
|
+
# RubyGems[http://www.rubygems.org/] installation.
|
84
|
+
#
|
85
|
+
# Note, however, if choosing the gem installation, that whilst Ruby/AWS's
|
86
|
+
# RubyForge UNIX name is now ruby-aaws. The ruby-aws name was taken by
|
87
|
+
# {another project}[http://rubyforge.org/projects/ruby-aws/] and this clash
|
88
|
+
# prevented remote installation of the Ruby/AWS gem.
|
89
|
+
#
|
90
|
+
#
|
91
|
+
# == Prerequisites
|
92
|
+
#
|
93
|
+
# Before you can use this library, you need to obtain an Amazon Web Services
|
94
|
+
# {access key
|
95
|
+
# ID}[https://aws-portal.amazon.com/gp/aws/developer/registration/index.html].
|
96
|
+
#
|
97
|
+
# You should also apply for an {Associates
|
98
|
+
# account}[http://docs.amazonwebservices.com/AWSECommerceService/2009-01-06/GSG/BecominganAssociate.html],
|
99
|
+
# although this isn't strictly necessary. If you do not explicitly provide an
|
100
|
+
# Associates tag in your calls through Ruby/AWS, the tag of the Ruby/AWS
|
101
|
+
# author will be used by default.
|
102
|
+
#
|
103
|
+
#
|
104
|
+
# == See Also
|
105
|
+
#
|
106
|
+
# Ultimately, the way to get the most from this library is to read the AWS
|
107
|
+
# documentation to get a feel for what is possible, and then experiment with
|
108
|
+
# this library to see how the AWS calls are mapped into the Ruby world. You
|
109
|
+
# should also review this library's
|
110
|
+
# RDoc[http://www.ruby-doc.org/core/classes/RDoc.html]
|
111
|
+
# documentation[http://www.caliban.org/ruby/ruby-aws/] as well as the
|
112
|
+
# plain-text +README+ file that came with the archive.
|
113
|
+
#
|
114
|
+
# Additionally, there's a
|
115
|
+
# {mailing-list}[http://www.caliban.org/mailman/listinfo/ruby-aws] available,
|
116
|
+
# where you can discuss all Ruby/AWS-related subjects and issues.
|
117
|
+
#
|
118
|
+
# Please see the Amazon Web Services
|
119
|
+
# documentation[http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=5]
|
120
|
+
# for definitive information on the capabilities and inner workings of the AWS
|
121
|
+
# API.
|
122
|
+
#
|
123
|
+
#
|
124
|
+
# == Download
|
125
|
+
#
|
126
|
+
# Version 0.5.0
|
127
|
+
# === {gzip'ed tar archive}[http://www.caliban.org/files/ruby/ruby-aws-0.5.0.tar.gz]
|
128
|
+
# === {Ruby Gem}[http://www.caliban.org/files/ruby/ruby-aaws-0.5.0.gem]
|
129
|
+
# === {Fedora 9 RPM}[http://www.caliban.org/files/redhat/RPMS/noarch/ruby-aws-0.5.0-1.fc9.noarch.rpm]
|
130
|
+
# === {Fedora 9 doc RPM}[http://www.caliban.org/files/redhat/RPMS/noarch/ruby-aws-doc-0.5.0-1.fc9.noarch.rpm]
|
131
|
+
# === {Fedora 9 source RPM}[http://www.caliban.org/files/redhat/SRPMS/ruby-aws-0.5.0-1.fc9.src.rpm]
|
132
|
+
#
|
133
|
+
#
|
134
|
+
# ---
|
135
|
+
# Author:: Ian Macdonald <mailto:ian@caliban.org>
|
136
|
+
# Version:: 0.5.0
|
137
|
+
# Copyright:: (C) 2008-2009 Ian Macdonald
|
138
|
+
# Licence:: GPL[http://www.gnu.org/copyleft/gpl.html]
|
data/lib/amazon.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# $Id: amazon.rb,v 1.26 2009/01/19 16:45:11 ianmacd Exp $
|
2
|
+
#
|
3
|
+
|
4
|
+
module Amazon
|
5
|
+
|
6
|
+
# A top-level exception container class.
|
7
|
+
#
|
8
|
+
class AmazonError < StandardError; end
|
9
|
+
|
10
|
+
NAME = 'Ruby/Amazon'
|
11
|
+
@@config = {}
|
12
|
+
|
13
|
+
# Prints debugging messages and works like printf, except that it prints
|
14
|
+
# only when Ruby is run with the -d switch.
|
15
|
+
#
|
16
|
+
def Amazon.dprintf(format='', *args)
|
17
|
+
$stderr.printf( format + "\n", *args ) if $DEBUG
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# Encode a string, such that it is suitable for HTTP transmission.
|
22
|
+
#
|
23
|
+
def Amazon.url_encode(string)
|
24
|
+
|
25
|
+
# Shamelessly plagiarised from Wakou Aoyama's cgi.rb.
|
26
|
+
#
|
27
|
+
string.gsub( /([^ a-zA-Z0-9_.-]+)/n ) do
|
28
|
+
'%' + $1.unpack( 'H2' * $1.size ).join( '%' ).upcase
|
29
|
+
end.tr( ' ', '+' )
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Convert a string from CamelCase to ruby_case.
|
34
|
+
#
|
35
|
+
def Amazon.uncamelise(str)
|
36
|
+
# Avoid modifying by reference.
|
37
|
+
#
|
38
|
+
str = str.dup
|
39
|
+
|
40
|
+
# Don't mess with string if all caps.
|
41
|
+
#
|
42
|
+
str.gsub!( /(.+?)(([A-Z][a-z]|[A-Z]+$))/, "\\1_\\2" ) if str =~ /[a-z]/
|
43
|
+
|
44
|
+
# Convert to lower case.
|
45
|
+
#
|
46
|
+
str.downcase
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# A Class for dealing with configuration files, such as
|
51
|
+
# <tt>/etc/amazonrc</tt> and <tt>~/.amazonrc</tt>.
|
52
|
+
#
|
53
|
+
class Config < Hash
|
54
|
+
|
55
|
+
require 'stringio'
|
56
|
+
|
57
|
+
# Exception class for configuration file errors.
|
58
|
+
#
|
59
|
+
class ConfigError < AmazonError; end
|
60
|
+
|
61
|
+
# A configuration may be passed in as a string. Otherwise, the files
|
62
|
+
# <tt>/etc/amazonrc</tt> and <tt>~/.amazonrc</tt> are read if they exist
|
63
|
+
# and are readable.
|
64
|
+
#
|
65
|
+
def initialize(config_str=nil)
|
66
|
+
locale = nil
|
67
|
+
|
68
|
+
if config_str
|
69
|
+
|
70
|
+
# We have been passed a config file as a string.
|
71
|
+
#
|
72
|
+
config_files = [ config_str ]
|
73
|
+
config_class = StringIO
|
74
|
+
|
75
|
+
else
|
76
|
+
|
77
|
+
# Perform the usual search for the system and user config files.
|
78
|
+
#
|
79
|
+
config_files = [ File.join( '', 'etc', 'amazonrc' ) ]
|
80
|
+
|
81
|
+
# Figure out where home is. The locations after HOME are for Windows.
|
82
|
+
# [ruby-core:12347]
|
83
|
+
#
|
84
|
+
home = ENV['AMAZONRCDIR'] ||
|
85
|
+
ENV['HOME'] || ENV['HOMEDRIVE'] + ENV['HOMEPATH'] ||
|
86
|
+
ENV['USERPROFILE']
|
87
|
+
user_rcfile = ENV['AMAZONRCFILE'] || '.amazonrc'
|
88
|
+
|
89
|
+
if home
|
90
|
+
config_files << File.expand_path( File.join( home, user_rcfile ) )
|
91
|
+
end
|
92
|
+
|
93
|
+
config_class = File
|
94
|
+
end
|
95
|
+
|
96
|
+
config_files.each do |cf|
|
97
|
+
|
98
|
+
if config_class == StringIO
|
99
|
+
readable = true
|
100
|
+
else
|
101
|
+
# We must determine whether the file is readable.
|
102
|
+
#
|
103
|
+
readable = File.exists?( cf ) && File.readable?( cf )
|
104
|
+
end
|
105
|
+
|
106
|
+
if readable
|
107
|
+
|
108
|
+
Amazon.dprintf( 'Opening %s ...', cf ) if config_class == File
|
109
|
+
|
110
|
+
config_class.open( cf ) { |f| lines = f.readlines }.each do |line|
|
111
|
+
line.chomp!
|
112
|
+
|
113
|
+
# Skip comments and blank lines.
|
114
|
+
#
|
115
|
+
next if line =~ /^(#|$)/
|
116
|
+
|
117
|
+
Amazon.dprintf( 'Read: %s', line )
|
118
|
+
|
119
|
+
# Determine whether we're entering the subsection of a new locale.
|
120
|
+
#
|
121
|
+
if match = line.match( /^\[(\w+)\]$/ )
|
122
|
+
locale = match[1]
|
123
|
+
Amazon.dprintf( "Config locale is now '%s'.", locale )
|
124
|
+
next
|
125
|
+
end
|
126
|
+
|
127
|
+
# Store these, because we'll probably find a use for these later.
|
128
|
+
#
|
129
|
+
begin
|
130
|
+
match = line.match( /^\s*(\S+)\s*=\s*(['"]?)([^'"]+)(['"]?)/ )
|
131
|
+
key, begin_quote, val, end_quote = match[1, 4]
|
132
|
+
raise ConfigError if begin_quote != end_quote
|
133
|
+
|
134
|
+
rescue NoMethodError, ConfigError
|
135
|
+
raise ConfigError, "bad config line: #{line}"
|
136
|
+
end
|
137
|
+
|
138
|
+
if locale && locale != 'global'
|
139
|
+
self[locale] ||= {}
|
140
|
+
self[locale][key] = val
|
141
|
+
else
|
142
|
+
self[key] = val
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
data/lib/amazon/aws.rb
ADDED
@@ -0,0 +1,1339 @@
|
|
1
|
+
# $Id: aws.rb,v 1.84 2009/02/19 16:01:11 ianmacd Exp $
|
2
|
+
#
|
3
|
+
#:include: ../../README.rdoc
|
4
|
+
|
5
|
+
module Amazon
|
6
|
+
|
7
|
+
module AWS
|
8
|
+
|
9
|
+
require 'uri'
|
10
|
+
require 'amazon'
|
11
|
+
require 'amazon/aws/cache'
|
12
|
+
require 'rexml/document'
|
13
|
+
|
14
|
+
NAME = '%s/%s' % [ Amazon::NAME, 'AWS' ]
|
15
|
+
VERSION = '0.4.4'
|
16
|
+
USER_AGENT = '%s %s' % [ NAME, VERSION ]
|
17
|
+
|
18
|
+
# Default Associate tags to use per locale.
|
19
|
+
#
|
20
|
+
DEF_ASSOC = {
|
21
|
+
'ca' => 'caliban-20',
|
22
|
+
'de' => 'calibanorg0a-21',
|
23
|
+
'fr' => 'caliban08-21',
|
24
|
+
'jp' => 'calibanorg-20',
|
25
|
+
'uk' => 'caliban-21',
|
26
|
+
'us' => 'calibanorg-20'
|
27
|
+
}
|
28
|
+
|
29
|
+
# Service name and API version for AWS. The version of the API used can be
|
30
|
+
# changed via the user configuration file.
|
31
|
+
#
|
32
|
+
SERVICE = { 'Service' => 'AWSECommerceService',
|
33
|
+
'Version' => '2009-01-06'
|
34
|
+
}
|
35
|
+
|
36
|
+
# Maximum number of 301 and 302 HTTP responses to follow, should Amazon
|
37
|
+
# later decide to change the location of the service.
|
38
|
+
#
|
39
|
+
MAX_REDIRECTS = 3
|
40
|
+
|
41
|
+
# Maximum number of results pages that can be retrieved for a given
|
42
|
+
# search operation, using whichever pagination parameter is relevant to
|
43
|
+
# that type of operation.
|
44
|
+
#
|
45
|
+
PAGINATION = {
|
46
|
+
'ItemSearch' => { 'parameter' => 'ItemPage',
|
47
|
+
'max_page' => 400 },
|
48
|
+
'ItemLookup' => { 'paraneter' => 'OfferPage',
|
49
|
+
'max_page' => 100 },
|
50
|
+
'ListLookup' => { 'parameter' => 'ProductPage',
|
51
|
+
'max_page' => 30 },
|
52
|
+
'ListSearch' => { 'parameter' => 'ListPage',
|
53
|
+
'max_page' => 20 },
|
54
|
+
'CustomerContentLookup' => { 'parameter' => 'ReviewPage',
|
55
|
+
'max_page' => 10 },
|
56
|
+
'CustomerContentSearch' => { 'parameter' => 'CustomerPage',
|
57
|
+
'max_page' => 20 },
|
58
|
+
'VehiclePartLookup' => { 'parameter' => 'FitmentPage',
|
59
|
+
'max_page' => 10 }
|
60
|
+
}
|
61
|
+
# N.B. ItemLookup can also use the following two pagination parameters
|
62
|
+
#
|
63
|
+
# max. page
|
64
|
+
# ---------
|
65
|
+
# VariationPage 150
|
66
|
+
# ReviewPage 20
|
67
|
+
|
68
|
+
|
69
|
+
# Exception class for HTTP errors.
|
70
|
+
#
|
71
|
+
class HTTPError < AmazonError; end
|
72
|
+
|
73
|
+
|
74
|
+
# Exception class for faulty batch operations.
|
75
|
+
#
|
76
|
+
class BatchError < AmazonError; end
|
77
|
+
|
78
|
+
|
79
|
+
class Endpoint
|
80
|
+
|
81
|
+
attr_reader :host, :path
|
82
|
+
|
83
|
+
def initialize(endpoint)
|
84
|
+
uri = URI.parse( endpoint )
|
85
|
+
@host = uri.host
|
86
|
+
@path = uri.path
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
ENDPOINT = {
|
91
|
+
'ca' => Endpoint.new( 'http://ecs.amazonaws.ca/onca/xml' ),
|
92
|
+
'de' => Endpoint.new( 'http://ecs.amazonaws.de/onca/xml' ),
|
93
|
+
'fr' => Endpoint.new( 'http://ecs.amazonaws.fr/onca/xml' ),
|
94
|
+
'jp' => Endpoint.new( 'http://ecs.amazonaws.jp/onca/xml' ),
|
95
|
+
'uk' => Endpoint.new( 'http://ecs.amazonaws.co.uk/onca/xml' ),
|
96
|
+
'us' => Endpoint.new( 'http://ecs.amazonaws.com/onca/xml' )
|
97
|
+
}
|
98
|
+
|
99
|
+
# Fetch a page, either from the cache or by HTTP. This is used internally.
|
100
|
+
#
|
101
|
+
def AWS.get_page(request, query) # :nodoc:
|
102
|
+
|
103
|
+
url = ENDPOINT[request.locale].path + query
|
104
|
+
cache_url = ENDPOINT[request.locale].host + url
|
105
|
+
|
106
|
+
# Check for cached page and return that if it's there.
|
107
|
+
#
|
108
|
+
if request.cache && request.cache.cached?( cache_url )
|
109
|
+
body = request.cache.fetch( cache_url )
|
110
|
+
return body if body
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get the existing connection. If there isn't one, force a new one.
|
114
|
+
#
|
115
|
+
conn = request.conn || request.reconnect.conn
|
116
|
+
user_agent = request.user_agent
|
117
|
+
|
118
|
+
Amazon.dprintf( 'Fetching http://%s%s ...', conn.address, url )
|
119
|
+
|
120
|
+
begin
|
121
|
+
response = conn.get( url, { 'user-agent' => user_agent } )
|
122
|
+
|
123
|
+
# If we've pulled and processed a lot of pages from the cache (or
|
124
|
+
# just not passed by here recently), the HTTP connection to the server
|
125
|
+
# will probably have timed out.
|
126
|
+
#
|
127
|
+
rescue Errno::ECONNRESET
|
128
|
+
conn = request.reconnect.conn
|
129
|
+
retry
|
130
|
+
end
|
131
|
+
|
132
|
+
redirects = 0
|
133
|
+
while response.key? 'location'
|
134
|
+
if ( redirects += 1 ) > MAX_REDIRECTS
|
135
|
+
raise HTTPError, "More than #{MAX_REDIRECTS} redirections"
|
136
|
+
end
|
137
|
+
|
138
|
+
old_url = url
|
139
|
+
url = URI.parse( response['location'] )
|
140
|
+
url.scheme = old_url.scheme unless url.scheme
|
141
|
+
url.host = old_url.host unless url.host
|
142
|
+
Amazon.dprintf( 'Following HTTP %s to %s ...', response.code, url )
|
143
|
+
response = Net::HTTP::start( url.host ).
|
144
|
+
get( url.path, { 'user-agent' => user_agent } )
|
145
|
+
end
|
146
|
+
|
147
|
+
if response.code != '200'
|
148
|
+
raise HTTPError, "HTTP response code #{response.code}"
|
149
|
+
end
|
150
|
+
|
151
|
+
# Cache the page if we're using a cache.
|
152
|
+
#
|
153
|
+
if request.cache
|
154
|
+
request.cache.store( cache_url, response.body )
|
155
|
+
end
|
156
|
+
|
157
|
+
response.body
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
def AWS.assemble_query(items) # :nodoc:
|
162
|
+
query = ''
|
163
|
+
|
164
|
+
# We must sort the items into an array to get reproducible ordering
|
165
|
+
# of the query parameters. Otherwise, URL caching would not work. We
|
166
|
+
# must also convert the keys to strings, in case Symbols have been used
|
167
|
+
# as the keys.
|
168
|
+
#
|
169
|
+
items.sort { |a,b| a.to_s <=> b.to_s }.each do |k, v|
|
170
|
+
query << '&%s=%s' % [ k, Amazon.url_encode( v.to_s ) ]
|
171
|
+
end
|
172
|
+
|
173
|
+
# Replace initial ampersand with question-mark.
|
174
|
+
#
|
175
|
+
query[0] = '?'
|
176
|
+
|
177
|
+
query
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
# Everything returned by AWS is an AWSObject.
|
182
|
+
#
|
183
|
+
class AWSObject
|
184
|
+
|
185
|
+
include REXML
|
186
|
+
|
187
|
+
# This method can be used to load AWSObject data previously serialised
|
188
|
+
# by Marshal.dump.
|
189
|
+
#
|
190
|
+
# Example:
|
191
|
+
#
|
192
|
+
# File.open( 'aws.dat' ) { |f| Amazon::AWS::AWSObject.load( f ) }
|
193
|
+
#
|
194
|
+
# Marshal.load cannot be used directly, because subclasses of AWSObject
|
195
|
+
# are dynamically defined as needed when AWS XML responses are parsed.
|
196
|
+
#
|
197
|
+
# Later attempts to load objects instantiated from these classes cause a
|
198
|
+
# problem for Marshal, because it knows nothing of classes that were
|
199
|
+
# dynamically defined by a separate process.
|
200
|
+
#
|
201
|
+
def AWSObject.load(io)
|
202
|
+
begin
|
203
|
+
Marshal.load( io )
|
204
|
+
rescue ArgumentError => ex
|
205
|
+
m = ex.to_s.match( /Amazon::AWS::AWSObject::([^ ]+)/ )
|
206
|
+
const_set( m[1], Class.new( AWSObject ) )
|
207
|
+
|
208
|
+
io.rewind
|
209
|
+
retry
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
# This method can be used to load AWSObject data previously serialised
|
215
|
+
# by YAML.dump.
|
216
|
+
#
|
217
|
+
# Example:
|
218
|
+
#
|
219
|
+
# File.open( 'aws.yaml' ) { |f| Amazon::AWS::AWSObject.yaml_load( f ) }
|
220
|
+
#
|
221
|
+
# The standard YAML.load cannot be used directly, because subclasses of
|
222
|
+
# AWSObject are dynamically defined as needed when AWS XML responses are
|
223
|
+
# parsed.
|
224
|
+
#
|
225
|
+
# Later attempts to load objects instantiated from these classes cause a
|
226
|
+
# problem for YAML, because it knows nothing of classes that were
|
227
|
+
# dynamically defined by a separate process.
|
228
|
+
#
|
229
|
+
def AWSObject.yaml_load(io)
|
230
|
+
io.each do |line|
|
231
|
+
|
232
|
+
# File data is external, so it's deemed unsafe when $SAFE > 0, which
|
233
|
+
# is the case with mod_ruby, for example, where $SAFE == 1.
|
234
|
+
#
|
235
|
+
# YAML data isn't eval'ed or anything dangerous like that, so we
|
236
|
+
# consider it safe to untaint it. If we don't, mod_ruby will complain
|
237
|
+
# when Module#const_defined? is invoked a few lines down from here.
|
238
|
+
#
|
239
|
+
line.untaint
|
240
|
+
|
241
|
+
m = line.match( /Amazon::AWS::AWSObject::([^ ]+)/ )
|
242
|
+
if m
|
243
|
+
cl_name = [ m[1] ]
|
244
|
+
|
245
|
+
# Module#const_defined? takes 2 parameters in Ruby 1.9.
|
246
|
+
#
|
247
|
+
cl_name << false if Object.method( :const_defined? ).arity == -1
|
248
|
+
|
249
|
+
unless AWSObject.const_defined?( *cl_name )
|
250
|
+
AWSObject.const_set( m[1], Class.new( AWSObject ) )
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
io.rewind
|
257
|
+
YAML.load( io )
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
def initialize(op=nil)
|
262
|
+
# The name of this instance variable must never clash with the
|
263
|
+
# uncamelised name of an Amazon tag.
|
264
|
+
#
|
265
|
+
# This is used to store the REXML::Text value of an element, which
|
266
|
+
# exists only when the element contains no children.
|
267
|
+
#
|
268
|
+
@__val__ = nil
|
269
|
+
@__op__ = op if op
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
def method_missing(method, *params)
|
274
|
+
iv = '@' + method.id2name
|
275
|
+
|
276
|
+
if instance_variables.include?( iv )
|
277
|
+
instance_variable_get( iv )
|
278
|
+
elsif instance_variables.include?( iv.to_sym )
|
279
|
+
|
280
|
+
# Ruby 1.9 Object#instance_variables method returns Array of Symbol,
|
281
|
+
# not String.
|
282
|
+
#
|
283
|
+
instance_variable_get( iv.to_sym )
|
284
|
+
else
|
285
|
+
nil
|
286
|
+
end
|
287
|
+
end
|
288
|
+
private :method_missing
|
289
|
+
|
290
|
+
|
291
|
+
def remove_val
|
292
|
+
remove_instance_variable( :@__val__ )
|
293
|
+
end
|
294
|
+
private :remove_val
|
295
|
+
|
296
|
+
|
297
|
+
# Iterator method for cycling through an object's properties and values.
|
298
|
+
#
|
299
|
+
def each # :yields: property, value
|
300
|
+
self.properties.each do |iv|
|
301
|
+
yield iv, instance_variable_get( "@#{iv}" )
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
alias :each_property :each
|
306
|
+
|
307
|
+
|
308
|
+
def inspect # :nodoc:
|
309
|
+
remove_val if instance_variable_defined?( :@__val__ ) && @__val__.nil?
|
310
|
+
str = super
|
311
|
+
str.sub( /@__val__=/, 'value=' ) if str
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
def to_s # :nodoc:
|
316
|
+
if instance_variable_defined?( :@__val__ )
|
317
|
+
return @__val__ if @__val__.is_a?( String )
|
318
|
+
remove_val
|
319
|
+
end
|
320
|
+
|
321
|
+
string = ''
|
322
|
+
|
323
|
+
# Assemble the object's details.
|
324
|
+
#
|
325
|
+
each { |iv, value| string << "%s = %s\n" % [ iv, value ] }
|
326
|
+
|
327
|
+
string
|
328
|
+
end
|
329
|
+
|
330
|
+
alias :to_str :to_s
|
331
|
+
|
332
|
+
|
333
|
+
def to_i # :nodoc:
|
334
|
+
@__val__.to_i
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
def ==(other) # :nodoc:
|
339
|
+
@__val__.to_s == other
|
340
|
+
end
|
341
|
+
|
342
|
+
|
343
|
+
def =~(other) # :nodoc:
|
344
|
+
@__val__.to_s =~ other
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
# This alias makes the ability to determine an AWSObject's properties a
|
349
|
+
# little more intuitive. It's pretty much just an alias for the
|
350
|
+
# inherited <em>Object#instance_variables</em> method, with a little
|
351
|
+
# tidying.
|
352
|
+
#
|
353
|
+
def properties
|
354
|
+
# Make sure we remove the leading @.
|
355
|
+
#
|
356
|
+
iv = instance_variables.collect { |v| v = v[1..-1] }
|
357
|
+
iv.delete( '__val__' )
|
358
|
+
iv
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
# Provide a shortcut down to the data likely to be of most interest.
|
363
|
+
# This method is experimental and may be removed.
|
364
|
+
#
|
365
|
+
def kernel # :nodoc:
|
366
|
+
# E.g. Amazon::AWS::SellerListingLookup -> seller_listing_lookup
|
367
|
+
#
|
368
|
+
stub = Amazon.uncamelise( @__op__.class.to_s.sub( /^.+::/, '' ) )
|
369
|
+
|
370
|
+
# E.g. seller_listing_response
|
371
|
+
#
|
372
|
+
level1 = stub + '_response'
|
373
|
+
|
374
|
+
# E.g. seller_listing
|
375
|
+
#
|
376
|
+
level3 = stub.sub( /_[^_]+$/, '' )
|
377
|
+
|
378
|
+
# E.g. seller_listings
|
379
|
+
#
|
380
|
+
level2 = level3 + 's'
|
381
|
+
|
382
|
+
# E.g.
|
383
|
+
# seller_listing_search_response[0].seller_listings[0].seller_listing
|
384
|
+
#
|
385
|
+
self.instance_variable_get( "@#{level1}" )[0].
|
386
|
+
instance_variable_get( "@#{level2}" )[0].
|
387
|
+
instance_variable_get( "@#{level3}" )
|
388
|
+
end
|
389
|
+
|
390
|
+
|
391
|
+
# Convert an AWSObject to a Hash.
|
392
|
+
#
|
393
|
+
def to_h
|
394
|
+
hash = {}
|
395
|
+
|
396
|
+
each do |iv, value|
|
397
|
+
if value.is_a? AWSObject
|
398
|
+
hash[iv] = value.to_h
|
399
|
+
elsif value.is_a?( AWSArray ) && value.size == 1
|
400
|
+
hash[iv] = value[0]
|
401
|
+
else
|
402
|
+
hash[iv] = value
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
hash
|
407
|
+
end
|
408
|
+
|
409
|
+
|
410
|
+
# Fake the appearance of an AWSObject as a hash. _key_ should be any
|
411
|
+
# attribute of the object and can be a String, Symbol or anything else
|
412
|
+
# that can be converted to a String with to_s.
|
413
|
+
#
|
414
|
+
def [](key)
|
415
|
+
instance_variable_get( "@#{key}" )
|
416
|
+
end
|
417
|
+
|
418
|
+
|
419
|
+
# Recursively walk through an XML tree, starting from _node_. This is
|
420
|
+
# called internally and is not intended for user code.
|
421
|
+
#
|
422
|
+
def walk(node) # :nodoc:
|
423
|
+
|
424
|
+
if node.instance_of?( REXML::Document )
|
425
|
+
walk( node.root )
|
426
|
+
|
427
|
+
elsif node.instance_of?( REXML::Element )
|
428
|
+
name = Amazon.uncamelise( node.name )
|
429
|
+
|
430
|
+
cl_name = [ node.name ]
|
431
|
+
|
432
|
+
# Module#const_defined? takes 2 parameters in Ruby 1.9.
|
433
|
+
#
|
434
|
+
cl_name << false if Object.method( :const_defined? ).arity == -1
|
435
|
+
|
436
|
+
# Create a class for the new element type unless it already exists.
|
437
|
+
#
|
438
|
+
unless AWS::AWSObject.const_defined?( *cl_name )
|
439
|
+
cl = AWS::AWSObject.const_set( node.name, Class.new( AWSObject ) )
|
440
|
+
|
441
|
+
# Give it an accessor for @attrib.
|
442
|
+
#
|
443
|
+
cl.send( :attr_accessor, :attrib )
|
444
|
+
end
|
445
|
+
|
446
|
+
# Instantiate an object in the newly created class.
|
447
|
+
#
|
448
|
+
obj = AWS::AWSObject.const_get( node.name ).new
|
449
|
+
|
450
|
+
sym_name = "@#{name}".to_sym
|
451
|
+
|
452
|
+
if instance_variable_defined?( sym_name)
|
453
|
+
instance_variable_set( sym_name,
|
454
|
+
instance_variable_get( sym_name ) << obj )
|
455
|
+
else
|
456
|
+
instance_variable_set( sym_name, AWSArray.new( [ obj ] ) )
|
457
|
+
end
|
458
|
+
|
459
|
+
if node.has_attributes?
|
460
|
+
obj.attrib = {}
|
461
|
+
node.attributes.each_pair do |a_name, a_value|
|
462
|
+
obj.attrib[a_name.downcase] =
|
463
|
+
a_value.to_s.sub( /^#{a_name}=/, '' )
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
node.children.each { |child| obj.walk( child ) }
|
468
|
+
|
469
|
+
else # REXML::Text
|
470
|
+
@__val__ = node.to_s
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
|
475
|
+
# For objects of class AWSObject::.*Image, fetch the image in question,
|
476
|
+
# optionally overlaying a discount icon for the percentage amount of
|
477
|
+
# _discount_ to the image.
|
478
|
+
#
|
479
|
+
def get(discount=nil)
|
480
|
+
if self.class.to_s =~ /Image$/ && @url
|
481
|
+
url = URI.parse( @url[0] )
|
482
|
+
url.path.sub!( /(\.\d\d\._)/, "\\1PE#{discount}" ) if discount
|
483
|
+
|
484
|
+
# FIXME: All HTTP in Ruby/AWS should go through the same method.
|
485
|
+
#
|
486
|
+
Net::HTTP.start( url.host, url.port ) do |http|
|
487
|
+
http.get( url.path )
|
488
|
+
end.body
|
489
|
+
|
490
|
+
else
|
491
|
+
nil
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
end
|
496
|
+
|
497
|
+
|
498
|
+
# Everything we get back from AWS is transformed into an array. Many of
|
499
|
+
# these, however, have only one element, because the corresponding XML
|
500
|
+
# consists of a parent element containing only a single child element.
|
501
|
+
#
|
502
|
+
# This class consists solely to allow single element arrays to pass a
|
503
|
+
# method call down to their one element, thus obviating the need for lots
|
504
|
+
# of references to <tt>foo[0]</tt> in user code.
|
505
|
+
#
|
506
|
+
# For example, the following:
|
507
|
+
#
|
508
|
+
# items = resp.item_search_response[0].items[0].item
|
509
|
+
#
|
510
|
+
# can be reduced to:
|
511
|
+
#
|
512
|
+
# items = resp.item_search_response.items.item
|
513
|
+
#
|
514
|
+
class AWSArray < Array
|
515
|
+
|
516
|
+
def method_missing(method, *params)
|
517
|
+
self.size == 1 ? self[0].send( method, *params ) : super
|
518
|
+
end
|
519
|
+
private :method_missing
|
520
|
+
|
521
|
+
|
522
|
+
# In the case of a single-element array, return the first element,
|
523
|
+
# converted to a String.
|
524
|
+
#
|
525
|
+
def to_s # :nodoc:
|
526
|
+
self.size == 1 ? self[0].to_s : super
|
527
|
+
end
|
528
|
+
|
529
|
+
alias :to_str :to_s
|
530
|
+
|
531
|
+
|
532
|
+
# In the case of a single-element array, return the first element,
|
533
|
+
# converted to an Integer.
|
534
|
+
#
|
535
|
+
def to_i # :nodoc:
|
536
|
+
self.size == 1 ? self[0].to_i : super
|
537
|
+
end
|
538
|
+
|
539
|
+
|
540
|
+
# In the case of a single-element array, compare the first element with
|
541
|
+
# _other_.
|
542
|
+
#
|
543
|
+
def ==(other) # :nodoc:
|
544
|
+
self.size == 1 ? self[0].to_s == other : super
|
545
|
+
end
|
546
|
+
|
547
|
+
|
548
|
+
# In the case of a single-element array, perform a pattern match on the
|
549
|
+
# first element against _other_.
|
550
|
+
#
|
551
|
+
def =~(other) # :nodoc:
|
552
|
+
self.size == 1 ? self[0].to_s =~ other : super
|
553
|
+
end
|
554
|
+
|
555
|
+
end
|
556
|
+
|
557
|
+
|
558
|
+
# This is the base class of all AWS operations.
|
559
|
+
#
|
560
|
+
class Operation
|
561
|
+
|
562
|
+
# These are the types of AWS operation currently implemented by Ruby/AWS.
|
563
|
+
#
|
564
|
+
OPERATIONS = %w[
|
565
|
+
BrowseNodeLookup CustomerContentLookup CustomerContentSearch
|
566
|
+
Help ItemLookup ItemSearch
|
567
|
+
ListLookup ListSearch SellerListingLookup
|
568
|
+
SellerListingSearch SellerLookup SimilarityLookup
|
569
|
+
TagLookup TransactionLookup VehiclePartLookup
|
570
|
+
VehiclePartSearch VehicleSearch
|
571
|
+
|
572
|
+
CartAdd CartClear CartCreate
|
573
|
+
CartGet CartModify
|
574
|
+
]
|
575
|
+
|
576
|
+
attr_reader :kind
|
577
|
+
attr_accessor :params
|
578
|
+
|
579
|
+
def initialize(parameters)
|
580
|
+
|
581
|
+
op_kind = self.class.to_s.sub( /^.*::/, '' )
|
582
|
+
unless OPERATIONS.include?( op_kind ) || op_kind == 'MultipleOperation'
|
583
|
+
raise "Bad operation: #{op_kind}"
|
584
|
+
end
|
585
|
+
#raise 'Too many parameters' if parameters.size > 10
|
586
|
+
|
587
|
+
@kind = op_kind
|
588
|
+
@params = { 'Operation' => op_kind }.merge( parameters )
|
589
|
+
end
|
590
|
+
|
591
|
+
|
592
|
+
# Group together operations of the same class in a batch request.
|
593
|
+
# _operations_ should be either an operation of the same class as *self*
|
594
|
+
# or an array of such operations.
|
595
|
+
#
|
596
|
+
# If you need to batch operations from different classes, use a
|
597
|
+
# MultipleOperation instead.
|
598
|
+
#
|
599
|
+
# Example:
|
600
|
+
#
|
601
|
+
# is = ItemSearch.new( 'Books', { 'Title' => 'ruby programming' } )
|
602
|
+
# is2 = ItemSearch.new( 'Music', { 'Artist' => 'stranglers' } )
|
603
|
+
# is.batch( is2 )
|
604
|
+
#
|
605
|
+
# Please see MultipleOperation.new for details of a couple of
|
606
|
+
# restrictions that also apply to batched operations.
|
607
|
+
#
|
608
|
+
def batch(*operations)
|
609
|
+
|
610
|
+
# Remove the Operation parameter to avoid batch syntax being applied.
|
611
|
+
# We'll readd it at the end.
|
612
|
+
#
|
613
|
+
op_type = @params.delete( 'Operation' )
|
614
|
+
|
615
|
+
operations.flatten.each do |op|
|
616
|
+
|
617
|
+
unless self.class == op.class
|
618
|
+
raise BatchError, "You can't batch different classes of operation. Use class MultipleOperation."
|
619
|
+
end
|
620
|
+
|
621
|
+
# Remove the Operation parameter.
|
622
|
+
#
|
623
|
+
op.params.delete( 'Operation' )
|
624
|
+
|
625
|
+
# Apply batch syntax.
|
626
|
+
#
|
627
|
+
@params = batch_parameters( @params, op.params )
|
628
|
+
end
|
629
|
+
|
630
|
+
# Reinstate the Operation parameter.
|
631
|
+
#
|
632
|
+
@params.merge!( { 'Operation' => op_type } )
|
633
|
+
end
|
634
|
+
|
635
|
+
|
636
|
+
# Convert parameters to batch format, e.g. ItemSearch.1.Title.
|
637
|
+
#
|
638
|
+
def batch_parameters(params, *b_params) # :nodoc:
|
639
|
+
|
640
|
+
@index ||= 1
|
641
|
+
|
642
|
+
unless b_params.empty?
|
643
|
+
op_str = self.class.to_s.sub( /^.+::/, '' )
|
644
|
+
|
645
|
+
# Fudge the operation string if we're dealing with a shopping cart.
|
646
|
+
#
|
647
|
+
op_str = 'Item' if op_str =~ /^Cart/
|
648
|
+
|
649
|
+
all_parameters = [ params ].concat( b_params )
|
650
|
+
params = {}
|
651
|
+
|
652
|
+
all_parameters.each_with_index do |hash, index|
|
653
|
+
|
654
|
+
# Don't batch an already batched hash.
|
655
|
+
#
|
656
|
+
if ! hash.empty? && hash.to_a[0][0] =~ /^.+\..+\..+$/
|
657
|
+
params = hash
|
658
|
+
next
|
659
|
+
end
|
660
|
+
|
661
|
+
hash.each do |tag, val|
|
662
|
+
shared_param = '%s.%d.%s' % [ op_str, @index + index, tag ]
|
663
|
+
params[shared_param] = val
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
@index += b_params.size
|
668
|
+
|
669
|
+
end
|
670
|
+
|
671
|
+
params
|
672
|
+
end
|
673
|
+
|
674
|
+
end
|
675
|
+
|
676
|
+
|
677
|
+
# This class can be used to merge multiple operations into a single
|
678
|
+
# operation for greater efficiency.
|
679
|
+
#
|
680
|
+
class MultipleOperation < Operation
|
681
|
+
|
682
|
+
# This will allow you to take two Operation objects and combine them to
|
683
|
+
# form a single object, which can then be used to perform a single
|
684
|
+
# request to AWS. This allows for greater efficiency, reducing the
|
685
|
+
# number of requests sent to AWS.
|
686
|
+
#
|
687
|
+
# AWS currently imposes a limit of two combined operations in a multiple
|
688
|
+
# operation.
|
689
|
+
#
|
690
|
+
# <em>operation1</em> and <em>operation2</em> are both objects from a
|
691
|
+
# subclass of Operation, such as ItemSearch, ItemLookup, etc.
|
692
|
+
#
|
693
|
+
# There are currently a few restrictions in the Ruby/AWS implementation
|
694
|
+
# of multiple operations:
|
695
|
+
#
|
696
|
+
# - ResponseGroup objects used when calling AWS::Search::Request#search
|
697
|
+
# apply to both operations. You cannot use a different ResponseGroup
|
698
|
+
# with each operation.
|
699
|
+
#
|
700
|
+
# - One or both operations may have multiple results pages available,
|
701
|
+
# but only the first page is returned. If you need the subsequent
|
702
|
+
# pages, perform the operations separately, not as part of a
|
703
|
+
# MultipleOperation.
|
704
|
+
#
|
705
|
+
# Example:
|
706
|
+
#
|
707
|
+
# is = ItemSearch.new( 'Books', { 'Title' => 'Ruby' } )
|
708
|
+
# il = ItemLookup.new( 'ASIN', { 'ItemId' => 'B0013DZAYO',
|
709
|
+
# 'MerchantId' => 'Amazon' } )
|
710
|
+
# mo = MultipleOperation.new( is, il )
|
711
|
+
#
|
712
|
+
# As you can see, the operations that are combined as a
|
713
|
+
# MultipleOperation do not have to belong to the same class. In the
|
714
|
+
# above example, we compose a multiple operation consisting of an
|
715
|
+
# ItemSearch and an ItemLookup.
|
716
|
+
#
|
717
|
+
# If you want to batch operations belonging to the same class,
|
718
|
+
# Operation#batch provides an alternative.
|
719
|
+
#
|
720
|
+
def initialize(operation1, operation2)
|
721
|
+
|
722
|
+
# Safeguard against changing original Operation objects in place. This
|
723
|
+
# is to protect me, not for user code.
|
724
|
+
#
|
725
|
+
operation1.freeze
|
726
|
+
operation2.freeze
|
727
|
+
|
728
|
+
op_kind = '%s,%s' % [ operation1.kind, operation2.kind ]
|
729
|
+
|
730
|
+
# Duplicate Operation objects and remove their Operation parameter.
|
731
|
+
#
|
732
|
+
op1 = operation1.dup
|
733
|
+
op1.params = op1.params.dup
|
734
|
+
op1.params.delete( 'Operation' )
|
735
|
+
|
736
|
+
op2 = operation2.dup
|
737
|
+
op2.params = op2.params.dup
|
738
|
+
op2.params.delete( 'Operation' )
|
739
|
+
|
740
|
+
if op1.class == op2.class
|
741
|
+
|
742
|
+
# If both operations are of the same type, we combine the parameters
|
743
|
+
# of both.
|
744
|
+
#
|
745
|
+
b_params = op1.batch_parameters( op1.params, op2.params )
|
746
|
+
else
|
747
|
+
|
748
|
+
# We have to convert the parameters to batch format.
|
749
|
+
#
|
750
|
+
bp1 = op1.batch_parameters( op1.params, {} )
|
751
|
+
bp2 = op2.batch_parameters( op2.params, {} )
|
752
|
+
b_params = bp1.merge( bp2 )
|
753
|
+
end
|
754
|
+
|
755
|
+
params = { 'Operation' => op_kind }.merge( b_params )
|
756
|
+
super( params )
|
757
|
+
|
758
|
+
end
|
759
|
+
|
760
|
+
end
|
761
|
+
|
762
|
+
|
763
|
+
# This class of operation aids in finding out about AWS operations and
|
764
|
+
# response groups.
|
765
|
+
#
|
766
|
+
class Help < Operation
|
767
|
+
|
768
|
+
# Return information on AWS operations and response groups.
|
769
|
+
#
|
770
|
+
# For operations, required and optional parameters are returned, along
|
771
|
+
# with information about which response groups the operation can use.
|
772
|
+
#
|
773
|
+
# For response groups, The list of operations that can use that group is
|
774
|
+
# returned, as well as the list of response tags returned by the group.
|
775
|
+
#
|
776
|
+
# _help_type_ is the type of object for which help is being sought, such
|
777
|
+
# as *Operation* or *ResponseGroup*. _about_ is the name of the
|
778
|
+
# operation or response group you need help with, and _parameters_ is an
|
779
|
+
# optional hash of parameters that further refine the request for help.
|
780
|
+
#
|
781
|
+
def initialize(help_type, about, parameters={})
|
782
|
+
super( { 'HelpType' => help_type,
|
783
|
+
'About' => about
|
784
|
+
}.merge( parameters ) )
|
785
|
+
end
|
786
|
+
|
787
|
+
end
|
788
|
+
|
789
|
+
|
790
|
+
# This is the class for the most common type of AWS look-up, an
|
791
|
+
# ItemSearch. This allows you to search for items that match a set of
|
792
|
+
# broad criteria. It returns items for sale by Amazon merchants and most
|
793
|
+
# types of seller.
|
794
|
+
#
|
795
|
+
class ItemSearch < Operation
|
796
|
+
|
797
|
+
# Not all search indices work in all locales. It is the user's
|
798
|
+
# responsibility to ensure that a given index is valid within a given
|
799
|
+
# locale.
|
800
|
+
#
|
801
|
+
# According to the AWS documentation:
|
802
|
+
#
|
803
|
+
# - *All* searches through all indices (but currently exists only in the
|
804
|
+
# *US* locale).
|
805
|
+
# - *Blended* combines Apparel, Automotive, Books, DVD, Electronics,
|
806
|
+
# GourmetFood, Kitchen, Music, PCHardware, PetSupplies, Software,
|
807
|
+
# SoftwareVideoGames, SportingGoods, Tools, Toys, VHS and VideoGames.
|
808
|
+
# - *Merchants* combines all search indices for a merchant given with
|
809
|
+
# MerchantId.
|
810
|
+
# - *Music* combines the Classical, DigitalMusic, and MusicTracks
|
811
|
+
# indices.
|
812
|
+
# - *Video* combines the DVD and VHS search indices.
|
813
|
+
#
|
814
|
+
# Note that {page
|
815
|
+
# 53}[http://docs.amazonwebservices.com/AWSECommerceService/2009-01-06/DG/SearchIndices.html]
|
816
|
+
# of the PDF of the AWS Developer Guide (revision 2009-01-06) contains
|
817
|
+
# an outdated description of *Blended* with too few subindices. {Page
|
818
|
+
# 95}[http://docs.amazonwebservices.com/AWSECommerceService/2009-01-06/DG/CommonItemSearchParameters.html]
|
819
|
+
# of the PDF contains the correct list.
|
820
|
+
#
|
821
|
+
SEARCH_INDICES = %w[
|
822
|
+
All
|
823
|
+
Apparel
|
824
|
+
Automotive
|
825
|
+
Baby
|
826
|
+
Beauty
|
827
|
+
Blended
|
828
|
+
Books
|
829
|
+
Classical
|
830
|
+
DigitalMusic
|
831
|
+
DVD
|
832
|
+
Electronics
|
833
|
+
ForeignBooks
|
834
|
+
GourmetFood
|
835
|
+
Grocery
|
836
|
+
HealthPersonalCare
|
837
|
+
Hobbies
|
838
|
+
HomeGarden
|
839
|
+
Industrial
|
840
|
+
Jewelry
|
841
|
+
KindleStore
|
842
|
+
Kitchen
|
843
|
+
Magazines
|
844
|
+
Merchants
|
845
|
+
Miscellaneous
|
846
|
+
MP3Downloads
|
847
|
+
Music
|
848
|
+
MusicalInstruments
|
849
|
+
MusicTracks
|
850
|
+
OfficeProducts
|
851
|
+
OutdoorLiving
|
852
|
+
PCHardware
|
853
|
+
PetSupplies
|
854
|
+
Photo
|
855
|
+
SilverMerchant
|
856
|
+
Software
|
857
|
+
SoftwareVideoGames
|
858
|
+
SportingGoods
|
859
|
+
Tools
|
860
|
+
Toys
|
861
|
+
VHS
|
862
|
+
Video
|
863
|
+
VideoGames
|
864
|
+
Watches
|
865
|
+
Wireless
|
866
|
+
WirelessAccessories
|
867
|
+
]
|
868
|
+
|
869
|
+
|
870
|
+
# Search AWS for items. _search_index_ must be one of _SEARCH_INDICES_
|
871
|
+
# and _parameters_ is an optional hash of parameters that further refine
|
872
|
+
# the scope of the search.
|
873
|
+
#
|
874
|
+
# Example:
|
875
|
+
#
|
876
|
+
# is = ItemSearch.new( 'Books', { 'Title' => 'ruby programming' } )
|
877
|
+
#
|
878
|
+
# In the above example, we search for books with <b>Ruby Programming</b>
|
879
|
+
# in the title.
|
880
|
+
#
|
881
|
+
def initialize(search_index, parameters)
|
882
|
+
unless SEARCH_INDICES.include? search_index.to_s
|
883
|
+
raise "Invalid search index: #{search_index}"
|
884
|
+
end
|
885
|
+
|
886
|
+
super( { 'SearchIndex' => search_index }.merge( parameters ) )
|
887
|
+
end
|
888
|
+
|
889
|
+
end
|
890
|
+
|
891
|
+
|
892
|
+
# This class of look-up deals with searching for *specific* items by some
|
893
|
+
# uniquely identifying attribute, such as the ASIN (*A*mazon *S*tandard
|
894
|
+
# *I*tem *N*umber).
|
895
|
+
#
|
896
|
+
class ItemLookup < Operation
|
897
|
+
|
898
|
+
# Look up a specific item in the AWS catalogue. _id_type_ is the type of
|
899
|
+
# identifier and _parameters_ is a hash that identifies the item to be
|
900
|
+
# located and narrows the scope of the search.
|
901
|
+
#
|
902
|
+
# Example:
|
903
|
+
#
|
904
|
+
# il = ItemLookup.new( 'ASIN', { 'ItemId' => 'B000AE4QEC'
|
905
|
+
# 'MerchantId' => 'Amazon' } )
|
906
|
+
#
|
907
|
+
# In the above example, we search for an item, based on its ASIN. The
|
908
|
+
# use of _MerchantId_ restricts the offers returned to those for sale
|
909
|
+
# by Amazon (as opposed to third-party sellers).
|
910
|
+
#
|
911
|
+
def initialize(id_type, parameters)
|
912
|
+
super( { 'IdType' => id_type }.merge( parameters ) )
|
913
|
+
end
|
914
|
+
|
915
|
+
end
|
916
|
+
|
917
|
+
|
918
|
+
# Search for items for sale by a particular seller.
|
919
|
+
#
|
920
|
+
class SellerListingSearch < Operation
|
921
|
+
|
922
|
+
# Search for items for sale by a particular seller. _seller_id_ is the
|
923
|
+
# Amazon seller ID and _parameters_ is an optional hash of parameters
|
924
|
+
# that further refine the scope of the search.
|
925
|
+
#
|
926
|
+
# Example:
|
927
|
+
#
|
928
|
+
# sls = SellerListingSearch.new( 'A33J388YD2MWJZ',
|
929
|
+
# { 'Keywords' => 'Killing Joke' } )
|
930
|
+
#
|
931
|
+
# In the above example, we search seller <b>A33J388YD2MWJ</b>'s listings
|
932
|
+
# for items with the keywords <b>Killing Joke</b>.
|
933
|
+
#
|
934
|
+
def initialize(seller_id, parameters)
|
935
|
+
super( { 'SellerId' => seller_id }.merge( parameters ) )
|
936
|
+
end
|
937
|
+
|
938
|
+
end
|
939
|
+
|
940
|
+
|
941
|
+
# Return specified items in a seller's store.
|
942
|
+
#
|
943
|
+
class SellerListingLookup < ItemLookup
|
944
|
+
|
945
|
+
# Look up a specific item for sale by a specific seller. _id_type_ is
|
946
|
+
# the type of identifier and _parameters_ is a hash that identifies the
|
947
|
+
# item to be located and narrows the scope of the search.
|
948
|
+
#
|
949
|
+
# Example:
|
950
|
+
#
|
951
|
+
# sll = SellerListingLookup.new( 'AP8U6Y3PYQ9VO', 'ASIN',
|
952
|
+
# { 'Id' => 'B0009RRRC8' } )
|
953
|
+
#
|
954
|
+
# In the above example, we search seller <b>AP8U6Y3PYQ9VO</b>'s listings
|
955
|
+
# to find items for sale with the ASIN <b>B0009RRRC8</b>.
|
956
|
+
#
|
957
|
+
def initialize(seller_id, id_type, parameters)
|
958
|
+
super( id_type, { 'SellerId' => seller_id }.merge( parameters ) )
|
959
|
+
end
|
960
|
+
|
961
|
+
end
|
962
|
+
|
963
|
+
|
964
|
+
# Return information about a specific seller.
|
965
|
+
#
|
966
|
+
class SellerLookup < Operation
|
967
|
+
|
968
|
+
# Search for the details of a specific seller. _seller_id_ is the Amazon
|
969
|
+
# ID of the seller in question and _parameters_ is an optional hash of
|
970
|
+
# parameters that further refine the scope of the search.
|
971
|
+
#
|
972
|
+
# Example:
|
973
|
+
#
|
974
|
+
# sl = SellerLookup.new( 'A3QFR0K2KCB7EG' )
|
975
|
+
#
|
976
|
+
# In the above example, we look up the details of the seller with ID
|
977
|
+
# <b>A3QFR0K2KCB7EG</b>.
|
978
|
+
#
|
979
|
+
def initialize(seller_id, parameters={})
|
980
|
+
super( { 'SellerId' => seller_id }.merge( parameters ) )
|
981
|
+
end
|
982
|
+
|
983
|
+
end
|
984
|
+
|
985
|
+
|
986
|
+
# Obtain the information an Amazon customer has made public about
|
987
|
+
# themselves.
|
988
|
+
#
|
989
|
+
class CustomerContentLookup < Operation
|
990
|
+
|
991
|
+
# Search for public customer data. _customer_id_ is the unique ID
|
992
|
+
# identifying the customer on Amazon and _parameters_ is an optional
|
993
|
+
# hash of parameters that further refine the scope of the search.
|
994
|
+
#
|
995
|
+
# Example:
|
996
|
+
#
|
997
|
+
# ccl = CustomerContentLookup.new( 'AJDWXANG1SYZP' )
|
998
|
+
#
|
999
|
+
# In the above example, we look up public data about the customer with
|
1000
|
+
# the ID <b>AJDWXANG1SYZP</b>.
|
1001
|
+
#
|
1002
|
+
def initialize(customer_id, parameters={})
|
1003
|
+
super( { 'CustomerId' => customer_id }.merge( parameters ) )
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
|
1009
|
+
# Retrieve basic Amazon customer data.
|
1010
|
+
#
|
1011
|
+
class CustomerContentSearch < Operation
|
1012
|
+
|
1013
|
+
# Retrieve customer information, using an e-mail address or name.
|
1014
|
+
#
|
1015
|
+
# If _customer_id_ contains an '@' sign, it is assumed to be an e-mail
|
1016
|
+
# address. Otherwise, it is assumed to be the customer's name.
|
1017
|
+
#
|
1018
|
+
# Example:
|
1019
|
+
#
|
1020
|
+
# ccs = CustomerContentSearch.new( 'ian@caliban.org' )
|
1021
|
+
#
|
1022
|
+
# In the above example, we look up customer information about
|
1023
|
+
# <b>ian@caliban.org</b>. The *CustomerInfo* response group will return,
|
1024
|
+
# amongst other things, a _customer_id_ property, which can then be
|
1025
|
+
# plugged into CustomerContentLookup to retrieve more detailed customer
|
1026
|
+
# information.
|
1027
|
+
#
|
1028
|
+
def initialize(customer_id)
|
1029
|
+
id = customer_id =~ /@/ ? 'Email' : 'Name'
|
1030
|
+
super( { id => customer_id } )
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
|
1036
|
+
# Find wishlists, registry lists, etc. created by users and placed on
|
1037
|
+
# Amazon. These are items that customers would like to receive as
|
1038
|
+
# presnets.
|
1039
|
+
#
|
1040
|
+
class ListSearch < Operation
|
1041
|
+
|
1042
|
+
# Search for Amazon lists. _list_type_ is the type of list to search for
|
1043
|
+
# and _parameters_ is an optional hash of parameters that narrow the
|
1044
|
+
# scope of the search.
|
1045
|
+
#
|
1046
|
+
# Example:
|
1047
|
+
#
|
1048
|
+
# ls = ListSearch.new( 'WishList', { 'Name' => 'Peter Duff' }
|
1049
|
+
#
|
1050
|
+
# In the above example, we retrieve the wishlist for the Amazon user,
|
1051
|
+
# <b>Peter Duff</b>.
|
1052
|
+
#
|
1053
|
+
def initialize(list_type, parameters)
|
1054
|
+
super( { 'ListType' => list_type }.merge( parameters ) )
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
|
1060
|
+
# Find the details of specific wishlists, registries, etc.
|
1061
|
+
#
|
1062
|
+
class ListLookup < Operation
|
1063
|
+
|
1064
|
+
# Look up and return details about a specific list. _list_id_ is the
|
1065
|
+
# Amazon list ID, _list_type_ is the type of list and _parameters_ is an
|
1066
|
+
# optional hash of parameters that narrow the scope of the search.
|
1067
|
+
#
|
1068
|
+
# Example:
|
1069
|
+
#
|
1070
|
+
# ll = ListLookup.new( '3P722DU4KUPCP', 'Listmania' )
|
1071
|
+
#
|
1072
|
+
# In the above example, a *Listmania* list with the ID
|
1073
|
+
# <b>3P722DU4KUPCP</b> is retrieved from AWS.
|
1074
|
+
#
|
1075
|
+
def initialize(list_id, list_type, parameters={})
|
1076
|
+
super( { 'ListId' => list_id,
|
1077
|
+
'ListType' => list_type
|
1078
|
+
}.merge( parameters ) )
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
|
1084
|
+
# Amazon use browse nodes as a means of organising the millions of items
|
1085
|
+
# in their inventory. An example might be *Carving Knives*. Looking up a
|
1086
|
+
# browse node enables you to determine that group's ancestors and
|
1087
|
+
# descendants.
|
1088
|
+
#
|
1089
|
+
class BrowseNodeLookup < Operation
|
1090
|
+
|
1091
|
+
# Look up and return the details of an Amazon browse node. _node_ is the
|
1092
|
+
# browse node to look up and _parameters_ is an optional hash of
|
1093
|
+
# parameters that further refine the scope of the search. _parameters_
|
1094
|
+
# is currently unused.
|
1095
|
+
#
|
1096
|
+
# Example:
|
1097
|
+
#
|
1098
|
+
# bnl = BrowseNodeLookup.new( '11232', {} )
|
1099
|
+
#
|
1100
|
+
# In the above example, we look up the browse node with the ID
|
1101
|
+
# <b>11232</b>. This is the <b>Social Sciences</b> browse node.
|
1102
|
+
#
|
1103
|
+
def initialize(node, parameters={})
|
1104
|
+
super( { 'BrowseNodeId' => node }.merge( parameters ) )
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
|
1110
|
+
# Similarity look-up is for items similar to others.
|
1111
|
+
#
|
1112
|
+
class SimilarityLookup < Operation
|
1113
|
+
|
1114
|
+
# Look up items similar to _asin_, which can be a single item or an
|
1115
|
+
# array. _parameters_ is an optional hash of parameters that further
|
1116
|
+
# refine the scope of the search.
|
1117
|
+
#
|
1118
|
+
# Example:
|
1119
|
+
#
|
1120
|
+
# sl = SimilarityLookup.new( 'B000051WBE' )
|
1121
|
+
#
|
1122
|
+
# In the above example, we search for items similar to the one with ASIN
|
1123
|
+
# <b>B000051WBE</b>.
|
1124
|
+
#
|
1125
|
+
def initialize(asin, parameters={})
|
1126
|
+
super( { 'ItemId' => asin.to_a.join( ',' ) }.merge( parameters ) )
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
|
1132
|
+
# Search for entities based on user-defined tags. A tag is a descriptive
|
1133
|
+
# word that a customer uses to label entities on Amazon's Web site.
|
1134
|
+
# Entities can be items for sale, Listmania lists, guides, etc.
|
1135
|
+
#
|
1136
|
+
class TagLookup < Operation
|
1137
|
+
|
1138
|
+
# Look up entities based on user-defined tags. _tag_name_ is the tag to
|
1139
|
+
# search on and _parameters_ is an optional hash of parameters that
|
1140
|
+
# further refine the scope of the search.
|
1141
|
+
#
|
1142
|
+
# Example:
|
1143
|
+
#
|
1144
|
+
# tl = TagLookup.new( 'Awful' )
|
1145
|
+
#
|
1146
|
+
# In the example above, we search for entities tagged by users with the
|
1147
|
+
# word *Awful*.
|
1148
|
+
#
|
1149
|
+
def initialize(tag_name, parameters={})
|
1150
|
+
super( { 'TagName' => tag_name }.merge( parameters ) )
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
|
1156
|
+
# Search for information on previously completed purchases.
|
1157
|
+
#
|
1158
|
+
class TransactionLookup < Operation
|
1159
|
+
|
1160
|
+
# Return information on an already completed purchase. _transaction_id_
|
1161
|
+
# is actually the order number that is created when you place an order
|
1162
|
+
# on Amazon.
|
1163
|
+
#
|
1164
|
+
# Example:
|
1165
|
+
#
|
1166
|
+
# tl = TransactionLookup.new( '103-5663398-5028241' )
|
1167
|
+
#
|
1168
|
+
# In the above example, we retrieve the details of order number
|
1169
|
+
# <b>103-5663398-5028241</b>.
|
1170
|
+
#
|
1171
|
+
def initialize(transaction_id)
|
1172
|
+
super( { 'TransactionId' => transaction_id } )
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
|
1178
|
+
# Look up individual vehicle parts.
|
1179
|
+
#
|
1180
|
+
class VehiclePartLookup < Operation
|
1181
|
+
|
1182
|
+
# Look up a particular vehicle part. _item_id_ is the ASIN of the part
|
1183
|
+
# in question and _parameters_ is an optional hash of parameters that
|
1184
|
+
# further refine the scope of the search.
|
1185
|
+
#
|
1186
|
+
# Although the _item_id_ alone is enough to locate the part, providing
|
1187
|
+
# _parameters_ can be useful in determining whether the part looked up
|
1188
|
+
# is a fit for a particular vehicle type, as with the *VehiclePartFit*
|
1189
|
+
# response group.
|
1190
|
+
#
|
1191
|
+
# Example:
|
1192
|
+
#
|
1193
|
+
# vpl = VehiclePartLookup.new( 'B000C1ZLI8',
|
1194
|
+
# { 'Year' => 2008,
|
1195
|
+
# 'MakeId' => 73,
|
1196
|
+
# 'ModelId' => 6039,
|
1197
|
+
# 'TrimId' => 20 } )
|
1198
|
+
#
|
1199
|
+
# Here, we search for a <b>2008</b> model *Audi* <b>R8</b> with *Base*
|
1200
|
+
# trim. The required Ids can be found using VehiclePartSearch.
|
1201
|
+
#
|
1202
|
+
def initialize(item_id, parameters={})
|
1203
|
+
super( { 'ItemId' => item_id }.merge( parameters ) )
|
1204
|
+
end
|
1205
|
+
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
|
1209
|
+
# Search for parts for a given vehicle.
|
1210
|
+
#
|
1211
|
+
class VehiclePartSearch < Operation
|
1212
|
+
|
1213
|
+
# Find parts for a given _year_, _make_id_ and _model_id_ of vehicle.
|
1214
|
+
# _parameters_ is an optional hash of parameters that further refine the
|
1215
|
+
# scope of the search.
|
1216
|
+
#
|
1217
|
+
# Example:
|
1218
|
+
#
|
1219
|
+
# vps = VehiclePartSearch.new( 2008, 73, 6039,
|
1220
|
+
# { 'TrimId' => 20,
|
1221
|
+
# 'EngineId' => 8914 } )
|
1222
|
+
#
|
1223
|
+
# In this example, we look for parts that will fit a <b>2008</b> model
|
1224
|
+
# *Audi* <b>R8</b> with *Base* trim and a <b>4.2L V8 Gas DOHC
|
1225
|
+
# Distributorless Naturally Aspirated Bosch Motronic Electronic FI
|
1226
|
+
# MFI</b> engine.
|
1227
|
+
#
|
1228
|
+
# Note that pagination of VehiclePartSearch results is not currently
|
1229
|
+
# supported.
|
1230
|
+
#
|
1231
|
+
# Use VehicleSearch to learn the MakeId and ModelId of the vehicle in
|
1232
|
+
# which you are interested.
|
1233
|
+
#
|
1234
|
+
def initialize(year, make_id, model_id, parameters={})
|
1235
|
+
super( { 'Year' => year,
|
1236
|
+
'MakeId' => make_id,
|
1237
|
+
'ModelId' => model_id }.merge( parameters ) )
|
1238
|
+
end
|
1239
|
+
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
|
1243
|
+
# Search for vehicles.
|
1244
|
+
#
|
1245
|
+
class VehicleSearch < Operation
|
1246
|
+
|
1247
|
+
# Search for vehicles, based on one or more of the following
|
1248
|
+
# _parameters_: Year, MakeId, ModelId and TrimId.
|
1249
|
+
#
|
1250
|
+
# This method is best used iteratively. For example, first search on
|
1251
|
+
# year with a response group of *VehicleMakes* to return all makes for
|
1252
|
+
# that year.
|
1253
|
+
#
|
1254
|
+
# Next, search on year and make with a response group of *VehicleModels*
|
1255
|
+
# to find all models for that year and make.
|
1256
|
+
#
|
1257
|
+
# Then, search on year, make and model with a response group of
|
1258
|
+
# *VehicleTrims* to find all trim packages for that year, make and model.
|
1259
|
+
#
|
1260
|
+
# Finally, if required, search on year, make, model and trim package
|
1261
|
+
# with a response group of *VehicleOptions* to find all vehicle options
|
1262
|
+
# for that year, make, model and trim package.
|
1263
|
+
#
|
1264
|
+
# Example:
|
1265
|
+
#
|
1266
|
+
# vs = VehicleSearch.new( { 'Year' => 2008,
|
1267
|
+
# 'MakeId' => 20,
|
1268
|
+
# 'ModelId' => 6039,
|
1269
|
+
# 'TrimId' => 20 } )
|
1270
|
+
#
|
1271
|
+
# In this example, we search for <b>2008 Audi R8</b> vehicles with a
|
1272
|
+
# *Base* trim package. Used with the *VehicleOptions* response group,
|
1273
|
+
# a list of vehicle options would be returned.
|
1274
|
+
#
|
1275
|
+
def initialize(parameters={})
|
1276
|
+
super
|
1277
|
+
end
|
1278
|
+
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
# Response groups determine which data pertaining to the item(s) being
|
1282
|
+
# sought is returned. They can strongly influence the amount of data
|
1283
|
+
# returned, so you should always use the smallest response group(s)
|
1284
|
+
# containing the data of interest to you, to avoid masses of unnecessary
|
1285
|
+
# data being returned.
|
1286
|
+
#
|
1287
|
+
class ResponseGroup
|
1288
|
+
|
1289
|
+
attr_reader :list, :params
|
1290
|
+
|
1291
|
+
# Define a set of one or more response groups to be applied to items
|
1292
|
+
# retrieved by an AWS operation.
|
1293
|
+
#
|
1294
|
+
# If no response groups are given in _rg_ when instantiating an object,
|
1295
|
+
# *Small* will be used by default.
|
1296
|
+
#
|
1297
|
+
# Example:
|
1298
|
+
#
|
1299
|
+
# rg = ResponseGroup.new( 'Medium', 'Offers', 'Reviews' )
|
1300
|
+
#
|
1301
|
+
def initialize(*rg)
|
1302
|
+
rg << 'Small' if rg.empty?
|
1303
|
+
@list = rg
|
1304
|
+
@params = { 'ResponseGroup' => @list.join( ',' ) }
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
|
1310
|
+
# All dynamically generated exceptions occur within this namespace.
|
1311
|
+
#
|
1312
|
+
module Error
|
1313
|
+
|
1314
|
+
# The base exception class for errors that result from AWS operations.
|
1315
|
+
# Classes for these are dynamically generated as subclasses of this one.
|
1316
|
+
#
|
1317
|
+
class AWSError < AmazonError; end
|
1318
|
+
|
1319
|
+
def Error.exception(xml)
|
1320
|
+
err_class = xml.elements['Code'].text.sub( /^AWS.*\./, '' )
|
1321
|
+
err_msg = xml.elements['Message'].text
|
1322
|
+
|
1323
|
+
# Dynamically define a new exception class for this class of error,
|
1324
|
+
# unless it already exists.
|
1325
|
+
#
|
1326
|
+
unless Amazon::AWS::Error.const_defined?( err_class )
|
1327
|
+
Amazon::AWS::Error.const_set( err_class, Class.new( AWSError ) )
|
1328
|
+
end
|
1329
|
+
|
1330
|
+
# Generate and return a new exception from the relevant class.
|
1331
|
+
#
|
1332
|
+
Amazon::AWS::Error.const_get( err_class ).new( err_msg )
|
1333
|
+
end
|
1334
|
+
|
1335
|
+
end
|
1336
|
+
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
end
|