ruby-aaws 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,504 @@
1
+ # $Id: shoppingcart.rb,v 1.17 2008/07/13 01:56:43 ianmacd Exp $
2
+ #
3
+
4
+ require 'amazon/aws/search'
5
+
6
+ module Amazon
7
+
8
+ module AWS
9
+
10
+ # Load this library with:
11
+ #
12
+ # require 'amazon/aws/shoppingcart'
13
+ #
14
+ module ShoppingCart
15
+
16
+ # Attempts to remove non-existent items from a shopping-cart will raise
17
+ # this exception.
18
+ #
19
+ class CartError < StandardError; end
20
+
21
+ class Cart < Amazon::AWS::Search::Request
22
+
23
+ # _cart_id_ is an alphanumeric token that uniquely identifies a
24
+ # remote shopping-cart. _hmac_ is a <b>H</b>ash <b>M</b>essage
25
+ # <b>A</b>uthentication <b>C</b>ode. This is an encrypted alphanumeric
26
+ # token used to authenticate requests. _purchase_url_ is the URL to
27
+ # follow in order to complete the purchase of the items in the
28
+ # shopping-cart. _cart_items_ is an Array of items in the active area
29
+ # of the cart and _saved_for_later_items_ is an Array of items in the
30
+ # <i>Save For Later</i> area of the cart.
31
+ #
32
+ attr_reader :cart_id, :hmac, :purchase_url, :cart_items,
33
+ :saved_for_later_items
34
+ alias :items :cart_items
35
+ alias :saved_items :saved_for_later_items
36
+ alias :saved :saved_for_later_items
37
+
38
+
39
+ # Create a new instance of a remote shopping-cart. See
40
+ # Amazon::AWS::Search::Request.new for details of the parameters.
41
+ #
42
+ # Example:
43
+ #
44
+ # cart = Cart.new
45
+ #
46
+ def initialize(key_id=nil, associate=nil, locale=nil,
47
+ user_agent=USER_AGENT)
48
+
49
+ @cart_items = []
50
+ @saved_for_later_items = []
51
+
52
+ # Note the *false* as the fourth parameter to _super_, because we
53
+ # never want to cache shopping-cart transactions.
54
+ #
55
+ super( key_id, associate, locale, false, user_agent )
56
+ end
57
+
58
+
59
+ # Prepare the remote shopping-cart for use and place one or more items
60
+ # in it.
61
+ #
62
+ # _id_type_ is a String, either *ASIN* or *OfferListingId*. _item_id_
63
+ # is the actual ASIN or offer listing ID in question, _quantity_ is
64
+ # the quantity of the item to add to the cart, and _merge_cart_ is
65
+ # whether or not the remote shopping-cart should be merged with the
66
+ # local cart on the Amazon retail site upon check-out.
67
+ #
68
+ # _more_items_ is an optional list of Hash objects describing
69
+ # additional items to place in the cart.
70
+ #
71
+ # Example:
72
+ #
73
+ # cart.cart_create( :ASIN, 'B00151HZA6', 1,
74
+ # { 'B000WC4AH0' => 2 },
75
+ # { 'B000PY32OM' => 3 } )
76
+ #
77
+ # or:
78
+ #
79
+ # cart.cart_create( :ASIN, 'B00151HZA6', 1,
80
+ # { 'B000WC4AH0' => 2,
81
+ # 'B000PY32OM' => 3 } )
82
+ #
83
+ # Please note that it's not yet possible to update a wishlist at
84
+ # purchase time by referring to the item's *ListItemId* when adding
85
+ # that item to the cart.
86
+ #
87
+ def cart_create(id_type, item_id, quantity=1, merge_cart=false,
88
+ *more_items)
89
+ cc = CartCreate.new( id_type, item_id, quantity, merge_cart, nil,
90
+ *more_items )
91
+
92
+ @rg = ResponseGroup.new( 'Cart' )
93
+ cart = search( cc, @rg ).cart_create_response.cart
94
+
95
+ @cart_id = cart.cart_id
96
+ @hmac = cart.hmac
97
+ @purchase_url = cart.purchase_url
98
+ @cart_items = cart.cart_items.cart_item
99
+ end
100
+
101
+ alias :create :cart_create
102
+
103
+
104
+ # Add one or more new items to the remote shopping-cart. This can not
105
+ # be used to update quantities of items *already* in the cart. For
106
+ # that, you must use Cart#cart_modify instead.
107
+ #
108
+ # _id_type_ is a String, either *ASIN* or *OfferListingId*. _item_id_
109
+ # is the actual ASIN or offer listing ID in question, and _quantity_
110
+ # is the quantity of the item to add to the cart.
111
+ #
112
+ # _more_items_ is an optional list of Hash objects describing
113
+ # additional items to add to the cart.
114
+ #
115
+ # Example:
116
+ #
117
+ # cart.cart_add( :ASIN, 'B0014C2BL4', 3,
118
+ # { 'B00006BCKL' => 2 },
119
+ # { 'B000VVE2UW' => 1 } )
120
+ #
121
+ # or:
122
+ #
123
+ # cart.cart_add( :ASIN, 'B0014C2BL4', 3,
124
+ # { 'B00006BCKL' => 2,
125
+ # 'B000VVE2UW' => 1 } )
126
+ #
127
+ def cart_add(id_type, item_id, quantity=1, *more_items)
128
+ ca = CartAdd.new( id_type, item_id, quantity, *more_items )
129
+ ca.params.merge!( { 'CartId' => @cart_id, 'HMAC' => @hmac } )
130
+ cart = search( ca, @rg ).cart_add_response.cart
131
+ @cart_items = cart.cart_items.cart_item
132
+ end
133
+
134
+ alias :add :cart_add
135
+
136
+
137
+ # Returns whether or not an item is present in the cart, be it in the
138
+ # active or <i>Save For Later</i> area.
139
+ #
140
+ # _item_id_type_ is the name of the attribute that uniquely identifies
141
+ # an item, such as *ASIN* or *CartItemId*. _item_id_ is the value of
142
+ # the _item_id_type_ for the item whose presence in the cart is being
143
+ # determined.
144
+ #
145
+ # If the item is present in the cart, its _CartItemId_ is returned as a
146
+ # String. Otherwise, *false* is returned.
147
+ #
148
+ # Example:
149
+ #
150
+ # cart.include?( :ASIN, 'B00151HZA6' )
151
+ #
152
+ def include?(item_id_type, item_id)
153
+ active?( item_id_type, item_id ) ||
154
+ saved_for_later?( item_id_type, item_id )
155
+ end
156
+
157
+ alias :contain? :include?
158
+
159
+
160
+ # Returns whether or not an item is present in an area of the cart.
161
+ #
162
+ # _area_ is an array of cart items, _item_id_type_ is the name of the
163
+ # attribute that uniquely identifies an item, such as *ASIN* or
164
+ # *CartItemId* and _item_id_ is the value of the _item_id_type_ for
165
+ # the item whose presence in the cart is being determined.
166
+ #
167
+ # If the item is present in the cart, its _CartItemId_ is returned as a
168
+ # String. Otherwise, *false* is returned.
169
+ #
170
+ # Example:
171
+ #
172
+ # cart.in_area?( @cart_items, :ASIN, 'B00151HZA6' )
173
+ #
174
+ # or:
175
+ #
176
+ # cart.in_area?( @saved_for_later_items, :ASIN, 'B00151HZA6' )
177
+ #
178
+ def in_area?(area, item_id_type, item_id)
179
+ found = area.find do |item|
180
+ item.send( Amazon.uncamelise( item_id_type.to_s ) ).to_s == item_id
181
+ end
182
+
183
+ found ? found.cart_item_id.to_s : false
184
+ end
185
+ private :in_area?
186
+
187
+
188
+ # Returns whether or not an item is present in the active area of the
189
+ # cart.
190
+ #
191
+ # _item_id_type_ is the name of the attribute that uniquely identifies
192
+ # an item, such as *ASIN* or *CartItemId*. _item_id_ is the value of
193
+ # the _item_id_type_ for the item whose presence in the cart is being
194
+ # determined.
195
+ #
196
+ # If the item is present in the cart, its _CartItemId_ is returned as a
197
+ # String. Otherwise, *false* is returned.
198
+ #
199
+ # Example:
200
+ #
201
+ # cart.active?( :ASIN, 'B00151HZA6' )
202
+ #
203
+ def active?(item_id_type, item_id)
204
+ in_area?( @cart_items, item_id_type, item_id )
205
+ end
206
+
207
+
208
+ # Returns whether or not an item is present in the <i>Save For
209
+ # Later</i> area of the cart.
210
+ #
211
+ # _item_id_type_ is the name of the attribute that uniquely identifies
212
+ # an item, such as *ASIN* or *CartItemId*. _item_id_ is the value of
213
+ # the _item_id_type_ for the item whose presence in the cart is being
214
+ # determined.
215
+ #
216
+ # If the item is present in the cart, its _CartItemId_ is returned as a
217
+ # String. Otherwise, *false* is returned.
218
+ #
219
+ # Example:
220
+ #
221
+ # cart.saved_for_later?( :ASIN, 'B00151HZA6' )
222
+ #
223
+ def saved_for_later?(item_id_type, item_id)
224
+ in_area?( @saved_for_later_items, item_id_type, item_id )
225
+ end
226
+
227
+ alias :saved? :saved_for_later?
228
+
229
+
230
+ # Modify the quantities of one or more products already in the cart.
231
+ # Changing the quantity of an item to <b>0</b> effectively removes it
232
+ # from the cart.
233
+ #
234
+ # _item_id_type_ is the name of the attribute that uniquely identifies
235
+ # an item in the cart, such as *ASIN* or *CartItemId*. _item_id_ is
236
+ # the value of the _item_id_type_ of the item to be modified, and
237
+ # _quantity_ is its new quantity.
238
+ #
239
+ # _save_for_later_ should be set to *true* if the items in question
240
+ # should be moved to the <i>Save For Later</i> area of the
241
+ # shopping-cart, or *false* if they should be moved to the active
242
+ # area. _save_for_later_ therefore applies to every item specified by
243
+ # _item_id_ and _more_items_. Use *nil* when the location of the items
244
+ # should not be changed.
245
+ #
246
+ # Current Amazon AWS documentation claims that specifying partial
247
+ # quantities can be used to move some copies of an item from one area
248
+ # of the cart to another, whilst leaving the rest in place. In
249
+ # practice, however, this causes an AWS error that explains that a
250
+ # quantity may not be specified in combination with an instruction to
251
+ # move copies from one area of the cart to another. For this reason,
252
+ # when _save_for_later_ is not *nil*, item quantities are currently
253
+ # ignored.
254
+ #
255
+ # _more_items_ is an optional list of Hash objects describing
256
+ # additional items whose quantity should be modified.
257
+ #
258
+ # Example:
259
+ #
260
+ # cart.cart_modify( :ASIN, 'B00151HZA6', 2, false,
261
+ # { 'B0013F2M52' => 1 },
262
+ # { 'B000HCPSR6' => 3 } )
263
+ #
264
+ # or:
265
+ #
266
+ # cart.cart_modify( :ASIN, 'B00151HZA6', 2, true,
267
+ # { 'B0013F2M52' => 1,
268
+ # 'B000HCPSR6' => 3 } )
269
+ #
270
+ def cart_modify(item_id_type, item_id, quantity, save_for_later=nil,
271
+ *more_items)
272
+ item_quantity1 = quantity
273
+
274
+ unless cart_item_id1 = self.include?( item_id_type, item_id )
275
+ raise CartError,
276
+ "Can't find item with '#{item_id_type}' of '#{item_id}' in cart."
277
+ end
278
+
279
+ more_items.collect! do |extra_item|
280
+ items = []
281
+
282
+ extra_item.each do |item|
283
+ item_id, quantity = item
284
+ unless cart_item_id = self.include?( item_id_type, item_id )
285
+ raise CartError,
286
+ "Can't find item with '#{item_id_type}' of '#{item_id}' in cart."
287
+ end
288
+
289
+ items << { cart_item_id => quantity }
290
+ end
291
+
292
+ items
293
+ end
294
+
295
+ more_items.flatten!
296
+
297
+ cm = CartModify.new( cart_item_id1, item_quantity1, save_for_later,
298
+ *more_items )
299
+ cm.params.merge!( { 'CartId' => @cart_id, 'HMAC' => @hmac } )
300
+ cart = search( cm, @rg ).cart_modify_response.cart
301
+
302
+ if ci = cart.cart_items
303
+ @cart_items = ci.cart_item
304
+ else
305
+ @cart_items = []
306
+ end
307
+
308
+ if sfl = cart.saved_for_later_items
309
+ @saved_for_later_items = sfl.saved_for_later_item
310
+ else
311
+ @saved_for_later_items = []
312
+ end
313
+ end
314
+
315
+ alias :modify :cart_modify
316
+
317
+
318
+ # Retrieve a remote shopping-cart. This is especially useful when
319
+ # needing to resurrect a cart at a later time, when the Cart object
320
+ # containing the original data no longer exists.
321
+ #
322
+ # _cart_id_ is the unique ID of the cart to be retrieved and _hmac_ is
323
+ # the cart's hash message authentication code. These details can
324
+ # be obtained from an existing cart using the <i>@cart_id</i> and
325
+ # <i>@hmac</i> instance variables.
326
+ #
327
+ # Example:
328
+ #
329
+ # old_cart = Cart.new
330
+ # old_cart.get_cart( '203-4219703-7532717',
331
+ # 'o98sn9Z16JOEF/9eo6OcD8zOZA4=' )
332
+ #
333
+ def cart_get(cart_id, hmac)
334
+ cg = CartGet.new
335
+ cg.params.merge!( { 'CartId' => cart_id, 'HMAC' => hmac } )
336
+
337
+ @rg = ResponseGroup.new( 'Cart' )
338
+ cart = search( cg, @rg ).cart_get_response.cart
339
+
340
+ @cart_id = cart.cart_id
341
+ @hmac = cart.hmac
342
+ @purchase_url = cart.purchase_url
343
+
344
+ if ci = cart.cart_items
345
+ @cart_items = ci.cart_item
346
+ else
347
+ @cart_items = []
348
+ end
349
+
350
+ if sfl = cart.saved_for_later_items
351
+ @saved_for_later_items = sfl.saved_for_later_item
352
+ else
353
+ @saved_for_later_items = []
354
+ end
355
+
356
+ self
357
+ end
358
+
359
+ alias :get :cart_get
360
+
361
+
362
+ # Remove all items from the shopping-cart.
363
+ #
364
+ # Example:
365
+ #
366
+ # cart.cart_clear
367
+ #
368
+ def cart_clear
369
+ cc = CartClear.new
370
+ cc.params.merge!( { 'CartId' => @cart_id, 'HMAC' => @hmac } )
371
+ cart = search( cc, @rg ).cart_clear_response.cart
372
+ @cart_items = []
373
+ @saved_for_later_items = []
374
+ end
375
+
376
+ alias :clear :cart_clear
377
+
378
+
379
+ include Enumerable
380
+
381
+ # Iterator for each item in the cart.
382
+ #
383
+ def each
384
+ @cart_items.each { |item| yield item }
385
+ end
386
+
387
+ alias :each_item :each
388
+
389
+ end
390
+
391
+
392
+ # Worker class used by Cart#cart_create.
393
+ #
394
+ class CartCreate < Operation # :nodoc:
395
+
396
+ # Create a shopping-cart and add item(s) to it.
397
+ #
398
+ def initialize(id_type, item_id, quantity, merge_cart=false,
399
+ save_for_later=nil, *more_items)
400
+
401
+ # FIXME: Need to deal with ListItemId, too.
402
+
403
+ # Prepend first item to more_items array (which may be empty).
404
+ #
405
+ more_items.unshift( { item_id => quantity } )
406
+
407
+ mc = merge_cart ? 'True' : 'False'
408
+
409
+ more_items.collect! do |extra_item|
410
+ items = []
411
+
412
+ extra_item.each do |item|
413
+ item_id, quantity = item
414
+ case save_for_later
415
+ when true
416
+ items << { id_type => item_id,
417
+ 'Action' => 'SaveForLater' }
418
+ when false
419
+ items << { id_type => item_id,
420
+ 'Action' => 'MoveToCart' }
421
+ when nil
422
+ items << { id_type => item_id,
423
+ 'Quantity' => quantity }
424
+ else
425
+ raise CartError,
426
+ "save_for_later must be true, false or nil, but was #{save_for_later}"
427
+ end
428
+ end
429
+
430
+ items
431
+ end
432
+
433
+ more_items.flatten!
434
+
435
+ # Force batch syntax if only a single item is being put in cart.
436
+ #
437
+ params = batch_parameters( {}, *more_items )
438
+ params.merge!( { 'MergeCart' => mc } ) if merge_cart
439
+
440
+ super( params )
441
+ end
442
+
443
+ end
444
+
445
+
446
+ # Worker class used by Cart#cart_add.
447
+ #
448
+ class CartAdd < CartCreate # :nodoc:
449
+
450
+ # Add new item(s) to a cart.
451
+ #
452
+ def initialize(id_type, item_id, quantity, *more_items)
453
+ super( id_type, item_id, quantity, false, nil, *more_items )
454
+ end
455
+
456
+ end
457
+
458
+
459
+ # Worker class used by Cart#cart_modify.
460
+ #
461
+ class CartModify < CartCreate # :nodoc:
462
+
463
+ # Modify the quantity of item(s) in a cart.
464
+ #
465
+ def initialize(cart_item_id, quantity, save_for_later=false,
466
+ *more_items)
467
+
468
+ super( 'CartItemId', cart_item_id, quantity, false, save_for_later,
469
+ *more_items )
470
+ end
471
+
472
+ end
473
+
474
+
475
+ # Worker class used by Cart#cart_clear.
476
+ #
477
+ class CartClear < Operation # :nodoc:
478
+
479
+ # Remove all items from a cart.
480
+ #
481
+ def initialize
482
+ super( {} )
483
+ end
484
+
485
+ end
486
+
487
+
488
+ # Worker class used by Cart#cart_get.
489
+ #
490
+ class CartGet < Operation # :nodoc:
491
+
492
+ # Fetch a cart.
493
+ #
494
+ def initialize
495
+ super( {} )
496
+ end
497
+
498
+ end
499
+
500
+ end
501
+
502
+ end
503
+
504
+ end