alandipert-ruby-aaws 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. data/COPYING +340 -0
  2. data/INSTALL +260 -0
  3. data/NEWS +710 -0
  4. data/README +653 -0
  5. data/README.rdoc +145 -0
  6. data/Rakefile +35 -0
  7. data/VERSION +1 -0
  8. data/example/batch_operation +27 -0
  9. data/example/browse_node_lookup1 +46 -0
  10. data/example/customer_content_lookup1 +27 -0
  11. data/example/customer_content_search1 +21 -0
  12. data/example/example1 +87 -0
  13. data/example/help1 +25 -0
  14. data/example/item_lookup1 +56 -0
  15. data/example/item_lookup2 +56 -0
  16. data/example/item_search1 +30 -0
  17. data/example/item_search2 +37 -0
  18. data/example/item_search3 +23 -0
  19. data/example/list_lookup1 +29 -0
  20. data/example/list_search1 +30 -0
  21. data/example/multiple_operation1 +68 -0
  22. data/example/seller_listing_lookup1 +30 -0
  23. data/example/seller_listing_search1 +28 -0
  24. data/example/seller_lookup1 +45 -0
  25. data/example/shopping_cart1 +42 -0
  26. data/example/similarity_lookup1 +48 -0
  27. data/example/tag_lookup1 +34 -0
  28. data/example/transaction_lookup1 +26 -0
  29. data/example/vehicle_search +22 -0
  30. data/lib/amazon/aws/cache.rb +141 -0
  31. data/lib/amazon/aws/search.rb +342 -0
  32. data/lib/amazon/aws/shoppingcart.rb +504 -0
  33. data/lib/amazon/aws.rb +1217 -0
  34. data/lib/amazon/locale.rb +102 -0
  35. data/lib/amazon.rb +145 -0
  36. data/ruby-aaws.gemspec +117 -0
  37. data/setup.rb +1306 -0
  38. data/test/setup.rb +34 -0
  39. data/test/tc_amazon.rb +20 -0
  40. data/test/tc_aws.rb +151 -0
  41. data/test/tc_browse_node_lookup.rb +62 -0
  42. data/test/tc_customer_content_lookup.rb +64 -0
  43. data/test/tc_help.rb +60 -0
  44. data/test/tc_item_lookup.rb +60 -0
  45. data/test/tc_item_search.rb +106 -0
  46. data/test/tc_list_lookup.rb +55 -0
  47. data/test/tc_list_search.rb +55 -0
  48. data/test/tc_multiple_operation.rb +265 -0
  49. data/test/tc_operation_request.rb +58 -0
  50. data/test/tc_seller_listing_lookup.rb +58 -0
  51. data/test/tc_seller_listing_search.rb +70 -0
  52. data/test/tc_seller_lookup.rb +54 -0
  53. data/test/tc_serialisation.rb +103 -0
  54. data/test/tc_shopping_cart.rb +214 -0
  55. data/test/tc_similarity_lookup.rb +59 -0
  56. data/test/tc_tag_lookup.rb +35 -0
  57. data/test/tc_transaction_lookup.rb +35 -0
  58. data/test/tc_vehicle_operations.rb +106 -0
  59. data/test/ts_aws.rb +24 -0
  60. metadata +135 -0
