amazon-associates 0.6.3

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 (50) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG +29 -0
  3. data/LICENSE +21 -0
  4. data/README.rdoc +84 -0
  5. data/Rakefile +62 -0
  6. data/VERSION.yml +5 -0
  7. data/amazon-associates.gemspec +118 -0
  8. data/lib/amazon-associates.rb +90 -0
  9. data/lib/amazon-associates/caching/filesystem_cache.rb +121 -0
  10. data/lib/amazon-associates/errors.rb +23 -0
  11. data/lib/amazon-associates/extensions/core.rb +31 -0
  12. data/lib/amazon-associates/extensions/hpricot.rb +115 -0
  13. data/lib/amazon-associates/request.rb +143 -0
  14. data/lib/amazon-associates/requests/browse_node.rb +10 -0
  15. data/lib/amazon-associates/requests/cart.rb +81 -0
  16. data/lib/amazon-associates/requests/item.rb +13 -0
  17. data/lib/amazon-associates/responses/browse_node_lookup_response.rb +10 -0
  18. data/lib/amazon-associates/responses/cart_responses.rb +26 -0
  19. data/lib/amazon-associates/responses/item_lookup_response.rb +16 -0
  20. data/lib/amazon-associates/responses/item_search_response.rb +20 -0
  21. data/lib/amazon-associates/responses/response.rb +27 -0
  22. data/lib/amazon-associates/responses/similarity_lookup_response.rb +9 -0
  23. data/lib/amazon-associates/types/api_result.rb +8 -0
  24. data/lib/amazon-associates/types/browse_node.rb +48 -0
  25. data/lib/amazon-associates/types/cart.rb +87 -0
  26. data/lib/amazon-associates/types/customer_review.rb +15 -0
  27. data/lib/amazon-associates/types/editorial_review.rb +8 -0
  28. data/lib/amazon-associates/types/error.rb +8 -0
  29. data/lib/amazon-associates/types/image.rb +37 -0
  30. data/lib/amazon-associates/types/image_set.rb +11 -0
  31. data/lib/amazon-associates/types/item.rb +156 -0
  32. data/lib/amazon-associates/types/listmania_list.rb +9 -0
  33. data/lib/amazon-associates/types/measurement.rb +47 -0
  34. data/lib/amazon-associates/types/offer.rb +10 -0
  35. data/lib/amazon-associates/types/ordinal.rb +24 -0
  36. data/lib/amazon-associates/types/price.rb +29 -0
  37. data/lib/amazon-associates/types/requests.rb +50 -0
  38. data/spec/requests/browse_node_lookup_spec.rb +41 -0
  39. data/spec/requests/item_search_spec.rb +27 -0
  40. data/spec/spec_helper.rb +8 -0
  41. data/spec/types/cart_spec.rb +294 -0
  42. data/spec/types/item_spec.rb +55 -0
  43. data/spec/types/measurement_spec.rb +43 -0
  44. data/test/amazon/browse_node_test.rb +34 -0
  45. data/test/amazon/cache_test.rb +33 -0
  46. data/test/amazon/caching/filesystem_cache_test.rb +198 -0
  47. data/test/amazon/item_test.rb +397 -0
  48. data/test/test_helper.rb +9 -0
  49. data/test/utilities/filesystem_test_helper.rb +35 -0
  50. metadata +216 -0
