treyconnell-ruby-aaws 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/COPYING +340 -0
  2. data/INSTALL +260 -0
  3. data/NEWS +808 -0
  4. data/README +679 -0
  5. data/README.rdoc +140 -0
  6. data/Rakefile +17 -0
  7. data/VERSION.yml +5 -0
  8. data/example/batch_operation +28 -0
  9. data/example/browse_node_lookup1 +46 -0
  10. data/example/customer_content_lookup1 +27 -0
  11. data/example/customer_content_search1 +21 -0
  12. data/example/example1 +78 -0
  13. data/example/help1 +24 -0
  14. data/example/item_lookup1 +56 -0
  15. data/example/item_lookup2 +56 -0
  16. data/example/item_search1 +30 -0
  17. data/example/item_search2 +37 -0
  18. data/example/item_search3 +23 -0
  19. data/example/list_lookup1 +29 -0
  20. data/example/list_search1 +30 -0
  21. data/example/multiple_operation1 +69 -0
  22. data/example/seller_listing_lookup1 +30 -0
  23. data/example/seller_listing_search1 +29 -0
  24. data/example/seller_lookup1 +45 -0
  25. data/example/shopping_cart1 +42 -0
  26. data/example/similarity_lookup1 +48 -0
  27. data/example/tag_lookup1 +34 -0
  28. data/example/transaction_lookup1 +25 -0
  29. data/example/vehicle_search +22 -0
  30. data/lib/amazon/aws/cache.rb +141 -0
  31. data/lib/amazon/aws/search.rb +464 -0
  32. data/lib/amazon/aws/shoppingcart.rb +537 -0
  33. data/lib/amazon/aws.rb +1493 -0
  34. data/lib/amazon/locale.rb +102 -0
  35. data/lib/amazon.rb +165 -0
  36. data/pkg/treyconnell-ruby-aaws-0.8.1.gem +0 -0
  37. data/test/setup.rb +56 -0
  38. data/test/tc_amazon.rb +20 -0
  39. data/test/tc_aws.rb +160 -0
  40. data/test/tc_browse_node_lookup.rb +49 -0
  41. data/test/tc_customer_content_lookup.rb +49 -0
  42. data/test/tc_help.rb +44 -0
  43. data/test/tc_item_lookup.rb +47 -0
  44. data/test/tc_item_search.rb +105 -0
  45. data/test/tc_list_lookup.rb +60 -0
  46. data/test/tc_list_search.rb +44 -0
  47. data/test/tc_multiple_operation.rb +375 -0
  48. data/test/tc_operation_request.rb +64 -0
  49. data/test/tc_seller_listing_lookup.rb +47 -0
  50. data/test/tc_seller_listing_search.rb +55 -0
  51. data/test/tc_seller_lookup.rb +44 -0
  52. data/test/tc_serialisation.rb +107 -0
  53. data/test/tc_shopping_cart.rb +214 -0
  54. data/test/tc_similarity_lookup.rb +48 -0
  55. data/test/tc_tag_lookup.rb +24 -0
  56. data/test/tc_transaction_lookup.rb +24 -0
  57. data/test/tc_vehicle_operations.rb +118 -0
  58. data/test/ts_aws.rb +24 -0
  59. metadata +146 -0
