ruby-paa 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/example/batch_operation +28 -0
  2. data/example/browse_node_lookup1 +46 -0
  3. data/example/customer_content_lookup1 +27 -0
  4. data/example/customer_content_search1 +21 -0
  5. data/example/example1 +78 -0
  6. data/example/help1 +24 -0
  7. data/example/item_lookup1 +54 -0
  8. data/example/item_lookup2 +56 -0
  9. data/example/item_search1 +30 -0
  10. data/example/item_search2 +37 -0
  11. data/example/item_search3 +23 -0
  12. data/example/list_lookup1 +29 -0
  13. data/example/list_search1 +31 -0
  14. data/example/multiple_operation1 +69 -0
  15. data/example/seller_listing_lookup1 +30 -0
  16. data/example/seller_listing_search1 +29 -0
  17. data/example/seller_lookup1 +45 -0
  18. data/example/shopping_cart1 +42 -0
  19. data/example/similarity_lookup1 +48 -0
  20. data/example/tag_lookup1 +34 -0
  21. data/example/transaction_lookup1 +25 -0
  22. data/example/vehicle_search +22 -0
  23. data/lib/ruby-paa.rb +165 -0
  24. data/lib/ruby-paa/aws.rb +1489 -0
  25. data/lib/ruby-paa/aws/cache.rb +141 -0
  26. data/lib/ruby-paa/aws/search.rb +463 -0
  27. data/lib/ruby-paa/aws/shoppingcart.rb +536 -0
  28. data/lib/ruby-paa/locale.rb +102 -0
  29. data/test/setup.rb +56 -0
  30. data/test/tc_amazon.rb +20 -0
  31. data/test/tc_aws.rb +160 -0
  32. data/test/tc_browse_node_lookup.rb +49 -0
  33. data/test/tc_customer_content_lookup.rb +49 -0
  34. data/test/tc_help.rb +44 -0
  35. data/test/tc_item_lookup.rb +47 -0
  36. data/test/tc_item_search.rb +105 -0
  37. data/test/tc_list_lookup.rb +60 -0
  38. data/test/tc_list_search.rb +44 -0
  39. data/test/tc_multiple_operation.rb +375 -0
  40. data/test/tc_operation_request.rb +64 -0
  41. data/test/tc_seller_listing_lookup.rb +47 -0
  42. data/test/tc_seller_listing_search.rb +55 -0
  43. data/test/tc_seller_lookup.rb +44 -0
  44. data/test/tc_serialisation.rb +107 -0
  45. data/test/tc_shopping_cart.rb +214 -0
  46. data/test/tc_similarity_lookup.rb +48 -0
  47. data/test/tc_tag_lookup.rb +24 -0
  48. data/test/tc_transaction_lookup.rb +24 -0
  49. data/test/tc_vehicle_operations.rb +118 -0
  50. data/test/ts_aws.rb +24 -0
  51. metadata +132 -0
