ruby-aaws 0.4.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.
@@ -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