@@ -0,0 +1,342 @@
1
+ # $Id: search.rb,v 1.26 2008/09/21 22:17:32 ianmacd Exp $
2
+ #
3
+
4
+ module Amazon
5
+
6
+ module AWS
7
+
8
+ require 'amazon/aws'
9
+ require 'net/http'
10
+ require 'rexml/document'
11
+
12
+ # Load this library with:
13
+ #
14
+ # require 'amazon/aws/search'
15
+ #
16
+ module Search
17
+
18
+ class Request
19
+
20
+ include REXML
21
+
22
+ # Exception class for bad access key ID.
23
+ #
24
+ class AccessKeyIdError < Amazon::AWS::Error::AWSError; end
25
+
26
+ # Exception class for bad locales.
27
+ #
28
+ class LocaleError < Amazon::AWS::Error::AWSError; end
29
+
30
+ attr_reader :conn, :locale, :user_agent, :secret_id
31
+ attr_writer :cache
32
+
33
+ # This method is used to generate an AWS search request object.
34
+ #
35
+ # _key_id_ is your AWS {access key
36
+ # ID}[https://aws-portal.amazon.com/gp/aws/developer/registration/index.html],
37
+ # _secret_id is your AWS Secret (needed for signing the requests)
38
+ # _associate_ is your
39
+ # Associates[http://docs.amazonwebservices.com/AWSECommerceService/2008-04-07/GSG/BecominganAssociate.html]
40
+ # tag (if any), _locale_ is the locale in which you which to work
41
+ # (*us* for amazon.com[http://www.amazon.com/], *uk* for
42
+ # amazon.co.uk[http://www.amazon.co.uk], etc.), _cache_ is whether or
43
+ # not you wish to utilise a response cache, and _user_agent_ is the
44
+ # client name to pass when performing calls to AWS. By default,
45
+ # _user_agent_ will be set to a string identifying the Ruby/AWS
46
+ # library and its version number.
47
+ #
48
+ # _locale_ and _cache_ can also be set later, if you wish to change
49
+ # the current behaviour.
50
+ #
51
+ # Example:
52
+ #
53
+ # req = Request.new( '0Y44V8FAFNM119CX4TR2', 'yoursecret_id', 'calibanorg-20' )
54
+ #
55
+ def initialize(key_id=nil, secret_id=nil, associate=nil, locale=nil, cache=nil, cache_dir=nil, user_agent=USER_AGENT)
56
+
57
+ @config ||= Amazon::Config.new
58
+
59
+ def_locale = locale
60
+ locale = 'us' unless locale
61
+ locale.downcase!
62
+
63
+ key_id ||= @config['key_id']
64
+ secret_id ||= @config['secret_id']
65
+ cache = @config['cache'] if cache.nil?
66
+ cache_dir ||= @config['cache_dir']
67
+
68
+ # Take locale from config file if no locale was passed to method.
69
+ #
70
+ if @config.key?( 'locale' ) && ! def_locale
71
+ locale = @config['locale']
72
+ end
73
+ validate_locale( locale )
74
+
75
+ if key_id.nil?
76
+ raise AccessKeyIdError, 'key_id may not be nil'
77
+ end
78
+
79
+ if secret_id.nil?
80
+ raise AccessKeyIdError, 'secrret_id may not be nil'
81
+ end
82
+
83
+ @key_id = key_id
84
+ @secret_id = secret_id
85
+ @tag = associate || @config['associate'] || DEF_ASSOC[locale]
86
+ @user_agent = user_agent
87
+ @cache = unless cache == 'false' || cache == false
88
+ Amazon::AWS::Cache.new( cache_dir )
89
+ else
90
+ nil
91
+ end
92
+ self.locale = locale
93
+ end
94
+
95
+
96
+ # Assign a new locale. If the locale we're coming from is using the
97
+ # default Associate ID for that locale, then we use the new locale's
98
+ # default ID, too.
99
+ #
100
+ def locale=(l) # :nodoc:
101
+ old_locale = @locale ||= nil
102
+ @locale = validate_locale( l )
103
+
104
+ # Use the new locale's default ID if the ID currently in use is the
105
+ # current locale's default ID.
106
+ #
107
+ if @tag == Amazon::AWS::DEF_ASSOC[old_locale]
108
+ @tag = Amazon::AWS::DEF_ASSOC[@locale]
109
+ end
110
+
111
+ # We must now set up a new HTTP connection to the correct server for
112
+ # this locale, unless the same server is used for both.
113
+ #
114
+ unless Amazon::AWS::ENDPOINT[@locale] ==
115
+ Amazon::AWS::ENDPOINT[old_locale]
116
+ #connect( @locale )
117
+ @conn = nil
118
+ end
119
+ end
120
+
121
+
122
+ # If @cache has simply been assigned *true* at some point in time,
123
+ # assign a proper cache object to it when it is referenced. Otherwise,
124
+ # just return its value.
125
+ #
126
+ def cache # :nodoc:
127
+ if @cache == true
128
+ @cache = Amazon::AWS::Cache.new( @config['cache_dir'] )
129
+ else
130
+ @cache
131
+ end
132
+ end
133
+
134
+
135
+ # Verify the validity of a locale string. _l_ is the locale string.
136
+ #
137
+ def validate_locale(l)
138
+ unless Amazon::AWS::ENDPOINT.has_key? l
139
+ raise LocaleError, "invalid locale: #{l}"
140
+ end
141
+ l
142
+ end
143
+ private :validate_locale
144
+
145
+
146
+ # Return an HTTP connection for the current _locale_.
147
+ #
148
+ def connect(locale)
149
+ if ENV.key? 'http_proxy'
150
+ uri = URI.parse( ENV['http_proxy'] )
151
+ proxy_user = proxy_pass = nil
152
+ proxy_user, proxy_pass = uri.userinfo.split( /:/ ) if uri.userinfo
153
+ @conn = Net::HTTP::Proxy( uri.host, uri.port, proxy_user,
154
+ proxy_pass ).start(
155
+ Amazon::AWS::ENDPOINT[locale].host )
156
+ else
157
+ @conn = Net::HTTP::start( Amazon::AWS::ENDPOINT[locale].host )
158
+ end
159
+ end
160
+ private :connect
161
+
162
+
163
+ # Reconnect to the server if our connection has been lost (due to a
164
+ # time-out, etc.).
165
+ #
166
+ def reconnect # :nodoc:
167
+ connect( self.locale )
168
+ self
169
+ end
170
+
171
+
172
+ # This method checks for errors in an XML response returned by AWS.
173
+ # _xml_ is the XML node below which to search.
174
+ #
175
+ def error_check(xml)
176
+ if xml = xml.elements['Errors/Error']
177
+ raise Amazon::AWS::Error.exception( xml )
178
+ end
179
+ end
180
+ private :error_check
181
+
182
+
183
+ # Perform a search of the AWS database. _operation_ is one of the
184
+ # objects subclassed from _Operation_, such as _ItemSearch_,
185
+ # _ItemLookup_, etc. It may also be a _MultipleOperation_ object.
186
+ #
187
+ # _response_group_ will apply to all both operations contained in
188
+ # _operation_, if _operation_ is a _MultipleOperation_ object.
189
+ #
190
+ # _nr_pages_ is the number of results pages to return. It defaults to
191
+ # <b>1</b>. If a higher number is given, pages 1 to _nr_pages_ will be
192
+ # returned. If the special value <b>:ALL_PAGES</b> is given, all
193
+ # results pages will be returned.
194
+ #
195
+ # The maximum page number that can be returned for each type of
196
+ # operation is documented in the AWS Developer's Guide:
197
+ #
198
+ # http://docs.amazonwebservices.com/AWSECommerceService/2008-08-19/DG/index.html?CHAP_MakingRequestsandUnderstandingResponses.html#PagingThroughResults
199
+ #
200
+ # Note that _ItemLookup_ operations can use three separate pagination
201
+ # parameters. Ruby/AWS, however, uses _OfferPage_ for the purposes of
202
+ # returning multiple pages.
203
+ #
204
+ # If operation is of class _MultipleOperation_, the operations
205
+ # combined within will return only the first page, regardless of
206
+ # whether a higher number of pages is requested.
207
+ #
208
+ def search(operation, response_group, nr_pages=1)
209
+ q_params = Amazon::AWS::SERVICE.
210
+ merge( { 'AWSAccessKeyId' => @key_id,
211
+ 'AssociateTag' => @tag } ).
212
+ merge( operation.params ).
213
+ merge( response_group.params )
214
+
215
+ query = Amazon::AWS.assemble_query( q_params )
216
+
217
+
218
+ page = Amazon::AWS.get_page( self, query )
219
+ doc = Document.new( page )
220
+
221
+ # Some errors occur at the very top level of the XML. For example,
222
+ # when no Operation parameter is given. This should not be possible
223
+ # with user code, but occurred during debugging of this library.
224
+ #
225
+ error_check( doc )
226
+
227
+ # Fundamental errors happen at the OperationRequest level. For
228
+ # example, if an invalid AWSAccessKeyId is used.
229
+ #
230
+ error_check( doc.elements['*/OperationRequest'] )
231
+
232
+ # Check for parameter and value errors deeper down, inside Request.
233
+ #
234
+ if operation.kind == 'MultipleOperation'
235
+
236
+ # Everything is a level deeper, because of the
237
+ # <MultiOperationResponse> container.
238
+ #
239
+ # Check for errors in the first operation.
240
+ #
241
+ error_check( doc.elements['*/*/*/Request'] )
242
+
243
+ # Check for errors in the second operation.
244
+ #
245
+ error_check( doc.elements['*/*[3]/*/Request'] )
246
+
247
+ # If second operation is batched, check for errors in its 2nd set
248
+ # of results.
249
+ #
250
+ if batched = doc.elements['*/*[3]/*[2]/Request']
251
+ error_check( batched )
252
+ end
253
+ else
254
+ error_check( doc.elements['*/*/Request'] )
255
+
256
+ # If operation is batched, check for errors in its 2nd set of
257
+ # results.
258
+ #
259
+ if batched = doc.elements['*/*[3]/Request']
260
+ error_check( batched )
261
+ end
262
+ end
263
+
264
+ # FIXME: This doesn't work if a MultipleOperation was used, because
265
+ # <TotalPages> will be nested one level deeper. It's therefore
266
+ # currently only possible to return the first page of results
267
+ # for operations combined in a MultipleOperation.
268
+ #
269
+ if doc.elements['*/*[2]/TotalPages']
270
+ total_pages = doc.elements['*/*[2]/TotalPages'].text.to_i
271
+ else
272
+ total_pages = 1
273
+ end
274
+
275
+ # Create a root AWS object and walk the XML response tree.
276
+ #
277
+ aws = AWS::AWSObject.new( operation )
278
+ aws.walk( doc )
279
+ result = aws
280
+
281
+ # If only one page has been requested or only one page is available,
282
+ # we can stop here. First yield to the block, if given.
283
+ #
284
+ if nr_pages == 1 || ( tp = total_pages ) == 1
285
+ yield result if block_given?
286
+ return result
287
+ end
288
+
289
+ # Limit the number of pages to the maximum number available.
290
+ #
291
+ nr_pages = tp.to_i if nr_pages == :ALL_PAGES || nr_pages > tp.to_i
292
+
293
+ if PAGINATION.key? operation.kind
294
+ page_parameter = PAGINATION[operation.kind]['parameter']
295
+ max_pages = PAGINATION[operation.kind]['max_page']
296
+ else
297
+ page_parameter = 'ItemPage'
298
+ max_pages = 400
299
+ end
300
+
301
+ # Iterate over pages 2 and higher, but go no higher than MAX_PAGES.
302
+ #
303
+ 2.upto( nr_pages < max_pages ? nr_pages : max_pages ) do |page_nr|
304
+ query = Amazon::AWS.assemble_query(
305
+ q_params.merge( { page_parameter => page_nr } ) )
306
+ page = Amazon::AWS.get_page( self, query )
307
+ doc = Document.new( page )
308
+
309
+ # Check for errors.
310
+ #
311
+ error_check( doc.elements['*/OperationRequest'] )
312
+ error_check( doc.elements['*/*/Request'] )
313
+
314
+ # Create a new AWS object and walk the XML response tree.
315
+ #
316
+ aws = AWS::AWSObject.new
317
+ aws.walk( doc )
318
+
319
+ # When dealing with multiple pages, we return not just an
320
+ # AWSObject, but an array of them.
321
+ #
322
+ result = [ result ] unless result.is_a? Array
323
+
324
+ # Append the new object to the array.
325
+ #
326
+ result << aws
327
+ end
328
+
329
+ # Yield each object to the block, if given.
330
+ #
331
+ result.each { |r| yield r } if block_given?
332
+
333
+ result
334
+ end
335
+
336
+ end
337
+
338
+ end
339
+
340
+ end
341
+
342
+ end