amazon-associates 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
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