@@ -0,0 +1,9 @@
1
+ module Amazon
2
+ module Associates
3
+ class ListmaniaList < ApiResult
4
+ xml_name 'ListmaniaList'
5
+ xml_reader :id, :from => 'ListId'
6
+ xml_reader :name, :from => 'ListName'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,47 @@
1
+ module Amazon
2
+ module Associates
3
+ class Measurement < ApiResult
4
+ include Comparable
5
+
6
+ xml_reader :value, :from => :content, :as => Float
7
+ xml_reader :units, :from => :attr
8
+
9
+ def initialize(value = nil, units = 'pixels')
10
+ @value = value && Float(value)
11
+ @units = units.to_s
12
+ normalize_hundredths
13
+ end
14
+
15
+ def to_s
16
+ value = @value.whole? ? @value.to_i : @value
17
+ #singularize here to avoid comparison problems
18
+ units = @value == 1 ? @units.singularize : @units
19
+ [value, units].join(' ')
20
+ end
21
+ alias_attribute :inspect, :to_s
22
+
23
+ def to_i
24
+ @value.round
25
+ end
26
+
27
+ def <=>(other)
28
+ return nil unless @units == other.units
29
+
30
+ @value <=> other.value
31
+ end
32
+
33
+ private
34
+ def after_parse
35
+ @units ||= 'pixels'
36
+ normalize_hundredths
37
+ end
38
+
39
+ def normalize_hundredths
40
+ if @units.try(:starts_with?, 'hundredths-')
41
+ @value /= 100.0
42
+ @units = @units.split('hundredths-')[1]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,10 @@
1
+ module Amazon
2
+ module Associates
3
+ class Offer < ApiResult
4
+ xml_reader :listing_id, :from => 'OfferListingId'
5
+ xml_reader :price, :as => Price
6
+ xml_reader :availability
7
+ xml_reader :is_eligible_for_super_saver_shipping?, :in => 'xmlns:OfferListing'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ module Amazon
2
+ module Associates
3
+ class Ordinal < ApiResult
4
+ include Comparable
5
+
6
+ xml_reader :value, :from => :content do |val|
7
+ val.to_i
8
+ end
9
+
10
+ def initialize(value = nil)
11
+ @value = value && value.to_i
12
+ end
13
+
14
+ def to_s
15
+ @value.ordinalize
16
+ end
17
+ alias_attribute :inspect, :to_s
18
+
19
+ def ==(other)
20
+ @value == other
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ module Amazon
2
+ module Associates
3
+ class Price < ApiResult
4
+ include Comparable
5
+ attr_reader :cents, :currency
6
+
7
+ xml_reader :to_s, :from => 'FormattedPrice'
8
+ xml_reader :currency, :from => 'CurrencyCode'
9
+ xml_reader :cents, :from => 'Amount', :as => Integer
10
+
11
+ def initialize(str = nil, cents = nil, currency = nil)
12
+ @to_s = str.to_s
13
+ @cents = Integer(cents)
14
+ @currency = currency.to_s
15
+ end
16
+
17
+ def <=>(other)
18
+ return nil if @currency.nil? or @cents.nil?
19
+ return nil if @currency != other.currency
20
+
21
+ @cents <=> other.cents
22
+ end
23
+
24
+ def inspect
25
+ "#{to_s} #{currency}"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ module Amazon
2
+ module Associates
3
+ class Request < ApiResult
4
+ xml_reader :valid?, :from => 'IsValid', :required => true
5
+ xml_reader :errors, :as => [Error] do |errors|
6
+ errors.collect do |error|
7
+ if error.code && !IGNORE_ERRORS.include?(error.code)
8
+ if exception = ERROR[error.code]
9
+ exception.new("#{error.message} (#{@url})")
10
+ else
11
+ RuntimeError.new("#{error.code}: #{error.message} (#{@url})")
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ def ==(other)
18
+ (instance_variables.sort == other.instance_variables.sort) && instance_variables.all? do |v|
19
+ instance_variable_get(v) == other.instance_variable_get(v)
20
+ end
21
+ end
22
+ end
23
+
24
+ class ItemSearchRequest < ApiResult
25
+ xml_name 'ItemSearchRequest'
26
+
27
+ xml_reader :current_page, :from => 'ItemPage', :as => Integer, :else => 1
28
+ end
29
+
30
+ class ItemLookupRequest < ApiResult
31
+ end
32
+
33
+ class BrowseNodeLookupRequest < ApiResult
34
+ xml_name 'BrowseNodeLookupRequest'
35
+
36
+ xml_reader :response_groups, :as => []
37
+ end
38
+
39
+ class CartRequest < ApiResult
40
+ end
41
+
42
+ class OperationRequest < ApiResult
43
+ xml_name 'OperationRequest'
44
+
45
+ xml_reader :request_id
46
+ xml_reader :arguments, :as => {:key => '@Name', :value => '@Value'}
47
+ xml_reader :request_processing_time
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Amazon
4
+ module Associates
5
+ describe ".browse_node_lookup" do
6
+ context "with 'TopSellers' response group" do
7
+ before(:all) do
8
+ @response = Amazon::Associates.browse_node_lookup("5", :response_group => "TopSellers")
9
+ end
10
+ it_should_behave_like "Amazon Associates response"
11
+
12
+ it "should work" do
13
+ @response.browse_nodes.first.id.should == '5'
14
+ @response.request_query.response_groups.first.should == "TopSellers"
15
+ end
16
+ end
17
+
18
+ context "with 'BrowseNodeInfo' response group" do
19
+ before(:all) do
20
+ @response = Amazon::Associates.browse_node_lookup("5", :response_group => "BrowseNodeInfo")
21
+ end
22
+ it_should_behave_like "Amazon Associates response"
23
+
24
+ it "should work" do
25
+ @response.request_query.response_groups.first.should == "BrowseNodeInfo"
26
+ end
27
+ end
28
+
29
+ context "with 'NewReleases' response group" do
30
+ before(:all) do
31
+ @response = Amazon::Associates.browse_node_lookup("5", :response_group => "NewReleases")
32
+ end
33
+ it_should_behave_like "Amazon Associates response"
34
+
35
+ it "should work" do
36
+ @response.request_query.response_groups.first.should == "NewReleases"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Amazon
4
+ module Associates
5
+ describe ".item_search" do
6
+ context "when omitting required parameters" do
7
+ it "should fail" do
8
+ proc { Amazon::Associates.item_search(nil) }.should raise_error(Amazon::Associates::RequiredParameterMissing)
9
+ end
10
+ end
11
+
12
+ context "when the country is not recognized" do
13
+ it "should fail" do
14
+ proc { Amazon::Associates.item_search('ruby', :country => :asfdkjjk) }.should raise_error(Amazon::Associates::RequestError)
15
+ end
16
+ end
17
+
18
+ context "on valid request" do
19
+ before(:all) do
20
+ @response = Amazon::Associates.item_search("ruby", :item_page => 2)
21
+ end
22
+
23
+ it_should_behave_like "Amazon Associates response"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ require "lib/amazon-associates"
2
+
3
+ describe "Amazon Associates response", :shared => true do
4
+ describe "request" do
5
+ subject { @response.request }
6
+ it { should be_valid }
7
+ end
8
+ end
@@ -0,0 +1,294 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Amazon
4
+ module Associates
5
+ describe Cart do
6
+ before(:all) do
7
+ @potter = Amazon::Associates::item_lookup("0545010225").item
8
+ @batman = Amazon::Associates::item_search("batman").items.first
9
+ @joker = Amazon::Associates::item_search("joker").items.first
10
+
11
+ @existing_items = [@potter, @batman]
12
+ @existing_cart = Cart.create(@potter => 1, @batman => 3)
13
+ end
14
+
15
+ describe "a cart", :shared => true do
16
+ it "should have a valid purchase_url" do
17
+ @cart.purchase_url.should_not be_blank
18
+ end
19
+
20
+ it "should have a valid hmac" do
21
+ @cart.hmac.should_not be_blank
22
+ end
23
+
24
+ it "should have a valid id" do
25
+ @cart.id.should_not be_blank
26
+ end
27
+
28
+ describe "#items" do
29
+ subject { @cart.items }
30
+
31
+ it { should be_an_instance_of(Array) }
32
+ it { should be_frozen }
33
+ end
34
+ end
35
+
36
+ describe "a valid cart", :shared => true do
37
+ it_should_behave_like "a cart"
38
+
39
+ it "should be in a valid state" do
40
+ @cart.changed?.should be_false
41
+ end
42
+ end
43
+
44
+ describe "a modified cart", :shared => true do
45
+ it_should_behave_like "a cart"
46
+
47
+ it "should be in a valid state" do
48
+ @cart.changed?.should be_true
49
+ end
50
+ end
51
+
52
+ describe ".create" do
53
+ context "items passed in hash form" do
54
+ before(:all) do
55
+ @cart = Cart.create(@potter => 1, @batman => 3)
56
+ end
57
+
58
+ it_should_behave_like "a valid cart"
59
+
60
+ it "should create a cart with those items in the quantities provided" do
61
+ @cart.items.index_by(&:quantity).should == {1 => @potter, 3 => @batman}
62
+ end
63
+ end
64
+
65
+ context "items passed in array form" do
66
+ context "with actual items" do
67
+ before(:all) do
68
+ @cart = Cart.create([@potter, @batman])
69
+ end
70
+
71
+ it_should_behave_like "a valid cart"
72
+
73
+ it "should create a chart with those items, implicitly 1 each" do
74
+ @cart.items.should =~ [@potter, @batman]
75
+ @cart.items.map(&:quantity).should == [1, 1]
76
+ end
77
+ end
78
+
79
+ context "with hashes containing item asins" do
80
+ before(:all) do
81
+ @items = { { :asin => "0974514055" } => 2, { :asin => "0672328844" } => 3 }
82
+ @cart = Cart.create(@items)
83
+ end
84
+
85
+ it_should_behave_like "a valid cart"
86
+
87
+ describe "#items" do
88
+ it "should match the passed items asins" do
89
+ @cart.items.map(&:asin).should =~ @items.keys.map {|item| item[:asin] }
90
+ end
91
+
92
+ it "should match the passed items quantities" do
93
+ @cart.items.map(&:quantity).should == @items.values
94
+ end
95
+ end
96
+ end
97
+
98
+ context "with hashes containing offer_listing_ids" do
99
+ before(:all) do
100
+ @items = { { :offer_listing_id => "MCK%2FnCXIges8tpX%2B222nOYEqeZ4AzbrFyiHuP6pFf45N3vZHTm8hFTytRF%2FLRONNkVmt182%2BmeX72n%2BbtUcGEtpLN92Oy9Y7"} => 2 }
101
+ @cart = Cart.create(@items)
102
+ end
103
+
104
+ it_should_behave_like "a valid cart"
105
+
106
+ describe "#items" do
107
+ it "should match the passed items asins" do
108
+ @cart.items.size.should == @items.size
109
+ end
110
+
111
+ it "should not have the offer_listing_id reflected immediately in the cart item" do
112
+ @cart.items.first.cart_item_id.should_not == @items.keys.first[:offer_listing_id]
113
+ end
114
+
115
+ it "should match the passed items quantities" do
116
+ @cart.items.map(&:quantity).should == @items.values
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe ".get" do
124
+ describe "gotten cart", :shared => true do
125
+ subject { @cart }
126
+
127
+ it { should == @existing_cart }
128
+
129
+ it_should_behave_like "a valid cart"
130
+
131
+ it "should have the same items as the original cart" do
132
+ @cart.items.should == @existing_cart.items
133
+ end
134
+ end
135
+
136
+ context "with an existing cart" do
137
+ before(:all) do
138
+ @cart = Cart.get(@existing_cart)
139
+ end
140
+
141
+ it_should_behave_like "gotten cart"
142
+ end
143
+
144
+ context "with an id and hmac" do
145
+ before(:all) do
146
+ @cart = Cart.get(:id => @existing_cart.id, :hmac => @existing_cart.hmac)
147
+ end
148
+
149
+ it_should_behave_like "gotten cart"
150
+ end
151
+ end
152
+
153
+ it "should have the capacity to remove existing items (short of #clear)"
154
+ # # Test cart_modify
155
+ # def test_cart_modify
156
+ # resp = Amazon::Associates.cart_get(:id => @cart_id, :hmac => @hmac)
157
+ # cart_item_id = resp.cart.items.first.cart_item_id
158
+ # resp = Amazon::Associates.cart_modify(:id => @cart_id, :hmac => @hmac,
159
+ # :items => [{:cart_item_id => cart_item_id, :quantity => 2}])
160
+ # item = resp.cart.items.first
161
+ #
162
+ # assert resp.request.valid?
163
+ # assert_equal 2, item.quantity
164
+ # assert_not_nil resp.cart.purchase_url
165
+ # end
166
+ #
167
+
168
+ describe "#add" do
169
+ context "adding a new item, on save" do
170
+ before(:all) do
171
+ @number_added = 3
172
+ @item_added = @joker
173
+ @existing_cart.should_not include(@item_added)
174
+ @existing_cart.add(@item_added, @number_added)
175
+ @cart = @existing_cart
176
+ end
177
+
178
+ context "before save" do
179
+ it_should_behave_like "a modified cart"
180
+
181
+ it "should be unchanged" do
182
+ @existing_cart.should have(2).items
183
+ @existing_cart.quantity.should == 4
184
+ end
185
+ end
186
+
187
+ context "on save" do
188
+ before(:all) do
189
+ @existing_cart.should have(2).items
190
+ lambda { @existing_cart.save }.should change(@existing_cart, :quantity).by(@number_added)
191
+ @existing_cart.should have(3).items
192
+ end
193
+
194
+ it_should_behave_like "a valid cart"
195
+
196
+ it "should include the old items" do
197
+ @existing_cart.items.should include(*@existing_items)
198
+ end
199
+
200
+ it "should include the new item" do
201
+ @existing_cart.items.should include(@item_added)
202
+ end
203
+ end
204
+ end
205
+
206
+ context "adding an existing item", :shared => true do
207
+ context "before save" do
208
+ it_should_behave_like "a modified cart"
209
+
210
+ it "should be unchanged" do
211
+ @existing_cart.should have(2).items
212
+ @existing_cart.quantity.should == 4
213
+ end
214
+ end
215
+
216
+ context "on save" do
217
+ before(:all) do
218
+ lambda { @existing_cart.save }.should change {
219
+ @existing_cart.items.find {|item| item == @item_added }.quantity }.by(@number_added)
220
+
221
+ @cart = @existing_cart
222
+ end
223
+
224
+ it_should_behave_like "a valid cart"
225
+
226
+ it "should add the item in the quantity requested" do
227
+ @existing_cart.should have(2).items
228
+ @existing_cart.items.should =~ @existing_items
229
+ end
230
+ end
231
+ end
232
+
233
+ context "adding an existing item, on save, via add" do
234
+ before(:all) do
235
+ @number_added = 2
236
+ @item_added = @existing_items.first
237
+ @cart = @existing_cart
238
+ @cart.add(@item_added, @number_added)
239
+ end
240
+
241
+ it_should_behave_like "adding an existing item"
242
+ end
243
+
244
+ context "adding an existing item, on save, via add" do
245
+ before(:all) do
246
+ @number_added = 1
247
+ @item_added = @existing_items.first
248
+ @cart = @existing_cart
249
+ @cart << @item_added
250
+ end
251
+
252
+ it_should_behave_like "adding an existing item"
253
+ end
254
+ end
255
+
256
+ describe "#clear" do
257
+ before(:all) do
258
+ @existing_quantity = @existing_cart.quantity
259
+ @existing_quantity.should == 4
260
+ @existing_cart.empty?.should be_false
261
+ @existing_cart.clear
262
+ @cart = @existing_cart
263
+ end
264
+
265
+ context "before #save" do
266
+ it_should_behave_like "a modified cart"
267
+
268
+ it "should have no effect" do
269
+ @cart.empty?.should be_false
270
+ @cart.items.should =~ @existing_items
271
+ end
272
+ end
273
+
274
+ context "after #save" do
275
+ before(:all) do
276
+ @cart.should have(2).items
277
+
278
+ lambda { @cart.save }.should change {
279
+ @cart.quantity }.by(- @existing_quantity)
280
+ end
281
+
282
+ it_should_behave_like "a valid cart"
283
+
284
+ it "should remove all items" do
285
+ @cart.items.should == []
286
+ @cart.items.should be_empty
287
+ @cart.should be_empty
288
+ @cart.quantity.should == 0
289
+ end
290
+ end
291
+ end
292
+ end
293
+ end
294
+ end