papercavalier-ruby-aaws 0.8.1

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.
Files changed (59) hide show
  1. data/.gitignore +3 -0
  2. data/COPYING +340 -0
  3. data/INSTALL +260 -0
  4. data/NEWS +808 -0
  5. data/README +679 -0
  6. data/README.rdoc +140 -0
  7. data/Rakefile +17 -0
  8. data/VERSION.yml +5 -0
  9. data/example/batch_operation +28 -0
  10. data/example/browse_node_lookup1 +46 -0
  11. data/example/customer_content_lookup1 +27 -0
  12. data/example/customer_content_search1 +21 -0
  13. data/example/example1 +78 -0
  14. data/example/help1 +24 -0
  15. data/example/item_lookup1 +56 -0
  16. data/example/item_lookup2 +56 -0
  17. data/example/item_search1 +30 -0
  18. data/example/item_search2 +37 -0
  19. data/example/item_search3 +23 -0
  20. data/example/list_lookup1 +29 -0
  21. data/example/list_search1 +30 -0
  22. data/example/multiple_operation1 +69 -0
  23. data/example/seller_listing_lookup1 +30 -0
  24. data/example/seller_listing_search1 +29 -0
  25. data/example/seller_lookup1 +45 -0
  26. data/example/shopping_cart1 +42 -0
  27. data/example/similarity_lookup1 +48 -0
  28. data/example/tag_lookup1 +34 -0
  29. data/example/transaction_lookup1 +25 -0
  30. data/example/vehicle_search +22 -0
  31. data/lib/amazon.rb +165 -0
  32. data/lib/amazon/aws.rb +1493 -0
  33. data/lib/amazon/aws/cache.rb +141 -0
  34. data/lib/amazon/aws/search.rb +464 -0
  35. data/lib/amazon/aws/shoppingcart.rb +537 -0
  36. data/lib/amazon/locale.rb +102 -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 +141 -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>