ruby-aaws 0.5.1 → 0.6.0
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.
- 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
|
|