data/README ADDED
@@ -0,0 +1,679 @@
1
+ $Id: README,v 1.26 2010/03/19 19:28:18 ianmacd Exp $
2
+
3
+
4
+ Introduction
5
+ ------------
6
+
7
+ Ruby/AWS is a Ruby language library that aims to make it relatively easy for
8
+ the programmer to retrieve information from the popular Amazon Web site via
9
+ Amazon's Associates Web Services (AWS). In addition to the original amazon.com
10
+ site, the local sites amazon.co.uk, amazon.de, amazon.fr, amazon.ca and
11
+ amazon.co.jp are also supported.
12
+
13
+ Development of Ruby/AWS has been quite swift since the appearance of the first
14
+ alpha version, 0.0.1, in late March of 2008. Although Ruby/AWS shares almost no
15
+ code with its now obsolete predecessor, Ruby/Amazon, many lessons were learnt
16
+ whilst developing that library, and the experience gained has been rolled into
17
+ Ruby/AWS.
18
+
19
+ As of version 0.3.0, I believe that Ruby/AWS has attained its goal of being
20
+ superior to the final version of Ruby/Amazon, 0.9.2, which was released in
21
+ August 2006.
22
+
23
+
24
+ History and compatibility with Ruby/Amazon
25
+ ------------------------------------------
26
+
27
+ In the beginning, there was Ruby/Amazon. This library was built around version
28
+ 3.x of the Amazon Web Service API and first saw the light of day in January
29
+ 2004. The version of the Amazon API in use at the time was known as AWS 3.x.
30
+
31
+ Amazon later renamed AWS to ECS, or E-Commerce Service, for the launch of
32
+ version 4 of their API, a complete overhaul that provided no backward
33
+ compatibility with previous versions. The previous version of the API was
34
+ thenceforth sometimes referred to as ECS 3.
35
+
36
+ Demonstrating the wisdom and consistency for which large companies are
37
+ renowned, Amazon changed their mind once again in late 2007, reverting to the
38
+ familiar name of AWS. This time, however, it was said to stand for Associates
39
+ Web Service, rather than Amazon Web Service.
40
+
41
+ Since Amazon first made AWS available, the number of Amazon Web APIs has
42
+ grown and AWS is now just one of many. It is therefore no longer appropriate
43
+ to call this library by a name so general as Ruby/Amazon, because it
44
+ provides an interface to just one of the Amazon Web APIs. Therefore, the
45
+ monicker for this library is Ruby/AWS.
46
+
47
+ Unfortunately for Ruby/AWS, Amazon changed the name once again in May 2009,
48
+ referring to it now as the Product Advertising API. Changing Ruby/AWS's name
49
+ would create more confusion than it would mitigate, however, so I'm not about
50
+ to do so. Similarly, I will continue to refer to the Amazon API in question as
51
+ AWS.
52
+
53
+ Ruby/AWS is built around version 4 of the Amazon AWS API, which is
54
+ fundamentally different to version 3, both in terms of how requests are made
55
+ and the data returned. The underlying structure of the XML response has
56
+ radically changed from previous versions.
57
+
58
+ It has therefore not been practical for Ruby/AWS to retain any level of API
59
+ compatibility with Ruby/Amazon. Unfortunately, this means that any code
60
+ written for Ruby/Amazon will need to be rewritten to work with Ruby/AWS. The
61
+ good news is that, in most cases, this isn't as much work as it might sound.
62
+
63
+ Another bit of good news is that the /etc/amazonrc and ~/.amazonrc files used
64
+ by Ruby/Amazon _are_ compatible with Ruby/AWS. The only change required for
65
+ Ruby/AWS is the addition of the 'key_id' and 'secret_key_id' parameters, which
66
+ should contain your AWS Access Key ID and its secret counterpart. That fact
67
+ notwithstanding, as of version 0.5.0, Ruby/AWS also supports a more flexible,
68
+ locale-specific configuration syntax.
69
+
70
+ Amazon finally decomissioned v3 of the AWS API on 2008-03-31. As a result, the
71
+ original Ruby/Amazon library no longer functions and is therefore obsolete.
72
+
73
+
74
+ AWS Access Key ID
75
+ -----------------
76
+
77
+ You can obtain an AWS Access Key ID here:
78
+
79
+ https://aws-portal.amazon.com/gp/aws/developer/registration/index.html
80
+
81
+ You may see mention of Subscription IDs at the above location. Subscription
82
+ IDs are not supported by Ruby/AWS and, in any case, are no longer supported by
83
+ Amazon since the introduction of authenticated requests. Please obtain and use
84
+ an AWS Access Key ID instead.
85
+
86
+
87
+ API version
88
+ -----------
89
+
90
+ Ruby/AWS currently requests the 2009-11-01 revision of the AWS API when
91
+ performing its operations:
92
+
93
+ http://docs.amazonwebservices.com/AWSECommerceService/2009-11-01/DG/
94
+
95
+ However, a different version can be requested via the 'api' parameter in the
96
+ user configuration file.
97
+
98
+
99
+ Status and functionality
100
+ ------------------------
101
+
102
+ Ruby/AWS is currently beta software. Amongst other things, this means:
103
+
104
+ - You will encounter bugs, but hopefully not too many and none too serious.
105
+ Tell me about them and I will endeavour to fix them.
106
+
107
+ - The documentation isn't what it could be, but it's hopefully enough to get
108
+ you up and running.
109
+
110
+ - Not all features are currently implemented. Others may not yet be _fully_
111
+ implemented. Some, I probably haven't even thought of yet. Again, if
112
+ something's missing, tell me, and if it makes sense, I'll add it.
113
+
114
+ In spite of this shortcomings, the AWS v4 API is more or less fully
115
+ supported, with only small gaps in the functionality of some operations.
116
+
117
+ Currently implemented operations are:
118
+
119
+ BrowseNodeLookup
120
+ CustomerContentLookup
121
+ CustomerContentSearch
122
+ Help
123
+ ItemLookup
124
+ ItemSearch
125
+ ListLookup
126
+ ListSearch
127
+ SellerListingLookup
128
+ SellerListingSearch
129
+ SellerLookup
130
+ SimilarityLookup
131
+ TagLookup
132
+ TransactionLookup
133
+
134
+ Remote shopping-carts are also implemented as of version 0.3.0. The
135
+ following remote shopping-cart operations are supported:
136
+
137
+ CartCreate
138
+ CartAdd
139
+ CartModify
140
+ CartClear
141
+
142
+ Version 0.4.0 added the remaining shopping-cart operation, which I had first
143
+ thought superfluous:
144
+
145
+ CartGet
146
+
147
+ Multiple operations are supported, but have not been heavily used, so there
148
+ may be residual bugs. The implementation of multiple operations was
149
+ rewritten from scratch for version 0.8.0, which appears to have resulted in
150
+ code that is much more robust (not to mention easier to read and maintain).
151
+
152
+ As of version 0.5.0, batch operations are fully supported, using the
153
+ Operation#batch method. Again, the implementation was scrapped and rewritten
154
+ for 0.8.0, yielding a big improvement.
155
+
156
+ There appear to also be (undocumented) Amazon-imposed restrictions on the
157
+ use of multiple operations and batch requests, so some experimentation may
158
+ be required on your part to determine what works and what doesn't.
159
+
160
+ In short, though, my investigations have demonstrated that no more than two
161
+ operations may be batched together, and no more than two operations may be
162
+ combined in a multiple operation. Nevertheless, by combining two batched
163
+ operations in a multiple operation, one can send four base operations to
164
+ Amazon in a single request. No more than two of these may be from the same
165
+ class, however.
166
+
167
+ The 2008-08-19 version of the AWS API added the following operations:
168
+
169
+ VehiclePartLookup
170
+ VehiclePartSearch
171
+ VehicleSearch
172
+
173
+ These are supported by Ruby/AWS from version 0.5.0 onwards.
174
+
175
+ - Classes, methods, constants and instance variables may change name in the
176
+ future. New ones may appear from nowhere and existing ones may change shape,
177
+ grow, shrink or disappear without trace. Such fundamental changes will break
178
+ existing code, so I will endeavour to keep them to a minimum.
179
+
180
+ In short, code written to work with this release of Ruby/AWS may stop working
181
+ when you upgrade to the next. In fact, it may even stop working _during_ this
182
+ release cycle, because it's possible there are fatal conditions that I didn't
183
+ encounter in my limited testing of the code. It's also possible that future
184
+ (possibly unannounced) changes made by Amazon will affect Ruby/AWS in
185
+ ways I can't anticipate.
186
+
187
+ That said, the Ruby/AWS API is pretty stable at this point in time. I won't
188
+ break any of the method interfaces without seriously considering the merits of
189
+ doing so.
190
+
191
+
192
+ Installation
193
+ ------------
194
+
195
+ Please see the INSTALL file for details of how to install Ruby/AWS. You can
196
+ choose between an installation script and a RubyGems installation.
197
+
198
+
199
+ Usage
200
+ -----
201
+
202
+ First of all, create either /etc/amazonrc or ~/.amazonrc as a plain text file.
203
+ Its contents should look something like this:
204
+
205
+ # Any line that starts with a hash character is a comment.
206
+ key_id = '0Y44V8G41KCQPGF6XYZ2'
207
+ secret_key_id = 'k+kuddeoQJzUnImC0Hyy21J4xLWQc1hbvfQ+7F1G
208
+ associate = 'fuzbarorg-21'
209
+ cache = false
210
+ locale = 'uk'
211
+ encoding = 'iso-8859-15'
212
+
213
+ The ability to include your secret key was a feature added in version 0.6.0 of
214
+ Ruby/AWS. If you choose to do so, your requests to AWS will be signed for
215
+ authentication by Amazon's servers. If you don't, your requests will fail,
216
+ because Amazon made this practice obligatory on 15th August 2009.
217
+
218
+ When Amazon checks for a valid signature, it does so by comparing its
219
+ computation of the signature with the one supplied by the user. In doing this,
220
+ Amazon uses the UTF-8 representation of parameter values that you supply, even
221
+ if you use a different encoding.
222
+
223
+ In order to have Ruby/AWS properly reencode your strings as UTF-8, you need
224
+ to tell it which encoding you are using. The 'encoding' parameter can be used
225
+ for this, but you can omit it if your strings are already UTF-8, because this
226
+ is the default.
227
+
228
+ As of version 0.5.0 of Ruby/AWS, the following locale-specific configuration
229
+ syntax is also supported:
230
+
231
+ [global]
232
+ key_id = '0Y44V8G41KCQPGF6XYZ2'
233
+ secret_key_id = 'k+kuddeoQJzUnImC0Hyy21J4xLWQc1hbvfQ+7F1G
234
+ cache = false
235
+ locale = 'uk'
236
+ encoding = 'iso-8859-15'
237
+ # Request a specific version of the API.
238
+ # api = '2008-03-03'
239
+
240
+ [uk]
241
+ associate = 'fuzbarorg-21'
242
+
243
+ [us]
244
+ associate = 'fuzbarorg-20'
245
+
246
+ This enables the use of a different associate tag for each locale, although,
247
+ oddly enough, no-one has ever requested this feature.
248
+
249
+ Because you're embedding your personal keys in the file, you should protect it
250
+ (on UNIX and equivalent systems) by making it mode 0600:
251
+
252
+ $ chmod 600 ~/.amazonrc
253
+
254
+ If you define 'cache' to be 'true', you may want to also define 'cache_dir' to
255
+ point to somewhere other the default cache directory, /tmp/amazon.
256
+
257
+ If you want to place .amazonrc somewhere other than $HOME, you may set
258
+ the $AMAZONRCDIR environment variable, as this location is checked prior to
259
+ $HOME.
260
+
261
+ If you're using Windows, $HOME is usually undefined, so a number of additional
262
+ locations are checked for .amazonrc.
263
+
264
+ The exact search order is as follows:
265
+
266
+ $AMAZONRCDIR
267
+ $HOME
268
+ $HOMEDRIVE + $HOMEPATH
269
+ $USERPROFILE
270
+
271
+ Note that only the first defined location is used, so if, for example, both
272
+ $AMAZONRCDIR and $HOME are defined, but only the path specified by $HOME
273
+ contains a file called .amazonrc, it will not be found.
274
+
275
+ If you want the user configuration file to be called something other than
276
+ .amazonrc, you may define the $AMAZONRCFILE environment variable.
277
+
278
+ Once you have your configuration file, you can get started writing your code.
279
+
280
+ Here's a basic example, showing how to perform an ItemSearch, probably the
281
+ most common type of AWS operation. Please see the ./examples subdirectory for
282
+ more examples of contrived, but working code.
283
+
284
+ --
285
+
286
+ require 'amazon/aws/search'
287
+
288
+ # Avoid having to fully qualify our methods.
289
+ #
290
+ include Amazon::AWS
291
+ include Amazon::AWS::Search
292
+
293
+ is = ItemSearch.new( 'Books', { 'Title' => 'Ruby' } )
294
+
295
+ # I want to receive just a small amount of data for the items found.
296
+ #
297
+ is.response_group = ResponseGroup.new( :Small )
298
+
299
+ req = Request.new
300
+
301
+ # Make sure I'm talking to amazon.co.uk.
302
+ #
303
+ req.locale = 'uk'
304
+
305
+ # Actually talk to AWS.
306
+ #
307
+ resp = req.search( is )
308
+
309
+ # Drill down to the meat: the array of items returned.
310
+ #
311
+ items = resp.item_search_response[0].items[0].item
312
+
313
+ # The following alternative shorthand would also have worked:
314
+ #
315
+ # items = resp.item_search_response.items.item
316
+
317
+ # Available properties for first item:
318
+ #
319
+ puts items[0].properties
320
+
321
+ items.each do |item|
322
+ attribs = item.item_attributes[0]
323
+ puts attribs.label
324
+ if attribs.list_price
325
+ puts attribs.title, attribs.list_price[0].formatted_price, ''
326
+ end
327
+ end
328
+
329
+
330
+ Troubleshooting
331
+ ---------------
332
+
333
+ HTTP 400 is the main bane of people's life since Amazon started authenticating
334
+ requests to AWS. If you're trying to get Ruby/AWS to work, but are plagued by
335
+ HTTP 400 responses, here are some possible causes:
336
+
337
+ - Your ~/.amazonrc file doesn't contain a secret_key_id parameter. Add one.
338
+
339
+ - Your computer's system clock is running more than 15 minutes slow. Amazon
340
+ consider a request timestamped more than 15 minutes in the past invalid.
341
+ Synchronise your system clock with a reliable time source.
342
+
343
+ Incidentally, Amazon don't object to requests timestamped in the future, but
344
+ that's something that may change at any time and therefore shouldn't be
345
+ relied upon.
346
+
347
+
348
+ XML to Ruby mapping
349
+ -------------------
350
+
351
+ Here, I will discuss the mapping of the XML returned from AWS to native Ruby
352
+ objects and data. Note that the XML shown below was that returned at the time
353
+ of writing and may look different to what you would see today if you were to
354
+ execute the same request.
355
+
356
+ When this code:
357
+
358
+ resp = req.search( is )
359
+
360
+ was called in the previous section, the following URL was composed and sent to
361
+ AWS as an HTTP GET operation:
362
+
363
+ http://ecs.amazonaws.co.uk/onca/xml?AWSAccessKeyId=01234567890123456789&AssociateTag=calibanorg-21&Operation=ItemSearch&ResponseGroup=Small&SearchIndex=Books&Service=AWSECommerceService&Title=Ruby&Version=2008-03-03
364
+
365
+ The following (abbreviated) AWS XML response was received:
366
+
367
+ <ItemSearchResponse>
368
+ <OperationRequest>
369
+ <HTTPHeaders>
370
+ <Header Name="UserAgent" Value="Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.13) Gecko/20080325 Fedora/2.0.0.13-1.fc7 Firefox/2.0.0.13"/>
371
+ </HTTPHeaders>
372
+ <RequestId>1TBGEZ48MF8KZ8TGXH65</RequestId>
373
+ <Arguments>
374
+ <Argument Name="SearchIndex" Value="Books"/>
375
+ <Argument Name="Service" Value="AWSECommerceService"/>
376
+ <Argument Name="ResponseGroup" Value="Small"/>
377
+ <Argument Name="Operation" Value="ItemSearch"/>
378
+ <Argument Name="Version" Value="2008-03-03"/>
379
+ <Argument Name="AssociateTag" Value="calibanorg-21"/>
380
+ <Argument Name="Title" Value="Ruby"/>
381
+ <Argument Name="AWSAccessKeyId" Value="01234567890123456789"/>
382
+ </Arguments>
383
+ <RequestProcessingTime>0.0671439170837402</RequestProcessingTime>
384
+ </OperationRequest>
385
+ <Items>
386
+ <Request>
387
+ <IsValid>True</IsValid>
388
+ <ItemSearchRequest>
389
+ <ResponseGroup>Small</ResponseGroup>
390
+ <SearchIndex>Books</SearchIndex>
391
+ <Title>Ruby</Title>
392
+ </ItemSearchRequest>
393
+ </Request>
394
+ <TotalResults>1804</TotalResults>
395
+ <TotalPages>181</TotalPages>
396
+ <Item>
397
+ <ASIN>0439943663</ASIN>
398
+ <DetailPageURL>
399
+ http://www.amazon.co.uk/gp/redirect.html%3FASIN=0439943663%26tag=calibanorg-21%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0439943663%253FSubscriptionId=0Y44V8FAFNM119C6PTR2
400
+ </DetailPageURL>
401
+ <ItemAttributes>
402
+ <Author>Philip Pullman</Author>
403
+ <Manufacturer>Scholastic</Manufacturer>
404
+ <ProductGroup>Book</ProductGroup>
405
+ <Title>The Ruby in the Smoke (Sally Lockhart Quartet)</Title>
406
+ </ItemAttributes>
407
+ </Item>
408
+ <Item>
409
+ <ASIN>0596516177</ASIN>
410
+ ...
411
+
412
+ In Ruby/AWS, each unique XML element name forms a class of the same name. All
413
+ such classes are subclasses of AWSObject. For example, OperationRequest is a
414
+ class, as is ItemAttributes.
415
+
416
+ As the XML tree is traversed, each element is converted to an instance of the
417
+ class of the same name. Every such object has instance variables, one per
418
+ unique child element name. The name of the instance variable is translated to
419
+ comply with Ruby convention by adding an underscore ('_') character at word
420
+ boundaries and converting the name to lower case.
421
+
422
+ For example, given the following XML:
423
+
424
+ <ItemAttributes>
425
+ <Author>Philip Pullman</Author>
426
+ <Manufacturer>Scholastic</Manufacturer>
427
+ <ProductGroup>Book</ProductGroup>
428
+ <Title>The Ruby in the Smoke (Sally Lockhart Quartet)</Title>
429
+ </ItemAttributes>
430
+
431
+ the following statements would all be true:
432
+
433
+ - ItemAttributes, Author, Manufacturer, ProductGroup and Title would all be
434
+ dynamically defined subclasses of AWSObject.
435
+
436
+ - An instance of the ItemAttributes class would be created, with instance
437
+ variables @author, @manufacturer, @product_group and @title.
438
+
439
+ - To each of these instance variables would respectively be assigned an array
440
+ of Author objects, an array of Manufacturer objects, an array of
441
+ ProductGroup objects and an array of Title objects. In the above case, these
442
+ would all be single element arrays, because there's only one instance of
443
+ each kind of tag in the XML.
444
+
445
+ - The Author, Manufacturer, ProductGroup and Title objects would have no
446
+ instance variables of their own, because the corresponding XML elements
447
+ have no children, just a value. These objects are therefore directly
448
+ assigned the value in question.
449
+
450
+ So, if resp is the top level AWSObject created and returned by calling the
451
+ Amazon::AWS::Search::Request#search method of the Request object, and we'd
452
+ like to know the ASIN of the first item found, we can refer to this as
453
+ follows:
454
+
455
+ resp.item_search_response[0].items[0].item[0].asin
456
+
457
+ Looking at each component of this chain in turn:
458
+
459
+ - resp is an AWSObject with a single instance variable, @item_search_response.
460
+ This is because the entire XML response is contained within a single
461
+ <ItemSearchResponse> element, so there's nothing else at the top level.
462
+
463
+ - resp.item_search_response is assigned an array of ItemSearchResponse
464
+ objects. Because there's only a single <ItemSearchResponse> element in the
465
+ whole document (containing the rest of the XML), the array contains only a
466
+ single element.
467
+
468
+ - resp.item_search_response[0] has an instance variable, @items, which is
469
+ assigned an array of Items objects. Here again, only a single element is
470
+ created, because there's only one corresponding <Items> element in the XML.
471
+
472
+ - resp.item_search_response[0].items[0] has an instance variable, @item, which
473
+ is an array containing the actual item(s) located by the search. It is a
474
+ multi-element array, however, because more than one item was found, as
475
+ represented by the multiple <Item> elements in the XML.
476
+
477
+ The creation of so many single element arrays is unfortunate. It makes user
478
+ code verboser, uglier and consequently harder to read.
479
+
480
+ You might wonder why Ruby/AWS doesn't just assign the single element itself,
481
+ rather than the array that contains it.
482
+
483
+ The answer is that most of these single-element arrays actually do have the
484
+ potential to be multi-element, because the corresponding XML tag _can_ appear
485
+ multiple times in an AWS response. A book, for example, _may_ have more than
486
+ one <Author>. Many other types of array, however, are necessarily single
487
+ element arrays. That same book, for example, is unlikely to have more than one
488
+ <Title>. This is context-dependent and difficult to define programatically.
489
+
490
+ As another concrete example, an ItemSearch will probably yield many <Item>
491
+ elements in the <ItemSearchResponse>, but these will invariably be nested in a
492
+ single <Items> element. The @items instance variable of the ItemSearchResponse
493
+ object will therefore always be a single-element array.
494
+
495
+ In other words, the following statements are both invariably true when an
496
+ ItemSearch successfully locates items:
497
+
498
+ - resp.item_search_response[0].items.size == 1
499
+
500
+ - resp.item_search_response[0].items[0].item.size >= 1
501
+
502
+ The awkwardness of using such single element arrays is alleviated in Ruby/AWS
503
+ by the use of the AWSArray subclass. An instance of this class differs from a
504
+ standard array by allowing element 0 of a single-element array to be
505
+ dereferenced using just the array name, i.e. without a subscript.
506
+
507
+ In other words, a reference to foo.bar will actually return foo[0].bar when
508
+ foo.size == 1. Note that this can only work because the array itself, foo, has
509
+ no bar method, so the intention is unambiguous and foo can delegate the
510
+ invocation of the method to foo[0]. foo.size, on the other hand, will _always_
511
+ invoke foo's bar method, never delegating to foo[0], because of the existence
512
+ of the Array#size method.
513
+
514
+ This allows the ASIN of the first item returned in the above XML to be
515
+ referred to using the following shorthand:
516
+
517
+ resp.item_search_response.items.item[0].asin
518
+
519
+ It's worth reiterating that it's still necessary in this example to refer to
520
+ item[0] using a subscript, because the <Items> element in the XML contains
521
+ multiple <Item> elements, making item.size > 1.
522
+
523
+ Use this syntactic shorthand to your advantage, but understand when you're
524
+ likely to be dealing with a single element array vs. a multiple. This will
525
+ become apparent as you gain familiarity with AWS v4.
526
+
527
+ An exception will be raised if an unknown method is called on a multi-element
528
+ array, as it can't be known to which array element the method invocation
529
+ should be delegated. This will almost certainly stem from an incorrect
530
+ assumption that an array contains only a single element when, in actual fact,
531
+ it contains multiple elements.
532
+
533
+ A further important detail to note is that not all AWS operations of the same
534
+ class return the same data. For example, an ItemSearch using the Books search
535
+ index will return items that, amongst other things, have an ItemAttributes
536
+ object containing further objects of class Author, ISBN, etc. An ItemSearch
537
+ using the DVD search index, by contrast, will have no Author or ISBN, but
538
+ will likely have a Director and probably one or more Actor objects.
539
+
540
+ Because of the disparity in same-class object attributes, Ruby/AWS returns
541
+ *nil* when an attempt is made to dereference a non-existent instance variable.
542
+ This approach was chosen because, more often than not, it cannot be known in
543
+ advance precisely which data will be returned by a given search operation.
544
+ Returning *nil* for non-existent attributes saves the user from having to
545
+ pepper their code with exception-handling clauses.
546
+
547
+ For example:
548
+
549
+ resp.item_search_response[0].items[0].item[0].item_attributes.director
550
+
551
+ will return *nil* for a book, because there was no corresponding Director
552
+ element in the XML returned by AWS.
553
+
554
+ Similarly:
555
+
556
+ resp.item_search_response[0].items[0].item[0].item_attributes.foo_bar
557
+
558
+ will _always_ return *nil* for _any_ item, because no kind of ItemSearch will
559
+ ever yield an item with a FooBar element.
560
+
561
+
562
+ Parameter checking
563
+ ------------------
564
+
565
+ There are many combinations of parameters and values that are legal for a
566
+ particular type of search. For example, an ItemSearch can use a Sort parameter
567
+ with a value of 'titlerank' if the SearchIndex is 'Books'. However, this value
568
+ wouldn't make much sense in the 'Automotive' SearchIndex.
569
+
570
+ The very presence of a certain parameter can be illegal in certain contexts.
571
+ For example, specifying the parameter 'Author' with _any_ value would be
572
+ nonsensical in the 'PetSupplies' SearchIndex.
573
+
574
+ To complicate things further, the validity of parameters and their values
575
+ differs not only by search type, but also by Amazon locale (amazon.com,
576
+ amazon.co.uk, amazon.de, etc.) and is prone to change with minor revisions of
577
+ the Amazon AWS API.
578
+
579
+ Even worse, the operations themselves can be illegal in certain locales.
580
+ TransactionLookup operations, for example, don't work in the UK locale at the
581
+ time of writing, but do work in the US locale. As a rule of thumb, we can say
582
+ that almost everything works in the US locale and _may_ work in others.
583
+
584
+ Ruby/Amazon attempted to track these complex and dynamic relationships to
585
+ prevent illegal or ineffective operations from being attempted. It was a
586
+ time-consuming and tedious task to track the evolving API (which often changed
587
+ in subtle ways without prior [or even belated] notice from Amazon), find all
588
+ of the corner cases and handle undocumented quirks.
589
+
590
+ With the highly dynamic nature of the Amazon environment and its many locales,
591
+ plus the sheer number of operations, parameters and their possibly legal
592
+ values in the AWS v4 API, this strict approach would be completely
593
+ impractical for Ruby/AWS. It therefore doesn't even try.
594
+
595
+ Instead, it's now up to you to ensure that you perform legal operations and
596
+ pass sensible parameters and values for the locale in which you're working.
597
+ The context is now your responsibility.
598
+
599
+ The one exception to this rule is search index checking for ItemSearch
600
+ operations. Code that attempts to use an invalid SearchIndex will raise an
601
+ exception. The list of allowable search indices can be found in the
602
+ Amazon::AWS::Operation::ItemSearch::SEARCH_INDICES array.
603
+
604
+ Of course, even this check exposes the user to the risk that Amazon may later
605
+ add new search indices, which would continue to be unrecognised and ruled
606
+ invalid by Ruby/AWS until an update was issued. Whilst I have chosen to
607
+ implement this very basic level of checking, it may be removed in the future
608
+ if it becomes impractical to keep it current.
609
+
610
+ In short, the validity of what goes into a search operation is your own
611
+ responsibility: garbage in, garbage out.
612
+
613
+ Thankfully, with the AWS Developer Guide at your side, it's largely common
614
+ sense which parameters and values can be used with each type of search. It's
615
+ less obvious when these differ by locale. For example, the 'Beauty'
616
+ SearchIndex was valid in the 'us' locale, but not in the 'uk' locale until the
617
+ 2009-01-06 revision of the AWS API.
618
+
619
+ Unfortunately, AWS abounds with such inconsistencies and they are prone to
620
+ change at any time. Amazon, themselves, seem to struggle to document all of
621
+ these quirks, a situation probably aggravated by the US focus of the AWS
622
+ staff.
623
+
624
+ The only way to apprise yourself of such peculiarities is to read Amazon's
625
+ latest developer documentation (and closely follow the release notes of each
626
+ minor API revision to make sure things haven't changed). If you don't want to
627
+ be exposed to such API changes, use the 'api' parameter in the user
628
+ configuration file to request a particular version of the API.
629
+
630
+ The AWS Developer Connection pages may also be of use to you. In particular,
631
+ the forum for discussing AWS has proved useful to me over the years:
632
+
633
+ http://developer.amazonwebservices.com/connect/forum.jspa?forumID=9
634
+
635
+ For those illegal operations that make it through to the Amazon servers, the
636
+ good news is that Amazon carries out extensive run-time parameter checking in
637
+ AWS v4 (much better than in v3) and will generate an error when an illegal set
638
+ of parameters and/or values is given. Ruby/AWS will dynamically generate a
639
+ class for the type of error reported and raise an exception of that class.
640
+
641
+ Using this approach, Ruby/AWS doesn't have to perform checks that Amazon will
642
+ perform, anyway. This helps keep the code base leaner, the library faster, and
643
+ reduces the chance that Ruby/AWS will disallow an operation that becomes valid
644
+ following a minor revision of AWS.
645
+
646
+
647
+ Documentation
648
+ -------------
649
+
650
+ You can generate HTML documentation for the library with the following
651
+ command, executed from the directory created when you unpacked the archive:
652
+
653
+ rdoc -SUx CVS lib
654
+
655
+ The documentation on how to use this library is currently incomplete, but it
656
+ should be enough to get you started.
657
+
658
+ You can also use the Ruby/AWS mailing-list:
659
+
660
+ http://caliban.org/mailman/listinfo/ruby-aws
661
+
662
+ to discuss any Ruby/AWS-related subjects and issues.
663
+
664
+
665
+ Examples
666
+ --------
667
+
668
+ The ./examples subdirectory contains working examples of code.
669
+
670
+
671
+ Licence
672
+ -------
673
+
674
+ This software is copyright (C) 2008-2010 Ian Macdonald and distributed under
675
+ the terms of the GNU GENERAL PUBLIC LICENSE, a copy of which is included.
676
+
677
+ --
678
+ Ian Macdonald
679
+ <ian@caliban.org>