@@ -0,0 +1,141 @@
1
+ # $Id: cache.rb,v 1.8 2008/06/10 06:33:46 ianmacd Exp $
2
+ #
3
+
4
+ module Amazon
5
+
6
+ module AWS
7
+
8
+ # This class provides a simple results caching system for operations
9
+ # performed by AWS.
10
+ #
11
+ # To use it, set _cache_ to *true* in either <tt>/etc/amazonrc</tt> or
12
+ # <tt>~/.amazonrc</tt>.
13
+ #
14
+ # By default, the cache directory used is <tt>/tmp/amazon</tt>, but this
15
+ # can be changed by defining _cache_dir_ in either <tt>/etc/amazonrc</tt>
16
+ # or <tt>~/.amazonrc</tt>.
17
+ #
18
+ # When a cache is used, Ruby/AWS will check the cache directory for a
19
+ # recent copy of a response to the exact operation that you are
20
+ # performing. If found, the cached response will be returned instead of
21
+ # the request being forwarded to the AWS servers for processing. If no
22
+ # (recent) copy is found, the request will be forwarded to the AWS servers
23
+ # as usual. Recency is defined here as less than 24 hours old.
24
+ #
25
+ class Cache
26
+
27
+ require 'fileutils'
28
+
29
+ begin
30
+ require 'md5'
31
+ rescue LoadError
32
+ # Ruby 1.9 has moved MD5.
33
+ #
34
+ require 'digest/md5'
35
+ end
36
+
37
+ # Exception class for bad cache paths.
38
+ #
39
+ class PathError < StandardError; end
40
+
41
+ # Length of one day in seconds
42
+ #
43
+ ONE_DAY = 86400 # :nodoc:
44
+
45
+ # Age in days below which to consider cache files valid.
46
+ #
47
+ MAX_AGE = 1.0
48
+
49
+ # Default cache location.
50
+ #
51
+ DEFAULT_CACHE_DIR = '/tmp/amazon'
52
+
53
+ attr_reader :path
54
+
55
+ def initialize(path=DEFAULT_CACHE_DIR)
56
+ path ||= DEFAULT_CACHE_DIR
57
+
58
+ ::FileUtils::mkdir_p( path ) unless File.exists? path
59
+
60
+ unless File.directory? path
61
+ raise PathError, "cache path #{path} is not a directory"
62
+ end
63
+
64
+ unless File.readable? path
65
+ raise PathError, "cache path #{path} is not readable"
66
+ end
67
+
68
+ unless File.writable? path
69
+ raise PathError, "cache path #{path} is not writable"
70
+ end
71
+
72
+ @path = path
73
+ end
74
+
75
+
76
+ # Determine whether or not the the response to a given URL is cached.
77
+ # Returns *true* or *false*.
78
+ #
79
+ def cached?(url)
80
+ digest = Digest::MD5.hexdigest( url )
81
+
82
+ cache_files = Dir.glob( File.join( @path, '*' ) ).map do |d|
83
+ File.basename( d )
84
+ end
85
+
86
+ return cache_files.include?( digest ) &&
87
+ ( Time.now - File.mtime( File.join( @path, digest ) ) ) /
88
+ ONE_DAY <= MAX_AGE
89
+ end
90
+
91
+
92
+ # Retrieve the cached response associated with _url_.
93
+ #
94
+ def fetch(url)
95
+ digest = Digest::MD5.hexdigest( url )
96
+ cache_file = File.join( @path, digest )
97
+
98
+ return nil unless File.exist? cache_file
99
+
100
+ Amazon.dprintf( 'Fetching %s from cache...', digest )
101
+ File.open( File.join( cache_file ) ).readlines.to_s
102
+ end
103
+
104
+
105
+ # Cache the data from _contents_ and associate it with _url_.
106
+ #
107
+ def store(url, contents)
108
+ digest = Digest::MD5.hexdigest( url )
109
+ cache_file = File.join( @path, digest )
110
+
111
+ Amazon.dprintf( 'Caching %s...', digest )
112
+ File.open( cache_file, 'w' ) { |f| f.puts contents }
113
+ end
114
+
115
+
116
+ # This method flushes all files from the cache directory specified
117
+ # in the object's <i>@path</i> variable.
118
+ #
119
+ def flush_all
120
+ FileUtils.rm Dir.glob( File.join( @path, '*' ) )
121
+ end
122
+
123
+
124
+ # This method flushes expired files from the cache directory specified
125
+ # in the object's <i>@path</i> variable.
126
+ #
127
+ def flush_expired
128
+ now = Time.now
129
+
130
+ expired_files = Dir.glob( File.join( @path, '*' ) ).find_all do |f|
131
+ ( now - File.mtime( f ) ) / ONE_DAY > MAX_AGE
132
+ end
133
+
134
+ FileUtils.rm expired_files
135
+ end
136
+
137
+ end
138
+
139
+ end
140
+
141
+ end
@@ -0,0 +1,463 @@
1
+ # $Id: search.rb,v 1.49 2010/03/19 19:28:19 ianmacd Exp $
2
+ #
3
+
4
+ module Amazon
5
+
6
+ module AWS
7
+
8
+ require 'ruby-paa/aws'
9
+ require 'net/http'
10
+ require 'rexml/document'
11
+ require 'openssl'
12
+
13
+ # Load this library with:
14
+ #
15
+ # require 'amazon/aws/search'
16
+ #
17
+ module Search
18
+
19
+ class Request
20
+
21
+ include REXML
22
+
23
+ # Exception class for bad access key ID.
24
+ #
25
+ class AccessKeyIdError < Amazon::AWS::Error::AWSError; end
26
+
27
+ # Exception class for bad locales.
28
+ #
29
+ class LocaleError < Amazon::AWS::Error::AWSError; end
30
+
31
+ # Do we have support for the SHA-256 Secure Hash Algorithm?
32
+ #
33
+ # Note that Module#constants returns Strings in Ruby 1.8 and Symbols
34
+ # in 1.9.
35
+ #
36
+ DIGEST_SUPPORT = OpenSSL::Digest.constants.include?( 'SHA256' ) || OpenSSL::Digest.constants.include?( :SHA256 )
37
+
38
+ # Requests are authenticated using the SHA-256 Secure Hash Algorithm.
39
+ #
40
+ DIGEST = OpenSSL::Digest::Digest.new( 'sha256' ) if DIGEST_SUPPORT
41
+
42
+ attr_reader :conn, :config, :locale, :query, :user_agent
43
+ attr_writer :cache
44
+ attr_accessor :encoding
45
+
46
+ # This method is used to generate an AWS search request object.
47
+ #
48
+ # _key_id_ is your AWS {access key
49
+ # ID}[https://aws-portal.amazon.com/gp/aws/developer/registration/index.html].
50
+ # Note that your secret key, used for signing requests, can be
51
+ # specified only in your <tt>~/.amazonrc</tt> configuration file.
52
+ #
53
+ # _associate_ is your
54
+ # Associates[http://docs.amazonwebservices.com/AWSECommerceService/2009-11-01/GSG/BecominganAssociate.html]
55
+ # tag (if any), _locale_ is the locale in which you which to work
56
+ # (*us* for amazon.com[http://www.amazon.com/], *uk* for
57
+ # amazon.co.uk[http://www.amazon.co.uk], etc.), _cache_ is whether or
58
+ # not you wish to utilise a response cache, and _user_agent_ is the
59
+ # client name to pass when performing calls to AWS. By default,
60
+ # _user_agent_ will be set to a string identifying the Ruby/AWS
61
+ # library and its version number.
62
+ #
63
+ # _locale_ and _cache_ can also be set later, if you wish to change
64
+ # the current behaviour.
65
+ #
66
+ # Example:
67
+ #
68
+ # req = Request.new( '0Y44V8FAFNM119CX4TR2', 'calibanorg-20' )
69
+ #
70
+ def initialize(key_id=nil, associate=nil, locale=nil, cache=nil, user_agent=USER_AGENT)
71
+
72
+ @config ||= Amazon::Config.new
73
+
74
+ def_locale = locale
75
+ locale = 'us' unless locale
76
+ locale.downcase!
77
+
78
+ key_id ||= @config['key_id']
79
+ cache = @config['cache'] if cache.nil?
80
+
81
+ # Take locale from config file if no locale was passed to method.
82
+ #
83
+ if @config.key?( 'locale' ) && ! def_locale
84
+ locale = @config['locale']
85
+ end
86
+ validate_locale( locale )
87
+
88
+ if key_id.nil?
89
+ raise AccessKeyIdError, 'key_id may not be nil'
90
+ end
91
+
92
+ @key_id = key_id
93
+ @tag = associate || @config['associate'] || DEF_ASSOC[locale]
94
+ @user_agent = user_agent
95
+ @cache = unless cache == 'false' || cache == false
96
+ Amazon::AWS::Cache.new( @config['cache_dir'] )
97
+ else
98
+ nil
99
+ end
100
+
101
+ # Set the following two variables from the config file. Will be
102
+ # *nil* if not present in config file.
103
+ #
104
+ @api = @config['api']
105
+ @encoding = @config['encoding']
106
+
107
+ self.locale = locale
108
+ end
109
+
110
+
111
+ # Assign a new locale. If the locale we're coming from is using the
112
+ # default Associate ID for that locale, then we use the new locale's
113
+ # default ID, too.
114
+ #
115
+ def locale=(l) # :nodoc:
116
+ old_locale = @locale ||= nil
117
+ @locale = validate_locale( l )
118
+
119
+ # Use the new locale's default ID if the ID currently in use is the
120
+ # current locale's default ID.
121
+ #
122
+ if @tag == Amazon::AWS::DEF_ASSOC[old_locale]
123
+ @tag = Amazon::AWS::DEF_ASSOC[@locale]
124
+ end
125
+
126
+ if @config.key?( @locale ) && @config[@locale].key?( 'associate' )
127
+ @tag = @config[@locale]['associate']
128
+ end
129
+
130
+ # We must now set up a new HTTP connection to the correct server for
131
+ # this locale, unless the same server is used for both.
132
+ #
133
+ unless Amazon::AWS::ENDPOINT[@locale] ==
134
+ Amazon::AWS::ENDPOINT[old_locale]
135
+ #connect( @locale )
136
+ @conn = nil
137
+ end
138
+ end
139
+
140
+
141
+ # If @cache has simply been assigned *true* at some point in time,
142
+ # assign a proper cache object to it when it is referenced. Otherwise,
143
+ # just return its value.
144
+ #
145
+ def cache # :nodoc:
146
+ if @cache == true
147
+ @cache = Amazon::AWS::Cache.new( @config['cache_dir'] )
148
+ else
149
+ @cache
150
+ end
151
+ end
152
+
153
+
154
+ # Verify the validity of a locale string. _l_ is the locale string.
155
+ #
156
+ def validate_locale(l)
157
+ unless Amazon::AWS::ENDPOINT.has_key? l
158
+ raise LocaleError, "invalid locale: #{l}"
159
+ end
160
+ l
161
+ end
162
+ private :validate_locale
163
+
164
+
165
+ # Return an HTTP connection for the current _locale_.
166
+ #
167
+ def connect(locale)
168
+ if ENV.key? 'http_proxy'
169
+ uri = URI.parse( ENV['http_proxy'] )
170
+ proxy_user = proxy_pass = nil
171
+ proxy_user, proxy_pass = uri.userinfo.split( /:/ ) if uri.userinfo
172
+ @conn = Net::HTTP::Proxy( uri.host, uri.port, proxy_user,
173
+ proxy_pass ).start(
174
+ Amazon::AWS::ENDPOINT[locale].host )
175
+ else
176
+ @conn = Net::HTTP::start( Amazon::AWS::ENDPOINT[locale].host )
177
+ end
178
+ end
179
+ private :connect
180
+
181
+
182
+ # Reconnect to the server if our connection has been lost (due to a
183
+ # time-out, etc.).
184
+ #
185
+ def reconnect # :nodoc:
186
+ connect( self.locale )
187
+ self
188
+ end
189
+
190
+
191
+ # This method checks for errors in an XML response returned by AWS.
192
+ # _xml_ is the XML node below which to search.
193
+ #
194
+ def error_check(xml)
195
+ if ! xml.nil? && xml = xml.elements['Errors/Error']
196
+ raise Amazon::AWS::Error.exception( xml )
197
+ end
198
+ end
199
+ private :error_check
200
+
201
+
202
+ # Add a timestamp to a request object's query string.
203
+ #
204
+ def timestamp # :nodoc:
205
+ @query << '&Timestamp=%s' %
206
+ [ Amazon.url_encode(
207
+ Time.now.utc.strftime( '%Y-%m-%dT%H:%M:%SZ' ) ) ]
208
+ end
209
+ private :timestamp
210
+
211
+
212
+ # Add a signature to a request object's query string. This implicitly
213
+ # also adds a timestamp.
214
+ #
215
+ def sign # :nodoc:
216
+
217
+ return false unless DIGEST_SUPPORT
218
+
219
+ timestamp
220
+ params = @query[1..-1].split( '&' ).sort.join( '&' )
221
+
222
+ sign_str = "GET\n%s\n%s\n%s" % [ ENDPOINT[@locale].host,
223
+ ENDPOINT[@locale].path,
224
+ params ]
225
+
226
+ Amazon.dprintf( 'Calculating SHA256 HMAC of "%s"...', sign_str )
227
+
228
+ hmac = OpenSSL::HMAC.digest( DIGEST,
229
+ @config['secret_key_id'],
230
+ sign_str )
231
+ Amazon.dprintf( 'SHA256 HMAC is "%s"', hmac.inspect )
232
+
233
+ base64_hmac = [ hmac ].pack( 'm' ).chomp
234
+ Amazon.dprintf( 'Base64-encoded HMAC is "%s".', base64_hmac )
235
+
236
+ signature = Amazon.url_encode( base64_hmac )
237
+
238
+ params << '&Signature=%s' % [ signature ]
239
+ @query = '?' + params
240
+
241
+ true
242
+ end
243
+
244
+
245
+ # Perform a search of the AWS database, returning an AWSObject.
246
+ #
247
+ # _operation_ is an object of a subclass of _Operation_, such as
248
+ # _ItemSearch_, _ItemLookup_, etc. It may also be a _MultipleOperation_
249
+ # object.
250
+ #
251
+ # In versions of Ruby/AWS up to prior to 0.8.0, the second parameter to
252
+ # this method was _response_group_. This way of passing response
253
+ # groups has been deprecated since 0.7.0 and completely removed in
254
+ # 0.8.0. To pair a set of response groups with an operation, assign
255
+ # directly to the operation's @response_group attribute.
256
+ #
257
+ # _nr_pages_ is the number of results pages to return. It defaults to
258
+ # <b>1</b>. If a higher number is given, pages 1 to _nr_pages_ will be
259
+ # returned. If the special value <b>:ALL_PAGES</b> is given, all
260
+ # results pages will be returned.
261
+ #
262
+ # Note that _ItemLookup_ operations can use several different
263
+ # pagination parameters. An _ItemLookup_ will typically return just
264
+ # one results page containing a single product, but <b>:ALL_PAGES</b>
265
+ # can still be used to apply the _OfferPage_ parameter to paginate
266
+ # through multiple pages of offers.
267
+ #
268
+ # Similarly, a single product may have multiple pages of reviews
269
+ # available. In such a case, it is up to the user to manually supply
270
+ # the _ReviewPage_ parameter and an appropriate value.
271
+ #
272
+ # In the same vein, variations can be returned by using the
273
+ # _VariationPage_ parameter.
274
+ #
275
+ # The pagination parameters supported by each type of operation,
276
+ # together with the maximum page number that can be retrieved for each
277
+ # type of data, are # documented in the AWS Developer's Guide:
278
+ #
279
+ # http://docs.amazonwebservices.com/AWSECommerceService/2009-11-01/DG/index.html?MaximumNumberofPages.html
280
+ #
281
+ # The pagination parameter used by <b>:ALL_PAGES</b> can be looked up
282
+ # in the Amazon::AWS::PAGINATION hash.
283
+ #
284
+ # If _operation_ is of class _MultipleOperation_, the operations
285
+ # encapsulated within will return only the first page of results,
286
+ # regardless of whether a higher number of pages is requested.
287
+ #
288
+ # If a block is passed to this method, each successive page of results
289
+ # will be yielded to the block.
290
+ #
291
+ def search(operation, nr_pages=1)
292
+ parameters = Amazon::AWS::SERVICE.
293
+ merge( { 'AWSAccessKeyId' => @key_id,
294
+ 'AssociateTag' => @tag } ).
295
+ merge( operation.query_parameters )
296
+
297
+ if nr_pages.is_a? Amazon::AWS::ResponseGroup
298
+ raise ObsolescenceError, 'Request#search method no longer accepts response_group parameter.'
299
+ end
300
+
301
+ # Pre-0.8.0 user code may have passed *nil* as the second parameter,
302
+ # in order to use the @response_group of the operation.
303
+ #
304
+ nr_pages ||= 1
305
+
306
+ # Check to see whether a particular version of the API has been
307
+ # requested. If so, overwrite Version with the new value.
308
+ #
309
+ parameters.merge!( { 'Version' => @api } ) if @api
310
+
311
+ @query = Amazon::AWS.assemble_query( parameters, @encoding )
312
+ page = Amazon::AWS.get_page( self )
313
+
314
+ # Ruby 1.9 needs to know that the page is UTF-8, not ASCII-8BIT.
315
+ #
316
+ page.force_encoding( 'utf-8' ) if RUBY_VERSION >= '1.9.0'
317
+
318
+ doc = Document.new( page )
319
+
320
+ # Some errors occur at the very top level of the XML. For example,
321
+ # when no Operation parameter is given. This should not be possible
322
+ # with user code, but occurred during debugging of this library.
323
+ #
324
+ error_check( doc )
325
+
326
+ # Another possible error results in a document containing nothing
327
+ # but <Result>Internal Error</Result>. This occurs when a specific
328
+ # version of the AWS API is requested, in combination with an
329
+ # operation that did not yet exist in that version of the API.
330
+ #
331
+ # For example:
332
+ #
333
+ # http://ecs.amazonaws.com/onca/xml?AWSAccessKeyId=foo&Operation=VehicleSearch&Year=2008&ResponseGroup=VehicleMakes&Service=AWSECommerceService&Version=2008-03-03
334
+ #
335
+ if xml = doc.elements['Result']
336
+ raise Amazon::AWS::Error::AWSError, xml.text
337
+ end
338
+
339
+ # Fundamental errors happen at the OperationRequest level. For
340
+ # example, if an invalid AWSAccessKeyId is used.
341
+ #
342
+ error_check( doc.elements['*/OperationRequest'] )
343
+
344
+ # Check for parameter and value errors deeper down, inside Request.
345
+ #
346
+ if operation.kind == 'MultipleOperation'
347
+
348
+ # Everything is a level deeper, because of the
349
+ # <MultiOperationResponse> container.
350
+ #
351
+ # Check for errors in the first operation.
352
+ #
353
+ error_check( doc.elements['*/*/*/Request'] )
354
+
355
+ # Check for errors in the second operation.
356
+ #
357
+ error_check( doc.elements['*/*[3]/*/Request'] )
358
+
359
+ # If second operation is batched, check for errors in its 2nd set
360
+ # of results.
361
+ #
362
+ if batched = doc.elements['*/*[3]/*[2]/Request']
363
+ error_check( batched )
364
+ end
365
+ else
366
+ error_check( doc.elements['*/*/Request'] )
367
+
368
+ # If operation is batched, check for errors in its 2nd set of
369
+ # results.
370
+ #
371
+ if batched = doc.elements['*/*[3]/Request']
372
+ error_check( batched )
373
+ end
374
+ end
375
+
376
+ if doc.elements['*/*[2]/TotalPages']
377
+ total_pages = doc.elements['*/*[2]/TotalPages'].text.to_i
378
+
379
+ # FIXME: ListLookup and MultipleOperation (and possibly others) have
380
+ # TotalPages nested one level deeper. I should take some time to
381
+ # ensure that all operations that can return multiple results pages
382
+ # are covered by either the 'if' above or the 'elsif' here.
383
+ #
384
+ elsif doc.elements['*/*[2]/*[2]/TotalPages']
385
+ total_pages = doc.elements['*/*[2]/*[2]/TotalPages'].text.to_i
386
+ else
387
+ total_pages = 1
388
+ end
389
+
390
+ # Create a root AWS object and walk the XML response tree.
391
+ #
392
+ aws = AWS::AWSObject.new( operation )
393
+ aws.walk( doc )
394
+ result = aws
395
+
396
+ # If only one page has been requested or only one page is available,
397
+ # we can stop here. First yield to the block, if given.
398
+ #
399
+ if nr_pages == 1 || ( tp = total_pages ) == 1
400
+ yield result if block_given?
401
+ return result
402
+ end
403
+
404
+ # Limit the number of pages to the maximum number available.
405
+ #
406
+ nr_pages = tp.to_i if nr_pages == :ALL_PAGES || nr_pages > tp.to_i
407
+
408
+ if PAGINATION.key? operation.kind
409
+ page_parameter = PAGINATION[operation.kind]['parameter']
410
+ max_pages = PAGINATION[operation.kind]['max_page']
411
+ else
412
+ page_parameter = 'ItemPage'
413
+ max_pages = 400
414
+ end
415
+
416
+ # Iterate over pages 2 and higher, but go no higher than MAX_PAGES.
417
+ #
418
+ 2.upto( nr_pages < max_pages ? nr_pages : max_pages ) do |page_nr|
419
+ @query = Amazon::AWS.assemble_query(
420
+ parameters.merge( { page_parameter => page_nr } ),
421
+ @encoding)
422
+ page = Amazon::AWS.get_page( self )
423
+
424
+ # Ruby 1.9 needs to know that the page is UTF-8, not ASCII-8BIT.
425
+ #
426
+ page.force_encoding( 'utf-8' ) if RUBY_VERSION >= '1.9.0'
427
+
428
+ doc = Document.new( page )
429
+
430
+ # Check for errors.
431
+ #
432
+ error_check( doc.elements['*/OperationRequest'] )
433
+ error_check( doc.elements['*/*/Request'] )
434
+
435
+ # Create a new AWS object and walk the XML response tree.
436
+ #
437
+ aws = AWS::AWSObject.new( operation )
438
+ aws.walk( doc )
439
+
440
+ # When dealing with multiple pages, we return not just an
441
+ # AWSObject, but an array of them.
442
+ #
443
+ result = [ result ] unless result.is_a? Array
444
+
445
+ # Append the new object to the array.
446
+ #
447
+ result << aws
448
+ end
449
+
450
+ # Yield each object to the block, if given.
451
+ #
452
+ result.each { |r| yield r } if block_given?
453
+
454
+ result
455
+ end
456
+
457
+ end
458
+
459
+ end
460
+
461
+ end
462
+
463
+ end