papercavalier-ruby-aaws 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/COPYING +340 -0
- data/INSTALL +260 -0
- data/NEWS +808 -0
- data/README +679 -0
- data/README.rdoc +140 -0
- data/Rakefile +17 -0
- data/VERSION.yml +5 -0
- data/example/batch_operation +28 -0
- data/example/browse_node_lookup1 +46 -0
- data/example/customer_content_lookup1 +27 -0
- data/example/customer_content_search1 +21 -0
- data/example/example1 +78 -0
- data/example/help1 +24 -0
- data/example/item_lookup1 +56 -0
- data/example/item_lookup2 +56 -0
- data/example/item_search1 +30 -0
- data/example/item_search2 +37 -0
- data/example/item_search3 +23 -0
- data/example/list_lookup1 +29 -0
- data/example/list_search1 +30 -0
- data/example/multiple_operation1 +69 -0
- data/example/seller_listing_lookup1 +30 -0
- data/example/seller_listing_search1 +29 -0
- data/example/seller_lookup1 +45 -0
- data/example/shopping_cart1 +42 -0
- data/example/similarity_lookup1 +48 -0
- data/example/tag_lookup1 +34 -0
- data/example/transaction_lookup1 +25 -0
- data/example/vehicle_search +22 -0
- data/lib/amazon.rb +165 -0
- data/lib/amazon/aws.rb +1493 -0
- data/lib/amazon/aws/cache.rb +141 -0
- data/lib/amazon/aws/search.rb +464 -0
- data/lib/amazon/aws/shoppingcart.rb +537 -0
- data/lib/amazon/locale.rb +102 -0
- data/test/setup.rb +56 -0
- data/test/tc_amazon.rb +20 -0
- data/test/tc_aws.rb +160 -0
- data/test/tc_browse_node_lookup.rb +49 -0
- data/test/tc_customer_content_lookup.rb +49 -0
- data/test/tc_help.rb +44 -0
- data/test/tc_item_lookup.rb +47 -0
- data/test/tc_item_search.rb +105 -0
- data/test/tc_list_lookup.rb +60 -0
- data/test/tc_list_search.rb +44 -0
- data/test/tc_multiple_operation.rb +375 -0
- data/test/tc_operation_request.rb +64 -0
- data/test/tc_seller_listing_lookup.rb +47 -0
- data/test/tc_seller_listing_search.rb +55 -0
- data/test/tc_seller_lookup.rb +44 -0
- data/test/tc_serialisation.rb +107 -0
- data/test/tc_shopping_cart.rb +214 -0
- data/test/tc_similarity_lookup.rb +48 -0
- data/test/tc_tag_lookup.rb +24 -0
- data/test/tc_transaction_lookup.rb +24 -0
- data/test/tc_vehicle_operations.rb +118 -0
- data/test/ts_aws.rb +24 -0
- 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>
|