ruby-aaws 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +110 -1
- data/README +173 -133
- data/README.rdoc +30 -25
- data/lib/amazon.rb +6 -4
- data/lib/amazon/aws.rb +35 -18
- data/lib/amazon/aws/search.rb +66 -15
- data/test/setup.rb +2 -1
- data/test/tc_item_search.rb +40 -3
- metadata +2 -2
data/NEWS
CHANGED
@@ -1,4 +1,113 @@
|
|
1
|
-
$Id: NEWS,v 1.
|
1
|
+
$Id: NEWS,v 1.18 2009/05/26 15:25:19 ianmacd Exp $
|
2
|
+
|
3
|
+
|
4
|
+
0.6.0 - 2009-05-26
|
5
|
+
------------------
|
6
|
+
|
7
|
+
1. Requests to AWS can now be signed in order to authenticate them. Amazon
|
8
|
+
plans to make the signing of requests mandatory as of 15th August 2009, so
|
9
|
+
it is recommended to start doing so now.
|
10
|
+
|
11
|
+
To have your requests automatically signed by Ruby/AWS, simply add the
|
12
|
+
'secret_key_id' parameter to your ~/.amazonrc configuration file. Its value
|
13
|
+
should, rather predictably, be your secret access key, which can be
|
14
|
+
retrieved here:
|
15
|
+
|
16
|
+
https://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&action=access-key
|
17
|
+
|
18
|
+
You needn't be concerned about Amazon's warnings not to show your secret
|
19
|
+
key to anyone else, because it will be used only for signing requests,
|
20
|
+
prior to sending them. The key itself will not be sent over the network to
|
21
|
+
Amazon, even in encrypted form.
|
22
|
+
|
23
|
+
In order to incorporate the new functionality, minor changes had to be made
|
24
|
+
to the way the AWS request URLs are encoded. This change means that
|
25
|
+
previous requests cached by earlier versions of Ruby/AWS will not be found
|
26
|
+
in the cache. This is a minor, one-time inconvenience, and it just means
|
27
|
+
that the requests will be performed and cached again.
|
28
|
+
|
29
|
+
2. When Amazon's AWS servers check whether the correct signature has been
|
30
|
+
applied to a request, they recalculate the signature based on the data in
|
31
|
+
the request and check for a match with the signature supplied by Ruby/AWS.
|
32
|
+
|
33
|
+
This introduces a complicating factor, namely the treatment of non-ASCII
|
34
|
+
characters in the request, such as accented letters. When recalculating the
|
35
|
+
signature, Amazon will use the UTF-8 representation of any such characters.
|
36
|
+
This will cause a signature mismatch if you used a different encoding, such
|
37
|
+
as ISO-8859-1 (a.k.a. Latin-1), when you supplied values for your request
|
38
|
+
parameters.
|
39
|
+
|
40
|
+
Ruby/AWS can't (reliably) dynamically determine which character encoding
|
41
|
+
your strings use, so this information can now be supplied via the
|
42
|
+
~/.amazonrc configuration file, using the 'encoding' parameter. This should
|
43
|
+
be set to whichever encoding you use. If left unset, it defaults to UTF-8.
|
44
|
+
An exception will be raised if you attempt to use an invalid (i.e. unknown)
|
45
|
+
encoding.
|
46
|
+
|
47
|
+
Currently, the encoding you use makes no difference unless your requests
|
48
|
+
are being signed, but because signing will soon be mandatory, I recommend
|
49
|
+
you explicitly state which encoding you intend to use.
|
50
|
+
|
51
|
+
You may also change the encoding in use at any time by assigning to the
|
52
|
+
@encoding instance variable of your Request object.
|
53
|
+
|
54
|
+
3. The robustness of the software has been improved by handling the following
|
55
|
+
additional exceptions while communicating with the AWS servers:
|
56
|
+
Errno::ECONNREFUSED, Errno::ECONNABORTED, Errno::ETIMEDOUT and
|
57
|
+
Timeout::Error. Users have reported that all of these occur from time to
|
58
|
+
time, although only Windows platforms seem to suffer from
|
59
|
+
Errno::ECONNABORTED.
|
60
|
+
|
61
|
+
4. The version of the AWS API used is now 2009-03-31, the latest at the time
|
62
|
+
of writing.
|
63
|
+
|
64
|
+
5. <RANT>
|
65
|
+
Amazon must have appointed a new interim Senior Vice-President of
|
66
|
+
Outward-Facing Moniker Reconciliation or something, because earlier this
|
67
|
+
month, on 8th May, I received an e-mail from Amazon, informing me that the
|
68
|
+
Web service formerly known as Amazon Web Services, latterly the E-Commerce
|
69
|
+
Service, and then, until this month, the Amazon Associates Web Service, is
|
70
|
+
to undergo yet another name change.
|
71
|
+
|
72
|
+
Once again, the reason for the new moniker is that it ostensibly "more
|
73
|
+
accurately reflects the purpose of the API". The new sobriquet is the
|
74
|
+
highly reflective 'Product Advertising API'.
|
75
|
+
|
76
|
+
It never ceases to amaze me that the collective intellectual might of a
|
77
|
+
company as large and resourceful as Amazon cannot, over a period of five
|
78
|
+
years, converge to decisively name an API.
|
79
|
+
|
80
|
+
How long until the reins of responsibility for this offering change hands
|
81
|
+
once again and the next in a growing line of misguided middle managers
|
82
|
+
decides that he can best raise his profile with his superiors by changing
|
83
|
+
the name of the API to "better reflect its purpose"?
|
84
|
+
|
85
|
+
Well, I'm resistant to change, especially stupid, time-wasting changes, so
|
86
|
+
I'll be continuing to refer to AWS as AWS for the foreseeable future. When
|
87
|
+
I say AWS, you'll know I mean the product whose name is woefully subject to
|
88
|
+
Amazon's whimsy.
|
89
|
+
|
90
|
+
The same e-mail informed me that Amazon would soon be introducing mandatory
|
91
|
+
authentication of AWS requests.
|
92
|
+
|
93
|
+
I downloaded the latest developer guide, wrote some code to implement
|
94
|
+
request signatures, and then spent a good couple of hours trying to make my
|
95
|
+
code produce the sample results shown in the developer guide.
|
96
|
+
|
97
|
+
Ultimately, I realised that the sample requests in the developer guide
|
98
|
+
could not have produced the output claimed by the documentation.
|
99
|
+
|
100
|
+
Not only that, but 3 of the 10 steps in the developer guide, in the section
|
101
|
+
detailing how to sign AWS requests, contain critical errors. This means
|
102
|
+
that Amazon didn't once follow their own documentation to verify its
|
103
|
+
correctness.
|
104
|
+
|
105
|
+
Most of the mistakes have since been silently rectified by Amazon, without
|
106
|
+
any mention on the AWS forum or revising the document version. Who knows
|
107
|
+
how often the developer guide changes on a day to day basis. I had thought
|
108
|
+
each dated version to be a static, completed work, but this is apparently
|
109
|
+
not the case.
|
110
|
+
</RANT>
|
2
111
|
|
3
112
|
|
4
113
|
0.5.1 - 2009-03-29
|
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
$Id: README,v 1.
|
1
|
+
$Id: README,v 1.21 2009/05/25 23:38:24 ianmacd Exp $
|
2
2
|
|
3
3
|
|
4
4
|
Introduction
|
@@ -56,9 +56,10 @@ good news is that, in most cases, this isn't as much work as it might sound.
|
|
56
56
|
|
57
57
|
Another bit of good news is that the /etc/amazonrc and ~/.amazonrc files used
|
58
58
|
by Ruby/Amazon _are_ compatible with Ruby/AWS. The only change required for
|
59
|
-
Ruby/AWS is the addition of
|
60
|
-
AWS Access Key ID
|
61
|
-
also supports a more flexible,
|
59
|
+
Ruby/AWS is the addition of the 'key_id' and 'secret_key_id' parameters, which
|
60
|
+
should contain your AWS Access Key ID and its secret counterpart. That fact
|
61
|
+
notwithstanding, as of version 0.5.0, Ruby/AWS also supports a more flexible,
|
62
|
+
locale-specific configuration syntax.
|
62
63
|
|
63
64
|
Amazon finally decomissioned v3 of the AWS API on 2008-03-31. As a result, the
|
64
65
|
original Ruby/Amazon library no longer functions and is therefore obsolete.
|
@@ -79,10 +80,10 @@ Please obtain and use an AWS Access Key ID instead.
|
|
79
80
|
API version
|
80
81
|
-----------
|
81
82
|
|
82
|
-
Ruby/AWS currently requests the 2009-
|
83
|
+
Ruby/AWS currently requests the 2009-03-31 revision of the AWS API when
|
83
84
|
performing its operations:
|
84
85
|
|
85
|
-
http://docs.amazonwebservices.com/AWSECommerceService/2009-
|
86
|
+
http://docs.amazonwebservices.com/AWSECommerceService/2009-03-31/DG/
|
86
87
|
|
87
88
|
However, a different version can be requested via the 'api' parameter in the
|
88
89
|
user configuration file.
|
@@ -91,19 +92,20 @@ user configuration file.
|
|
91
92
|
Status and functionality
|
92
93
|
------------------------
|
93
94
|
|
94
|
-
Ruby/AWS is currently beta
|
95
|
+
Ruby/AWS is currently beta software. Amongst other things, this means:
|
95
96
|
|
96
|
-
- You will encounter bugs, but hopefully not too many and none too serious.
|
97
|
-
|
97
|
+
- You will encounter bugs, but hopefully not too many and none too serious.
|
98
|
+
Tell me about them and I will endeavour to fix them.
|
98
99
|
|
99
|
-
- The documentation
|
100
|
-
|
100
|
+
- The documentation isn't what it could be, but it's hopefully enough to get
|
101
|
+
you up and running.
|
101
102
|
|
102
|
-
- Not all features are currently implemented. Others may not be _fully_
|
103
|
-
implemented.
|
103
|
+
- Not all features are currently implemented. Others may not yet be _fully_
|
104
|
+
implemented. Some, I probably haven't even thought of yet. Again, if
|
105
|
+
something's missing, tell me, and if it makes sense, I'll add it.
|
104
106
|
|
105
|
-
|
106
|
-
small gaps in the functionality of some operations.
|
107
|
+
In spite of this shortcomings, the AWS v4 API is more or less fully
|
108
|
+
supported, with only small gaps in the functionality of some operations.
|
107
109
|
|
108
110
|
Currently implemented operations are:
|
109
111
|
|
@@ -130,19 +132,21 @@ Ruby/AWS is currently beta code. Amongst other things, this means:
|
|
130
132
|
CartModify
|
131
133
|
CartClear
|
132
134
|
|
133
|
-
Version 0.4.0
|
135
|
+
Version 0.4.0 adds the remaining shopping-cart operation, which I had first
|
136
|
+
thought superfluous:
|
134
137
|
|
135
138
|
CartGet
|
136
139
|
|
137
|
-
Multiple operations are supported, but not well tested.
|
140
|
+
Multiple operations are supported, but not well tested (by me, anyway).
|
138
141
|
|
139
142
|
As of version 0.5.0, batch operations are fully supported, using the
|
140
143
|
Operation#batch method.
|
141
144
|
|
142
|
-
Beware of bugs in this area.
|
143
|
-
|
144
|
-
|
145
|
-
determine what works and
|
145
|
+
Beware of bugs in this area. Quite apart from any mistakes I've made in the
|
146
|
+
implementation, there appear to also be (undocumented) Amazon-imposed
|
147
|
+
restrictions on the use of multiple operations and batch requests, so some
|
148
|
+
experimentation will be required on your part to determine what works and
|
149
|
+
what doesn't.
|
146
150
|
|
147
151
|
The 2008-08-19 version of the AWS API added the following operations:
|
148
152
|
|
@@ -150,21 +154,21 @@ Ruby/AWS is currently beta code. Amongst other things, this means:
|
|
150
154
|
VehiclePartSearch
|
151
155
|
VehicleSearch
|
152
156
|
|
153
|
-
These are supported by Ruby/AWS
|
157
|
+
These are supported by Ruby/AWS from version 0.5.0 onwards.
|
154
158
|
|
155
159
|
- Classes, methods, constants and instance variables may change name in the
|
156
|
-
future.
|
157
|
-
shrink or disappear
|
158
|
-
|
160
|
+
future. New ones may appear from nowhere and existing ones may change shape,
|
161
|
+
grow, shrink or disappear without trace. Such fundamental changes will break
|
162
|
+
existing code, so I will endeavour to keep them to a minimum.
|
159
163
|
|
160
164
|
In short, code written to work with this release of Ruby/AWS may stop working
|
161
165
|
when you upgrade to the next. In fact, it may even stop working _during_ this
|
162
|
-
release, because it's possible there are
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
+
release cycle, because it's possible there are fatal conditions that I didn't
|
167
|
+
encounter in my limited testing of the code. It's also possible that future
|
168
|
+
(possibly unannounced) changes made by Amazon will affect Ruby/AWS in
|
169
|
+
ways I can't anticipate.
|
166
170
|
|
167
|
-
That said, the Ruby/AWS
|
171
|
+
That said, the Ruby/AWS API is pretty stable at this point in time. I won't
|
168
172
|
break any of the method interfaces without seriously considering the merits of
|
169
173
|
doing so.
|
170
174
|
|
@@ -179,22 +183,43 @@ choose between an installation script and a RubyGems installation.
|
|
179
183
|
Usage
|
180
184
|
-----
|
181
185
|
|
182
|
-
First of all, create either /etc/amazonrc or ~/.amazonrc
|
183
|
-
look something like this:
|
186
|
+
First of all, create either /etc/amazonrc or ~/.amazonrc as a plain text file.
|
187
|
+
Its contents should look something like this:
|
184
188
|
|
185
189
|
# Any line that starts with a hash character is a comment.
|
186
190
|
key_id = '0Y44V8G41KCQPGF6XYZ2'
|
191
|
+
secret_key_id = 'k+kuddeoQJzUnImC0Hyy21J4xLWQc1hbvfQ+7F1G
|
187
192
|
associate = 'fuzbarorg-21'
|
188
193
|
cache = false
|
189
194
|
locale = 'uk'
|
195
|
+
encoding = 'iso-8859-15'
|
196
|
+
|
197
|
+
Including your secret key is a new feature in version 0.6.0 of Ruby/AWS. If
|
198
|
+
you choose to do so, your requests to AWS will be signed for authentication by
|
199
|
+
the Amazon's servers.
|
200
|
+
|
201
|
+
Amazon plans to make this practice obligatory as of 15th August 2009, so I
|
202
|
+
recommend that you adopt it well ahead of time.
|
203
|
+
|
204
|
+
When Amazon checks for a valid signature, it does so by comparing its
|
205
|
+
computation of the signature with the one supplied by the user. In doing this,
|
206
|
+
Amazon uses the UTF-8 representation of parameter values that you supply, even
|
207
|
+
if you use a different encoding.
|
208
|
+
|
209
|
+
In order to have Ruby/AWS properly reencode your strings as UTF-8, you need
|
210
|
+
to tell it which encoding you are using. The 'encoding' parameter can be used
|
211
|
+
for this, but you can omit it if your strings are already UTF-8, because this
|
212
|
+
is the default.
|
190
213
|
|
191
214
|
As of version 0.5.0 of Ruby/AWS, the following locale-specific configuration
|
192
215
|
syntax is also supported:
|
193
216
|
|
194
217
|
[global]
|
195
218
|
key_id = '0Y44V8G41KCQPGF6XYZ2'
|
196
|
-
|
219
|
+
secret_key_id = 'k+kuddeoQJzUnImC0Hyy21J4xLWQc1hbvfQ+7F1G
|
197
220
|
cache = false
|
221
|
+
locale = 'uk'
|
222
|
+
encoding = 'iso-8859-15'
|
198
223
|
# Request a specific version of the API.
|
199
224
|
# api = '2008-03-03'
|
200
225
|
|
@@ -204,16 +229,20 @@ syntax is also supported:
|
|
204
229
|
[us]
|
205
230
|
associate = 'fuzbarorg-20'
|
206
231
|
|
207
|
-
|
208
|
-
|
232
|
+
This enables the use of a different associate tag for each locale, although,
|
233
|
+
oddly enough, no-one has ever requested this feature.
|
234
|
+
|
235
|
+
Because you're embedding your personal keys in the file, you should protect it
|
236
|
+
(on UNIX and equivalent systems) by making it mode 0600:
|
209
237
|
|
210
238
|
$ chmod 600 ~/.amazonrc
|
211
239
|
|
212
|
-
If you define 'cache' to be 'true', you may also define 'cache_dir' to
|
213
|
-
to somewhere other the default, /tmp/amazon.
|
240
|
+
If you define 'cache' to be 'true', you may want to also define 'cache_dir' to
|
241
|
+
point to somewhere other the default cache directory, /tmp/amazon.
|
214
242
|
|
215
243
|
If you want to place .amazonrc somewhere other than $HOME, you may set
|
216
|
-
$AMAZONRCDIR
|
244
|
+
the $AMAZONRCDIR environment variable, as this location is checked prior to
|
245
|
+
$HOME.
|
217
246
|
|
218
247
|
If you're using Windows, $HOME is usually undefined, so a number of additional
|
219
248
|
locations are checked for .amazonrc.
|
@@ -230,13 +259,13 @@ $AMAZONRCDIR and $HOME are defined, but only the path specified by $HOME
|
|
230
259
|
contains a file called .amazonrc, it will not be found.
|
231
260
|
|
232
261
|
If you want the user configuration file to be called something other than
|
233
|
-
.amazonrc, you may define $AMAZONRCFILE
|
262
|
+
.amazonrc, you may define the $AMAZONRCFILE environment variable.
|
234
263
|
|
235
|
-
Once you have your configuration file, you can get started
|
264
|
+
Once you have your configuration file, you can get started writing your code.
|
236
265
|
|
237
|
-
Here's
|
266
|
+
Here's a basic example, showing how to perform an ItemSearch, probably the
|
238
267
|
most common type of AWS operation. Please see the ./examples subdirectory for
|
239
|
-
more examples of working code.
|
268
|
+
more examples of contrived, but working code.
|
240
269
|
|
241
270
|
--
|
242
271
|
|
@@ -288,11 +317,13 @@ XML to Ruby mapping
|
|
288
317
|
-------------------
|
289
318
|
|
290
319
|
Here, I will discuss the mapping of the XML returned from AWS to native Ruby
|
291
|
-
objects and data.
|
320
|
+
objects and data. Note that the XML shown below was that returned at the time
|
321
|
+
of writing and may look different to what you would see today if you were to
|
322
|
+
execute the same request.
|
292
323
|
|
293
|
-
When
|
324
|
+
When this code:
|
294
325
|
|
295
|
-
resp = req.search( is, rg )
|
326
|
+
resp = req.search( is, rg )
|
296
327
|
|
297
328
|
was called in the previous section, the following URL was composed and sent to
|
298
329
|
AWS as an HTTP GET operation:
|
@@ -370,7 +401,7 @@ the following statements would all be true:
|
|
370
401
|
- ItemAttributes, Author, Manufacturer, ProductGroup and Title would all be
|
371
402
|
dynamically defined subclasses of AWSObject.
|
372
403
|
|
373
|
-
- An instance of the ItemAttributes class would be
|
404
|
+
- An instance of the ItemAttributes class would be created, with instance
|
374
405
|
variables @author, @manufacturer, @product_group and @title.
|
375
406
|
|
376
407
|
- To each of these instance variables would respectively be assigned an array
|
@@ -379,13 +410,15 @@ the following statements would all be true:
|
|
379
410
|
would all be single element arrays, because there's only one instance of
|
380
411
|
each kind of tag in the XML.
|
381
412
|
|
382
|
-
-
|
383
|
-
|
384
|
-
|
413
|
+
- The Author, Manufacturer, ProductGroup and Title objects would have no
|
414
|
+
instance variables of their own, because the corresponding XML elements
|
415
|
+
have no children, just a value. These objects are therefore directly
|
416
|
+
assigned the value in question.
|
385
417
|
|
386
|
-
So, if resp is the top level AWSObject created and returned by calling
|
387
|
-
Amazon::AWS::Search::Request#search
|
388
|
-
know the ASIN of the first item found, we can refer to this as
|
418
|
+
So, if resp is the top level AWSObject created and returned by calling the
|
419
|
+
Amazon::AWS::Search::Request#search method of the Request object, and we'd
|
420
|
+
like to know the ASIN of the first item found, we can refer to this as
|
421
|
+
follows:
|
389
422
|
|
390
423
|
resp.item_search_response[0].items[0].item[0].asin
|
391
424
|
|
@@ -393,101 +426,105 @@ Looking at each component of this chain in turn:
|
|
393
426
|
|
394
427
|
- resp is an AWSObject with a single instance variable, @item_search_response.
|
395
428
|
This is because the entire XML response is contained within a single
|
396
|
-
<ItemSearchResponse>
|
429
|
+
<ItemSearchResponse> element, so there's nothing else at the top level.
|
397
430
|
|
398
431
|
- resp.item_search_response is assigned an array of ItemSearchResponse
|
399
|
-
objects. Because there's only a single <ItemSearchResponse>
|
432
|
+
objects. Because there's only a single <ItemSearchResponse> element in the
|
400
433
|
whole document (containing the rest of the XML), the array contains only a
|
401
434
|
single element.
|
402
435
|
|
403
436
|
- resp.item_search_response[0] has an instance variable, @items, which is
|
404
437
|
assigned an array of Items objects. Here again, only a single element is
|
405
|
-
created, because there's only one <Items>
|
438
|
+
created, because there's only one corresponding <Items> element in the XML.
|
406
439
|
|
407
440
|
- resp.item_search_response[0].items[0] has an instance variable, @item, which
|
408
|
-
is an array containing the item(s)
|
441
|
+
is an array containing the actual item(s) located by the search. It is a
|
409
442
|
multi-element array, however, because more than one item was found, as
|
410
443
|
represented by the multiple <Item> elements in the XML.
|
411
444
|
|
412
445
|
The creation of so many single element arrays is unfortunate. It makes user
|
413
446
|
code verboser, uglier and consequently harder to read.
|
414
447
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
448
|
+
You might wonder why Ruby/AWS doesn't just assign the single element itself,
|
449
|
+
rather than the array that contains it.
|
450
|
+
|
451
|
+
The answer is that most of these single-element arrays actually do have the
|
452
|
+
potential to be multi-element, because the corresponding XML tag _can_ appear
|
453
|
+
multiple times in an AWS response. A book, for example, _may_ have more than
|
454
|
+
one <Author>. Many other types of array, however, are necessarily single
|
455
|
+
element arrays. That same book, for example, is unlikely to have more than one
|
456
|
+
<Title>. This is context-dependent and difficult to define programatically.
|
420
457
|
|
421
|
-
As another concrete example, an ItemSearch will probably
|
458
|
+
As another concrete example, an ItemSearch will probably yield many <Item>
|
422
459
|
elements in the <ItemSearchResponse>, but these will invariably be nested in a
|
423
|
-
single <Items>
|
424
|
-
object will therefore always
|
460
|
+
single <Items> element. The @items instance variable of the ItemSearchResponse
|
461
|
+
object will therefore always be a single-element array.
|
425
462
|
|
426
|
-
In other words, the following statements are
|
427
|
-
successfully
|
463
|
+
In other words, the following statements are both invariably true when an
|
464
|
+
ItemSearch successfully locates items:
|
428
465
|
|
429
466
|
- resp.item_search_response[0].items.size == 1
|
430
467
|
|
431
468
|
- resp.item_search_response[0].items[0].item.size >= 1
|
432
469
|
|
433
470
|
The awkwardness of using such single element arrays is alleviated in Ruby/AWS
|
434
|
-
by the use of the AWSArray subclass.
|
435
|
-
array
|
436
|
-
|
471
|
+
by the use of the AWSArray subclass. An instance of this class differs from a
|
472
|
+
standard array by allowing element 0 of a single-element array to be
|
473
|
+
dereferenced using just the array name, i.e. without a subscript.
|
437
474
|
|
438
|
-
In other words, a reference to foo.bar will actually return foo[0].bar
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
method.
|
475
|
+
In other words, a reference to foo.bar will actually return foo[0].bar when
|
476
|
+
foo.size == 1. Note that this can only work because the array itself, foo, has
|
477
|
+
no bar method, so the intention is unambiguous and foo can delegate the
|
478
|
+
invocation of the method to foo[0]. foo.size, on the other hand, will _always_
|
479
|
+
invoke foo's bar method, never delegating to foo[0], because of the existence
|
480
|
+
of the Array#size method.
|
444
481
|
|
445
482
|
This allows the ASIN of the first item returned in the above XML to be
|
446
483
|
referred to using the following shorthand:
|
447
484
|
|
448
485
|
resp.item_search_response.items.item[0].asin
|
449
486
|
|
450
|
-
It's worth reiterating that it's still necessary to refer to
|
451
|
-
|
452
|
-
multiple <Item>
|
487
|
+
It's worth reiterating that it's still necessary in this example to refer to
|
488
|
+
item[0] using a subscript, because the <Items> element in the XML contains
|
489
|
+
multiple <Item> elements, making item.size > 1.
|
453
490
|
|
454
491
|
Use this syntactic shorthand to your advantage, but understand when you're
|
455
|
-
likely to be dealing with a single element array
|
456
|
-
|
492
|
+
likely to be dealing with a single element array vs. a multiple. This will
|
493
|
+
become apparent as you gain familiarity with AWS v4.
|
457
494
|
|
458
|
-
An exception will be
|
459
|
-
array, as it can't be known which element the method invocation
|
460
|
-
|
461
|
-
that an array contains only a single element when
|
462
|
-
multiple.
|
495
|
+
An exception will be raised if an unknown method is called on a multi-element
|
496
|
+
array, as it can't be known to which array element the method invocation
|
497
|
+
should be delegated. This will almost certainly stem from an incorrect
|
498
|
+
assumption that an array contains only a single element when, in actual fact,
|
499
|
+
it contains multiple elements.
|
463
500
|
|
464
|
-
A further important detail to note is that not all AWS operations
|
501
|
+
A further important detail to note is that not all AWS operations of the same
|
465
502
|
class return the same data. For example, an ItemSearch using the Books search
|
466
|
-
index will return items that, amongst other things,
|
503
|
+
index will return items that, amongst other things, have an ItemAttributes
|
467
504
|
object containing further objects of class Author, ISBN, etc. An ItemSearch
|
468
|
-
using the DVD search index,
|
469
|
-
have a Director and probably one or more Actor objects.
|
505
|
+
using the DVD search index, by contrast, will have no Author or ISBN, but
|
506
|
+
will likely have a Director and probably one or more Actor objects.
|
470
507
|
|
471
508
|
Because of the disparity in same-class object attributes, Ruby/AWS returns
|
472
509
|
*nil* when an attempt is made to dereference a non-existent instance variable.
|
473
|
-
This approach was chosen
|
474
|
-
precisely which data will be returned by a given search.
|
475
|
-
non-existent attributes
|
476
|
-
with exception-handling clauses.
|
510
|
+
This approach was chosen because, more often than not, it cannot be known in
|
511
|
+
advance precisely which data will be returned by a given search operation.
|
512
|
+
Returning *nil* for non-existent attributes saves the user from having to
|
513
|
+
pepper their code with exception-handling clauses.
|
477
514
|
|
478
515
|
For example:
|
479
516
|
|
480
|
-
resp.item_search_response[0].items[0].item[0].item_attributes.director
|
517
|
+
resp.item_search_response[0].items[0].item[0].item_attributes.director
|
481
518
|
|
482
519
|
will return *nil* for a book, because there was no corresponding Director
|
483
520
|
element in the XML returned by AWS.
|
484
521
|
|
485
522
|
Similarly:
|
486
523
|
|
487
|
-
resp.item_search_response[0].items[0].item[0].item_attributes.foo_bar
|
524
|
+
resp.item_search_response[0].items[0].item[0].item_attributes.foo_bar
|
488
525
|
|
489
|
-
will _always_ return *nil* for
|
490
|
-
ever yield an item with a FooBar
|
526
|
+
will _always_ return *nil* for _any_ item, because no kind of ItemSearch will
|
527
|
+
ever yield an item with a FooBar element.
|
491
528
|
|
492
529
|
|
493
530
|
Parameter checking
|
@@ -498,18 +535,19 @@ particular type of search. For example, an ItemSearch can use a Sort parameter
|
|
498
535
|
with a value of 'titlerank' if the SearchIndex is 'Books'. However, this value
|
499
536
|
wouldn't make much sense in the 'Automotive' SearchIndex.
|
500
537
|
|
501
|
-
The very presence of a certain parameter can be illegal in
|
502
|
-
For example, specifying the parameter 'Author' with
|
538
|
+
The very presence of a certain parameter can be illegal in certain contexts.
|
539
|
+
For example, specifying the parameter 'Author' with _any_ value would be
|
503
540
|
nonsensical in the 'PetSupplies' SearchIndex.
|
504
541
|
|
505
542
|
To complicate things further, the validity of parameters and their values
|
506
543
|
differs not only by search type, but also by Amazon locale (amazon.com,
|
507
|
-
amazon.co.uk, amazon.de, etc.) and is prone to change with
|
508
|
-
|
544
|
+
amazon.co.uk, amazon.de, etc.) and is prone to change with minor revisions of
|
545
|
+
the Amazon AWS API.
|
509
546
|
|
510
547
|
Even worse, the operations themselves can be illegal in certain locales.
|
511
|
-
TransactionLookup operations, for example, don't
|
512
|
-
|
548
|
+
TransactionLookup operations, for example, don't work in the UK locale at the
|
549
|
+
time of writing, but do work in the US locale. As a rule of thumb, we can say
|
550
|
+
that almost everything works in the US locale and _may_ work in others.
|
513
551
|
|
514
552
|
Ruby/Amazon attempted to track these complex and dynamic relationships to
|
515
553
|
prevent illegal or ineffective operations from being attempted. It was a
|
@@ -517,24 +555,25 @@ time-consuming and tedious task to track the evolving API (which often changed
|
|
517
555
|
in subtle ways without prior [or even belated] notice from Amazon), find all
|
518
556
|
of the corner cases and handle undocumented quirks.
|
519
557
|
|
520
|
-
With the highly dynamic nature of the Amazon environment
|
521
|
-
number of operations, parameters
|
522
|
-
v4 API, this strict approach would be completely
|
523
|
-
therefore doesn't even try.
|
558
|
+
With the highly dynamic nature of the Amazon environment and its many locales,
|
559
|
+
plus the sheer number of operations, parameters and their possibly legal
|
560
|
+
values in the AWS v4 API, this strict approach would be completely
|
561
|
+
impractical for Ruby/AWS. It therefore doesn't even try.
|
524
562
|
|
525
563
|
Instead, it's now up to you to ensure that you perform legal operations and
|
526
|
-
pass
|
564
|
+
pass sensible parameters and values for the locale in which you're working.
|
565
|
+
The context is now your responsibility.
|
527
566
|
|
528
567
|
The one exception to this rule is search index checking for ItemSearch
|
529
568
|
operations. Code that attempts to use an invalid SearchIndex will raise an
|
530
|
-
exception. The list of allowable search indices can be found in
|
531
|
-
Amazon::AWS::Operation::ItemSearch::SEARCH_INDICES.
|
569
|
+
exception. The list of allowable search indices can be found in the
|
570
|
+
Amazon::AWS::Operation::ItemSearch::SEARCH_INDICES array.
|
532
571
|
|
533
572
|
Of course, even this check exposes the user to the risk that Amazon may later
|
534
573
|
add new search indices, which would continue to be unrecognised and ruled
|
535
|
-
invalid by Ruby/AWS until
|
536
|
-
this very basic level of checking, it may be removed in the future
|
537
|
-
becomes impractical to keep it current.
|
574
|
+
invalid by Ruby/AWS until an update was issued. Whilst I have chosen to
|
575
|
+
implement this very basic level of checking, it may be removed in the future
|
576
|
+
if it becomes impractical to keep it current.
|
538
577
|
|
539
578
|
In short, the validity of what goes into a search operation is your own
|
540
579
|
responsibility: garbage in, garbage out.
|
@@ -542,34 +581,35 @@ responsibility: garbage in, garbage out.
|
|
542
581
|
Thankfully, with the AWS Developer Guide at your side, it's largely common
|
543
582
|
sense which parameters and values can be used with each type of search. It's
|
544
583
|
less obvious when these differ by locale. For example, the 'Beauty'
|
545
|
-
SearchIndex was valid in the 'us', but not in the 'uk' until the
|
546
|
-
revision of the AWS API.
|
584
|
+
SearchIndex was valid in the 'us' locale, but not in the 'uk' locale until the
|
585
|
+
2009-01-06 revision of the AWS API.
|
547
586
|
|
548
587
|
Unfortunately, AWS abounds with such inconsistencies and they are prone to
|
549
|
-
change at any time.
|
588
|
+
change at any time. Amazon, themselves, seem to struggle to document all of
|
589
|
+
these quirks, a situation probably aggravated by the US focus of the AWS
|
590
|
+
staff.
|
550
591
|
|
551
|
-
The only way to apprise yourself of such
|
552
|
-
developer documentation (and closely follow the release notes of each
|
553
|
-
API revision to make sure things haven't changed). If you don't want to
|
554
|
-
exposed to such API changes, use the 'api' parameter in the user
|
555
|
-
file to request a particular version of the API.
|
592
|
+
The only way to apprise yourself of such peculiarities is to read Amazon's
|
593
|
+
latest developer documentation (and closely follow the release notes of each
|
594
|
+
minor API revision to make sure things haven't changed). If you don't want to
|
595
|
+
be exposed to such API changes, use the 'api' parameter in the user
|
596
|
+
configuration file to request a particular version of the API.
|
556
597
|
|
557
598
|
The AWS Developer Connection pages may also be of use to you. In particular,
|
558
599
|
the forum for discussing AWS has proved useful to me over the years:
|
559
600
|
|
560
601
|
http://developer.amazonwebservices.com/connect/forum.jspa?forumID=9
|
561
602
|
|
562
|
-
For those illegal operations that make it through
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
raise an exception of that class.
|
603
|
+
For those illegal operations that make it through to the Amazon servers, the
|
604
|
+
good news is that Amazon carries out extensive run-time parameter checking in
|
605
|
+
AWS v4 (much better than in v3) and will generate an error when an illegal set
|
606
|
+
of parameters and/or values is given. Ruby/AWS will dynamically generate a
|
607
|
+
class for the type of error reported and raise an exception of that class.
|
568
608
|
|
569
609
|
Using this approach, Ruby/AWS doesn't have to perform checks that Amazon will
|
570
|
-
perform
|
571
|
-
|
572
|
-
|
610
|
+
perform, anyway. This helps keep the code base leaner, the library faster, and
|
611
|
+
reduces the chance that Ruby/AWS will disallow an operation that becomes valid
|
612
|
+
following a minor revision of AWS.
|
573
613
|
|
574
614
|
|
575
615
|
Documentation
|
@@ -580,14 +620,14 @@ command, executed from the directory created when you unpacked the archive:
|
|
580
620
|
|
581
621
|
rdoc -SUx CVS lib
|
582
622
|
|
583
|
-
The documentation on how to use this library is currently incomplete, but
|
584
|
-
|
623
|
+
The documentation on how to use this library is currently incomplete, but it
|
624
|
+
should be enough to get you started.
|
585
625
|
|
586
626
|
You can also use the Ruby/AWS mailing-list:
|
587
627
|
|
588
628
|
http://www.caliban.org/mailman/listinfo/ruby-aws
|
589
629
|
|
590
|
-
to discuss
|
630
|
+
to discuss any Ruby/AWS-related subjects and issues.
|
591
631
|
|
592
632
|
|
593
633
|
Examples
|
data/README.rdoc
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# $Id: README.rdoc,v 1.
|
2
|
+
# $Id: README.rdoc,v 1.23 2009/05/26 15:25:19 ianmacd Exp $
|
3
3
|
#++
|
4
4
|
#
|
5
5
|
#
|
@@ -53,19 +53,22 @@
|
|
53
53
|
# CartClear
|
54
54
|
# CartGet
|
55
55
|
#
|
56
|
-
#
|
56
|
+
# Finally, multiple operations and batch requests are also supported.
|
57
57
|
#
|
58
|
-
# Ruby/AWS
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
58
|
+
# Ruby/AWS supports request authentication, using your secret key to sign your
|
59
|
+
# requests to AWS.
|
60
|
+
#
|
61
|
+
# Beyond wrapping features readily available in the AWS API, Ruby/AWS also
|
62
|
+
# offers advanced features not directly supported by the AWS API, such as the
|
63
|
+
# ability to retrieve *all* results pages for a particular search, rather than
|
64
|
+
# having to manually deal with multiple AWS responses of 10 results per page.
|
62
65
|
#
|
63
66
|
# You can also retrieve product images and optionally overlay them with
|
64
67
|
# percentage discount icons.
|
65
68
|
#
|
66
69
|
# Another advanced feature is the ability to cache responses returned by AWS.
|
67
70
|
# If the cache is used (as it is by default), the results of each unique
|
68
|
-
#
|
71
|
+
# search will be cached and used for 24 hours. The cache can be manually
|
69
72
|
# flushed of all or just the expired entries.
|
70
73
|
#
|
71
74
|
# One other useful advanced feature is the ability to determine the
|
@@ -82,9 +85,9 @@
|
|
82
85
|
# to install Ruby/AWS. You can choose between an installation script and a
|
83
86
|
# RubyGems[http://www.rubygems.org/] installation.
|
84
87
|
#
|
85
|
-
# Note, however, if
|
86
|
-
#
|
87
|
-
#
|
88
|
+
# Note, however, if opting for the gem installation, that Ruby/AWS's RubyForge
|
89
|
+
# UNIX name is now ruby-aaws. The ruby-aws name was taken by {another
|
90
|
+
# project}[http://rubyforge.org/projects/ruby-aws/] and this namespace clash
|
88
91
|
# prevented remote installation of the Ruby/AWS gem.
|
89
92
|
#
|
90
93
|
#
|
@@ -95,17 +98,17 @@
|
|
95
98
|
# ID}[https://aws-portal.amazon.com/gp/aws/developer/registration/index.html].
|
96
99
|
#
|
97
100
|
# You should also apply for an {Associates
|
98
|
-
# account}[http://docs.amazonwebservices.com/AWSECommerceService/2009-
|
101
|
+
# account}[http://docs.amazonwebservices.com/AWSECommerceService/2009-03-31/GSG/BecominganAssociate.html],
|
99
102
|
# although this isn't strictly necessary. If you do not explicitly provide an
|
100
|
-
# Associates tag in
|
101
|
-
# author will be used by default.
|
103
|
+
# Associates tag in the operations you conduct via Ruby/AWS, the tag of the
|
104
|
+
# Ruby/AWS author will be used by default.
|
102
105
|
#
|
103
106
|
#
|
104
107
|
# == See Also
|
105
108
|
#
|
106
|
-
# Ultimately, the way to get the most from
|
109
|
+
# Ultimately, the way to get the most from Ruby/AWS is to read the AWS
|
107
110
|
# documentation to get a feel for what is possible, and then experiment with
|
108
|
-
#
|
111
|
+
# the library to see how the calls to AWS are mapped to the Ruby world. You
|
109
112
|
# should also review this library's
|
110
113
|
# RDoc[http://www.ruby-doc.org/core/classes/RDoc.html]
|
111
114
|
# documentation[http://www.caliban.org/ruby/ruby-aws/] as well as the
|
@@ -113,26 +116,28 @@
|
|
113
116
|
#
|
114
117
|
# Additionally, there's a
|
115
118
|
# {mailing-list}[http://www.caliban.org/mailman/listinfo/ruby-aws] available,
|
116
|
-
# where you can discuss
|
119
|
+
# where you can discuss any Ruby/AWS-related subjects and issues.
|
117
120
|
#
|
118
|
-
# Please see the Amazon Web Services
|
119
|
-
# documentation[http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=5]
|
121
|
+
# Please see the Amazon Web Services API
|
122
|
+
# documentation[http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=5],
|
123
|
+
# not forgetting the {release
|
124
|
+
# notes}[http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=17]
|
120
125
|
# for definitive information on the capabilities and inner workings of the AWS
|
121
126
|
# API.
|
122
127
|
#
|
123
128
|
#
|
124
129
|
# == Download
|
125
130
|
#
|
126
|
-
# Version 0.
|
127
|
-
# === {gzip'ed tar archive}[http://www.caliban.org/files/ruby/ruby-aws-0.
|
128
|
-
# === {Ruby Gem}[http://www.caliban.org/files/ruby/ruby-aaws-0.
|
129
|
-
# === {Fedora 9 RPM}[http://www.caliban.org/files/redhat/RPMS/noarch/ruby-aws-0.
|
130
|
-
# === {Fedora 9 doc RPM}[http://www.caliban.org/files/redhat/RPMS/noarch/ruby-aws-doc-0.
|
131
|
-
# === {Fedora 9 source RPM}[http://www.caliban.org/files/redhat/SRPMS/ruby-aws-0.
|
131
|
+
# Version 0.6.0
|
132
|
+
# === {gzip'ed tar archive}[http://www.caliban.org/files/ruby/ruby-aws-0.6.0.tar.gz]
|
133
|
+
# === {Ruby Gem}[http://www.caliban.org/files/ruby/ruby-aaws-0.6.0.gem]
|
134
|
+
# === {Fedora 9 RPM}[http://www.caliban.org/files/redhat/RPMS/noarch/ruby-aws-0.6.0-1.fc9.noarch.rpm]
|
135
|
+
# === {Fedora 9 doc RPM}[http://www.caliban.org/files/redhat/RPMS/noarch/ruby-aws-doc-0.6.0-1.fc9.noarch.rpm]
|
136
|
+
# === {Fedora 9 source RPM}[http://www.caliban.org/files/redhat/SRPMS/ruby-aws-0.6.0-1.fc9.src.rpm]
|
132
137
|
#
|
133
138
|
#
|
134
139
|
# ---
|
135
140
|
# Author:: Ian Macdonald <mailto:ian@caliban.org>
|
136
|
-
# Version:: 0.
|
141
|
+
# Version:: 0.6.0
|
137
142
|
# Copyright:: (C) 2008-2009 Ian Macdonald
|
138
143
|
# Licence:: GPL[http://www.gnu.org/copyleft/gpl.html]
|
data/lib/amazon.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: amazon.rb,v 1.
|
1
|
+
# $Id: amazon.rb,v 1.28 2009/05/26 15:25:20 ianmacd Exp $
|
2
2
|
#
|
3
3
|
|
4
4
|
module Amazon
|
@@ -8,6 +8,7 @@ module Amazon
|
|
8
8
|
class AmazonError < StandardError; end
|
9
9
|
|
10
10
|
NAME = 'Ruby/Amazon'
|
11
|
+
|
11
12
|
@@config = {}
|
12
13
|
|
13
14
|
# Prints debugging messages and works like printf, except that it prints
|
@@ -22,11 +23,12 @@ module Amazon
|
|
22
23
|
#
|
23
24
|
def Amazon.url_encode(string)
|
24
25
|
|
25
|
-
# Shamelessly plagiarised from Wakou Aoyama's cgi.rb
|
26
|
+
# Shamelessly plagiarised from Wakou Aoyama's cgi.rb, but then altered
|
27
|
+
# slightly to please AWS.
|
26
28
|
#
|
27
|
-
string.gsub( /([^
|
29
|
+
string.gsub( /([^a-zA-Z0-9_.~-]+)/n ) do
|
28
30
|
'%' + $1.unpack( 'H2' * $1.size ).join( '%' ).upcase
|
29
|
-
end
|
31
|
+
end
|
30
32
|
end
|
31
33
|
|
32
34
|
|
data/lib/amazon/aws.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: aws.rb,v 1.
|
1
|
+
# $Id: aws.rb,v 1.97 2009/05/26 15:24:31 ianmacd Exp $
|
2
2
|
#
|
3
3
|
#:include: ../../README.rdoc
|
4
4
|
|
@@ -6,13 +6,14 @@ module Amazon
|
|
6
6
|
|
7
7
|
module AWS
|
8
8
|
|
9
|
-
require 'uri'
|
10
9
|
require 'amazon'
|
11
10
|
require 'amazon/aws/cache'
|
11
|
+
require 'iconv'
|
12
12
|
require 'rexml/document'
|
13
|
+
require 'uri'
|
13
14
|
|
14
15
|
NAME = '%s/%s' % [ Amazon::NAME, 'AWS' ]
|
15
|
-
VERSION = '0.
|
16
|
+
VERSION = '0.6.0'
|
16
17
|
USER_AGENT = '%s %s' % [ NAME, VERSION ]
|
17
18
|
|
18
19
|
# Default Associate tags to use per locale.
|
@@ -30,7 +31,7 @@ module Amazon
|
|
30
31
|
# changed via the user configuration file.
|
31
32
|
#
|
32
33
|
SERVICE = { 'Service' => 'AWSECommerceService',
|
33
|
-
'Version' => '2009-
|
34
|
+
'Version' => '2009-03-31'
|
34
35
|
}
|
35
36
|
|
36
37
|
# Maximum number of 301 and 302 HTTP responses to follow, should Amazon
|
@@ -66,6 +67,11 @@ module Amazon
|
|
66
67
|
# ReviewPage 20
|
67
68
|
|
68
69
|
|
70
|
+
# A hash to store character encoding converters.
|
71
|
+
#
|
72
|
+
@@encodings = {}
|
73
|
+
|
74
|
+
|
69
75
|
# Exception class for HTTP errors.
|
70
76
|
#
|
71
77
|
class HTTPError < AmazonError; end
|
@@ -96,11 +102,12 @@ module Amazon
|
|
96
102
|
'us' => Endpoint.new( 'http://ecs.amazonaws.com/onca/xml' )
|
97
103
|
}
|
98
104
|
|
105
|
+
|
99
106
|
# Fetch a page, either from the cache or by HTTP. This is used internally.
|
100
107
|
#
|
101
|
-
def AWS.get_page(request
|
108
|
+
def AWS.get_page(request) # :nodoc:
|
102
109
|
|
103
|
-
url = ENDPOINT[request.locale].path + query
|
110
|
+
url = ENDPOINT[request.locale].path + request.query
|
104
111
|
cache_url = ENDPOINT[request.locale].host + url
|
105
112
|
|
106
113
|
# Check for cached page and return that if it's there.
|
@@ -110,6 +117,14 @@ module Amazon
|
|
110
117
|
return body if body
|
111
118
|
end
|
112
119
|
|
120
|
+
# Check whether we have a secret key available for signing the request.
|
121
|
+
# If so, sign the request for authentication.
|
122
|
+
#
|
123
|
+
if request.config['secret_key_id']
|
124
|
+
request.sign
|
125
|
+
url = ENDPOINT[request.locale].path + request.query
|
126
|
+
end
|
127
|
+
|
113
128
|
# Get the existing connection. If there isn't one, force a new one.
|
114
129
|
#
|
115
130
|
conn = request.conn || request.reconnect.conn
|
@@ -124,7 +139,9 @@ module Amazon
|
|
124
139
|
# just not passed by here recently), the HTTP connection to the server
|
125
140
|
# will probably have timed out.
|
126
141
|
#
|
127
|
-
rescue Errno::
|
142
|
+
rescue EOFError, Errno::ECONNABORTED, Errno::ECONNREFUSED,
|
143
|
+
Errno::ECONNRESET, Errno::EPIPE, Errno::ETIMEDOUT,
|
144
|
+
Timeout::Error => error
|
128
145
|
Amazon.dprintf( 'Connection to server lost: %s. Retrying...', error )
|
129
146
|
conn = request.reconnect.conn
|
130
147
|
retry
|
@@ -159,16 +176,23 @@ module Amazon
|
|
159
176
|
end
|
160
177
|
|
161
178
|
|
162
|
-
def AWS.assemble_query(items) # :nodoc:
|
179
|
+
def AWS.assemble_query(items, encoding=nil) # :nodoc:
|
180
|
+
|
163
181
|
query = ''
|
182
|
+
@@encodings[encoding] ||= Iconv.new( 'utf-8', encoding ) if encoding
|
164
183
|
|
165
184
|
# We must sort the items into an array to get reproducible ordering
|
166
185
|
# of the query parameters. Otherwise, URL caching would not work. We
|
167
|
-
# must also convert the
|
168
|
-
# as the
|
186
|
+
# must also convert the parameter values to strings, in case Symbols
|
187
|
+
# have been used as the values.
|
169
188
|
#
|
170
189
|
items.sort { |a,b| a.to_s <=> b.to_s }.each do |k, v|
|
171
|
-
|
190
|
+
if encoding
|
191
|
+
query << '&%s=%s' %
|
192
|
+
[ k, Amazon.url_encode( @@encodings[encoding].iconv( v.to_s ) ) ]
|
193
|
+
else
|
194
|
+
query << '&%s=%s' % [ k, Amazon.url_encode( v.to_s ) ]
|
195
|
+
end
|
172
196
|
end
|
173
197
|
|
174
198
|
# Replace initial ampersand with question-mark.
|
@@ -819,13 +843,6 @@ module Amazon
|
|
819
843
|
# indices.
|
820
844
|
# - *Video* combines the DVD and VHS search indices.
|
821
845
|
#
|
822
|
-
# Note that {page
|
823
|
-
# 53}[http://docs.amazonwebservices.com/AWSECommerceService/2009-01-06/DG/SearchIndices.html]
|
824
|
-
# of the PDF of the AWS Developer Guide (revision 2009-01-06) contains
|
825
|
-
# an outdated description of *Blended* with too few subindices. {Page
|
826
|
-
# 95}[http://docs.amazonwebservices.com/AWSECommerceService/2009-01-06/DG/CommonItemSearchParameters.html]
|
827
|
-
# of the PDF contains the correct list.
|
828
|
-
#
|
829
846
|
SEARCH_INDICES = %w[
|
830
847
|
All
|
831
848
|
Apparel
|
data/lib/amazon/aws/search.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: search.rb,v 1.
|
1
|
+
# $Id: search.rb,v 1.35 2009/05/26 16:27:12 ianmacd Exp $
|
2
2
|
#
|
3
3
|
|
4
4
|
module Amazon
|
@@ -8,6 +8,7 @@ module Amazon
|
|
8
8
|
require 'amazon/aws'
|
9
9
|
require 'net/http'
|
10
10
|
require 'rexml/document'
|
11
|
+
require 'openssl'
|
11
12
|
|
12
13
|
# Load this library with:
|
13
14
|
#
|
@@ -27,8 +28,14 @@ module Amazon
|
|
27
28
|
#
|
28
29
|
class LocaleError < Amazon::AWS::Error::AWSError; end
|
29
30
|
|
30
|
-
|
31
|
+
# Requests can be authenticated using the SHA-256 Secure Hash
|
32
|
+
# Algorithm.
|
33
|
+
#
|
34
|
+
DIGEST = OpenSSL::Digest::Digest.new( 'sha256' )
|
35
|
+
|
36
|
+
attr_reader :conn, :config, :locale, :query, :user_agent
|
31
37
|
attr_writer :cache
|
38
|
+
attr_accessor :encoding
|
32
39
|
|
33
40
|
# This method is used to generate an AWS search request object.
|
34
41
|
#
|
@@ -82,7 +89,13 @@ module Amazon
|
|
82
89
|
else
|
83
90
|
nil
|
84
91
|
end
|
85
|
-
|
92
|
+
|
93
|
+
# Set the following two variables from the config file. Will be
|
94
|
+
# *nil* if not present in config file.
|
95
|
+
#
|
96
|
+
@api = @config['api']
|
97
|
+
@encoding = @config['encoding']
|
98
|
+
|
86
99
|
self.locale = locale
|
87
100
|
end
|
88
101
|
|
@@ -178,6 +191,43 @@ module Amazon
|
|
178
191
|
private :error_check
|
179
192
|
|
180
193
|
|
194
|
+
# Add a timestamp to a request object's query string.
|
195
|
+
#
|
196
|
+
def timestamp
|
197
|
+
@query << '&Timestamp=%s' %
|
198
|
+
[ Amazon.url_encode( Time.now.utc.strftime( '%FT%TZ' ) ) ]
|
199
|
+
end
|
200
|
+
private :timestamp
|
201
|
+
|
202
|
+
|
203
|
+
# Add a signature to a request object's query string. This implicitly
|
204
|
+
# also adds a timestamp.
|
205
|
+
#
|
206
|
+
def sign
|
207
|
+
timestamp
|
208
|
+
params = @query[1..-1].split( '&' ).sort.join( '&' )
|
209
|
+
|
210
|
+
sign_str = "GET\n%s\n%s\n%s" % [ ENDPOINT[@locale].host,
|
211
|
+
ENDPOINT[@locale].path,
|
212
|
+
params ]
|
213
|
+
|
214
|
+
Amazon.dprintf( 'Calculating SHA256 HMAC of "%s"...', sign_str )
|
215
|
+
|
216
|
+
hmac = OpenSSL::HMAC.digest( DIGEST,
|
217
|
+
@config['secret_key_id'],
|
218
|
+
sign_str )
|
219
|
+
Amazon.dprintf( 'SHA256 HMAC is "%s"', hmac.inspect )
|
220
|
+
|
221
|
+
base64_hmac = [ hmac ].pack( 'm' ).chomp
|
222
|
+
Amazon.dprintf( 'Base64-encoded HMAC is "%s".', base64_hmac )
|
223
|
+
|
224
|
+
signature = Amazon.url_encode( base64_hmac )
|
225
|
+
|
226
|
+
params << '&Signature=%s' % [ signature ]
|
227
|
+
@query = '?' + params
|
228
|
+
end
|
229
|
+
|
230
|
+
|
181
231
|
# Perform a search of the AWS database. _operation_ is one of the
|
182
232
|
# objects subclassed from _Operation_, such as _ItemSearch_,
|
183
233
|
# _ItemLookup_, etc. It may also be a _MultipleOperation_ object.
|
@@ -204,19 +254,19 @@ module Amazon
|
|
204
254
|
# whether a higher number of pages is requested.
|
205
255
|
#
|
206
256
|
def search(operation, response_group, nr_pages=1)
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
257
|
+
parameters = Amazon::AWS::SERVICE.
|
258
|
+
merge( { 'AWSAccessKeyId' => @key_id,
|
259
|
+
'AssociateTag' => @tag } ).
|
260
|
+
merge( operation.params ).
|
261
|
+
merge( response_group.params )
|
212
262
|
|
213
263
|
# Check to see whether a particular version of the API has been
|
214
264
|
# requested. If so, overwrite Version with the new value.
|
215
265
|
#
|
216
|
-
|
266
|
+
parameters.merge!( { 'Version' => @api } ) if @api
|
217
267
|
|
218
|
-
query = Amazon::AWS.assemble_query(
|
219
|
-
page = Amazon::AWS.get_page( self
|
268
|
+
@query = Amazon::AWS.assemble_query( parameters, @encoding )
|
269
|
+
page = Amazon::AWS.get_page( self )
|
220
270
|
doc = Document.new( page )
|
221
271
|
|
222
272
|
# Some errors occur at the very top level of the XML. For example,
|
@@ -315,9 +365,10 @@ module Amazon
|
|
315
365
|
# Iterate over pages 2 and higher, but go no higher than MAX_PAGES.
|
316
366
|
#
|
317
367
|
2.upto( nr_pages < max_pages ? nr_pages : max_pages ) do |page_nr|
|
318
|
-
query = Amazon::AWS.assemble_query(
|
319
|
-
|
320
|
-
|
368
|
+
@query = Amazon::AWS.assemble_query(
|
369
|
+
parameters.merge( { page_parameter => page_nr } ),
|
370
|
+
@encoding)
|
371
|
+
page = Amazon::AWS.get_page( self )
|
321
372
|
doc = Document.new( page )
|
322
373
|
|
323
374
|
# Check for errors.
|
@@ -327,7 +378,7 @@ module Amazon
|
|
327
378
|
|
328
379
|
# Create a new AWS object and walk the XML response tree.
|
329
380
|
#
|
330
|
-
aws = AWS::AWSObject.new
|
381
|
+
aws = AWS::AWSObject.new( operation )
|
331
382
|
aws.walk( doc )
|
332
383
|
|
333
384
|
# When dealing with multiple pages, we return not just an
|
data/test/setup.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: setup.rb,v 1.
|
1
|
+
# $Id: setup.rb,v 1.4 2009/05/25 23:35:54 ianmacd Exp $
|
2
2
|
#
|
3
3
|
|
4
4
|
# Attempt to load Ruby/AWS using RubyGems.
|
@@ -24,6 +24,7 @@ class AWSTest < Test::Unit::TestCase
|
|
24
24
|
@req = Request.new
|
25
25
|
@req.locale = 'uk'
|
26
26
|
@req.cache = false
|
27
|
+
@req.encoding = 'utf-8'
|
27
28
|
end
|
28
29
|
|
29
30
|
undef_method :default_test
|
data/test/tc_item_search.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: tc_item_search.rb,v 1.
|
1
|
+
# $Id: tc_item_search.rb,v 1.3 2009/05/26 16:18:14 ianmacd Exp $
|
2
2
|
#
|
3
3
|
|
4
4
|
require 'test/unit'
|
@@ -6,8 +6,13 @@ require './setup'
|
|
6
6
|
|
7
7
|
class TestItemSearch < AWSTest
|
8
8
|
|
9
|
-
def
|
10
|
-
|
9
|
+
def test_item_search_iso_8859_15
|
10
|
+
|
11
|
+
# Ensure that character set encoding works properly by manually trying
|
12
|
+
# ISO-8859-15, a.k.a. Latin-15.
|
13
|
+
#
|
14
|
+
@req.encoding = 'iso-8859-15'
|
15
|
+
is = ItemSearch.new( 'Books', { 'Title' => 'Caf�' } )
|
11
16
|
response = @req.search( is, @rg )
|
12
17
|
|
13
18
|
results = response.kernel
|
@@ -18,4 +23,36 @@ class TestItemSearch < AWSTest
|
|
18
23
|
|
19
24
|
end
|
20
25
|
|
26
|
+
def test_item_search_utf8
|
27
|
+
|
28
|
+
# Manually set UTF-8 encoding.
|
29
|
+
#
|
30
|
+
@req.encoding = 'utf-8'
|
31
|
+
is = ItemSearch.new( 'Books', { 'Title' => 'Café' } )
|
32
|
+
response = @req.search( is, @rg )
|
33
|
+
|
34
|
+
results = response.kernel
|
35
|
+
|
36
|
+
# Ensure we got some actual results back.
|
37
|
+
#
|
38
|
+
assert( results.size > 0 )
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_item_search_multiple_pages
|
43
|
+
|
44
|
+
is = ItemSearch.new( 'Books', { 'Title' => 'programming' } )
|
45
|
+
responses = @req.search( is, @rg, 5 )
|
46
|
+
|
47
|
+
results = []
|
48
|
+
responses.each do |response|
|
49
|
+
results += response.kernel
|
50
|
+
end
|
51
|
+
|
52
|
+
# Ensure we got more than 10 results back.
|
53
|
+
#
|
54
|
+
assert( results.size > 10 )
|
55
|
+
|
56
|
+
end
|
57
|
+
|
21
58
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-aaws
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ian Macdonald
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-26 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|