sucker 1.1.4 → 1.2.0

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 (47) hide show
  1. data/README.md +13 -19
  2. data/lib/sucker.rb +4 -4
  3. data/lib/sucker/parameters.rb +28 -0
  4. data/lib/sucker/request.rb +100 -88
  5. data/lib/sucker/response.rb +10 -0
  6. data/lib/sucker/version.rb +1 -1
  7. data/spec/fixtures/cassette_library/{unit → spec}/sucker/request.yml +4 -4
  8. data/spec/fixtures/cassette_library/spec/sucker/response.yml +26 -0
  9. data/spec/sucker/parameters_spec.rb +61 -0
  10. data/spec/sucker/request_spec.rb +276 -0
  11. data/spec/{unit/sucker → sucker}/response_spec.rb +39 -60
  12. data/spec/{unit/sucker_spec.rb → sucker_spec.rb} +0 -4
  13. data/spec/support/vcr.rb +0 -1
  14. metadata +69 -114
  15. data/spec/fixtures/cassette_library/integration/alternate_versions.yml +0 -26
  16. data/spec/fixtures/cassette_library/integration/errors.yml +0 -26
  17. data/spec/fixtures/cassette_library/integration/france.yml +0 -26
  18. data/spec/fixtures/cassette_library/integration/images.yml +0 -26
  19. data/spec/fixtures/cassette_library/integration/item_lookup/multiple.yml +0 -26
  20. data/spec/fixtures/cassette_library/integration/item_lookup/single.yml +0 -26
  21. data/spec/fixtures/cassette_library/integration/item_search.yml +0 -26
  22. data/spec/fixtures/cassette_library/integration/japan.yml +0 -26
  23. data/spec/fixtures/cassette_library/integration/keyword_search.yml +0 -26
  24. data/spec/fixtures/cassette_library/integration/kindle.yml +0 -26
  25. data/spec/fixtures/cassette_library/integration/kindle_2.yml +0 -26
  26. data/spec/fixtures/cassette_library/integration/multiple_locales.yml +0 -151
  27. data/spec/fixtures/cassette_library/integration/power_search.yml +0 -26
  28. data/spec/fixtures/cassette_library/integration/related_items/child.yml +0 -26
  29. data/spec/fixtures/cassette_library/integration/related_items/parent.yml +0 -26
  30. data/spec/fixtures/cassette_library/integration/seller_listings_search.yml +0 -26
  31. data/spec/fixtures/cassette_library/integration/twenty_items.yml +0 -26
  32. data/spec/fixtures/cassette_library/unit/sucker/response.yml +0 -26
  33. data/spec/integration/alternate_versions_spec.rb +0 -35
  34. data/spec/integration/errors_spec.rb +0 -40
  35. data/spec/integration/france_spec.rb +0 -42
  36. data/spec/integration/images_spec.rb +0 -41
  37. data/spec/integration/item_lookup_spec.rb +0 -71
  38. data/spec/integration/item_search_spec.rb +0 -41
  39. data/spec/integration/japan_spec.rb +0 -35
  40. data/spec/integration/keyword_search_spec.rb +0 -33
  41. data/spec/integration/kindle_spec.rb +0 -55
  42. data/spec/integration/multiple_locales_spec.rb +0 -70
  43. data/spec/integration/power_search_spec.rb +0 -41
  44. data/spec/integration/related_items_spec.rb +0 -53
  45. data/spec/integration/seller_listing_search_spec.rb +0 -32
  46. data/spec/integration/twenty_items_spec.rb +0 -49
  47. data/spec/unit/sucker/request_spec.rb +0 -282
@@ -1,70 +0,0 @@
1
- # encoding: utf-8
2
- require "spec_helper"
3
-
4
- module Sucker
5
-
6
- describe "Item lookup" do
7
-
8
- use_vcr_cassette "integration/multiple_locales", :record => :new_episodes
9
-
10
- context "when using Curl::Multi to search all locales simultaneously" do
11
-
12
- it "returns matches for all locales" do
13
- worker = Sucker.new(
14
- :key => amazon["key"],
15
- :secret => amazon["secret"])
16
- worker << {
17
- "Operation" => "ItemLookup",
18
- "IdType" => "ASIN",
19
- "ResponseGroup" => "ItemAttributes",
20
- "ItemId" => "0816614024" }
21
-
22
- bindings = worker.get_all.map do |response|
23
- item = response.find("Item").first
24
- item["ItemAttributes"]["Binding"]
25
- end
26
-
27
- bindings.uniq.should =~ %w{ Paperback Taschenbuch Broché ペーパーバック }
28
-
29
- end
30
-
31
- end
32
-
33
- context "when using threads to search all locales simultaneously" do
34
-
35
- # Use this approach to be able to throttle requests individually.
36
-
37
- it "returns matches for all locales" do
38
- locales = %w{us uk de ca fr jp}
39
-
40
- params = {
41
- "Operation" => "ItemLookup",
42
- "IdType" => "ASIN",
43
- "ResponseGroup" => "ItemAttributes",
44
- "ItemId" => "0816614024" }
45
-
46
- threads = locales.map do |locale|
47
- Thread.new do
48
- worker = Sucker.new(
49
- :locale => locale,
50
- :key => amazon["key"],
51
- :secret => amazon["secret"])
52
- worker << params
53
- Thread.current[:response] = worker.get
54
- end
55
- end
56
-
57
- bindings = threads.map do |thread|
58
- thread.join
59
- item = thread[:response].find("Item").first
60
- item["ItemAttributes"]["Binding"]
61
- end
62
-
63
- bindings.uniq.should =~ %w{ Paperback Taschenbuch Broché ペーパーバック }
64
- end
65
-
66
- end
67
-
68
- end
69
-
70
- end
@@ -1,41 +0,0 @@
1
- require "spec_helper"
2
-
3
- # http://docs.amazonwebservices.com/AWSECommerceService/2010-09-01/DG/index.html?PowerSearchSyntax.html
4
- #
5
- # author: ambrose and binding: (abridged or large print) and pubdate: after 11-1996
6
- # subject: history and (Spain or Mexico) and not military and language: Spanish
7
- # (subject: marketing and author: kotler) or (publisher: harper and subject: "high technology")
8
- # keywords: "high tech*" and not fiction and pubdate: during 1999
9
- # isbn: 0446394319 or 0306806819 or 1567993850
10
-
11
- module Sucker
12
-
13
- describe "Power search" do
14
-
15
- use_vcr_cassette "integration/power_search", :record => :new_episodes
16
-
17
- let(:worker) do
18
- worker = Sucker.new(
19
- :locale => "us",
20
- :key => amazon["key"],
21
- :secret => amazon["secret"])
22
- worker << {
23
- "Operation" => "ItemSearch",
24
- "SearchIndex" => "Books",
25
- "Power" => "author:lacan or deleuze and not fiction",
26
- "Sort" => "relevancerank" }
27
- worker
28
- end
29
-
30
- it "returns matches" do
31
- items = worker.get.find("Item")
32
- items.size.should > 0
33
- items.each do |item|
34
- item["ItemAttributes"]["Title"].should_not be_nil
35
- end
36
-
37
- end
38
-
39
- end
40
-
41
- end
@@ -1,53 +0,0 @@
1
- require "spec_helper"
2
-
3
- module Sucker
4
-
5
- describe "Item lookup" do
6
-
7
- context "when response group includes related items" do
8
-
9
- let(:worker) do
10
- worker = Sucker.new(
11
- :locale => "us",
12
- :key => amazon["key"],
13
- :secret => amazon["secret"])
14
-
15
- worker << {
16
- "Operation" => "ItemLookup",
17
- "IdType" => "ASIN",
18
- "ResponseGroup" => ["RelatedItems"],
19
- "RelationshipType" => "AuthorityTitle" }
20
-
21
- worker
22
- end
23
-
24
- context "when item is a child" do
25
-
26
- use_vcr_cassette "integration/related_items/child", :record => :new_episodes
27
-
28
- it "finds parent and related items" do
29
- worker << { "ItemId" => "0415246334" }
30
- response = worker.get
31
- response.find("RelatedItem").size.should eql 1
32
- parent_asin = response.find("RelatedItem").first["Item"]["ASIN"]
33
- end
34
-
35
- end
36
-
37
- context "when item is a parent" do
38
-
39
- use_vcr_cassette "integration/related_items/parent", :record => :new_episodes
40
-
41
- it "finds related items" do
42
- worker << { "ItemId" => "B000ASPUES" }
43
- response = worker.get
44
- response.find("RelatedItem").size.should > 1
45
- end
46
-
47
- end
48
-
49
- end
50
-
51
- end
52
-
53
- end
@@ -1,32 +0,0 @@
1
- require "spec_helper"
2
-
3
- module Sucker
4
- describe "Seller listing search" do
5
-
6
- use_vcr_cassette "integration/seller_listings_search", :record => :new_episodes
7
-
8
- let(:listings) do
9
- worker = Sucker.new(
10
- :locale => "us",
11
- :key => amazon["key"],
12
- :secret => amazon["secret"])
13
-
14
- worker << {
15
- "Operation" => "SellerListingSearch",
16
- "SellerId" => "A2JYSO6W6KEP83" }
17
-
18
- worker.get.find("SellerListings").first
19
- end
20
-
21
- it "returns page count" do
22
- listings["TotalPages"].to_i.should be > 0
23
- end
24
-
25
- it "returns listings" do
26
- listings["SellerListing"].size.should be > 0
27
- listings["SellerListing"].first.has_key?("Price").should be_true
28
- end
29
-
30
- end
31
-
32
- end
@@ -1,49 +0,0 @@
1
- require "spec_helper"
2
-
3
- module Sucker
4
- describe "Item lookup" do
5
-
6
- context "when querying for twenty items" do
7
-
8
- use_vcr_cassette "integration/twenty_items", :record => :new_episodes
9
-
10
- let(:asins) do
11
- %w{
12
- 0816614024 0143105825 0485113600 0816616779 0942299078
13
- 0816614008 144006654X 0486400360 0486417670 087220474X
14
- 0486454398 0268018359 1604246014 184467598X 0312427182
15
- 1844674282 0745640974 0745646441 0826489540 1844672972 }
16
- end
17
-
18
- let(:items) do
19
- worker = Sucker.new(
20
- :locale => "us",
21
- :key => amazon["key"],
22
- :secret => amazon["secret"])
23
-
24
- # Prep worker
25
- worker << {
26
- "Operation" => "ItemLookup",
27
- "ItemLookup.Shared.IdType" => "ASIN",
28
- "ItemLookup.Shared.Condition" => "All",
29
- "ItemLookup.Shared.MerchantId" => "All",
30
- "ItemLookup.Shared.ResponseGroup" => "OfferFull" }
31
-
32
- # Push twenty ASINs to worker
33
- worker << {
34
- "ItemLookup.1.ItemId" => asins[0, 10],
35
- "ItemLookup.2.ItemId" => asins[10, 10] }
36
-
37
- worker.get.find("Item")
38
- end
39
-
40
- it "returns all items" do
41
- items.count.should eql 20
42
- items.map { |item| item["ASIN"] }.should eql asins
43
- end
44
-
45
- end
46
-
47
- end
48
-
49
- end
@@ -1,282 +0,0 @@
1
- require "spec_helper"
2
-
3
- module Sucker
4
-
5
- describe Request do
6
-
7
- use_vcr_cassette "unit/sucker/request", :record => :new_episodes
8
-
9
- let(:worker) do
10
- Sucker.new(
11
- :locale => "us",
12
- :key => "key",
13
- :secret => "secret")
14
- end
15
-
16
- describe ".new" do
17
-
18
- it "sets default parameters" do
19
- default_parameters = {
20
- "Service" => "AWSECommerceService",
21
- "Version" => Sucker::CURRENT_AMAZON_API_VERSION }
22
- worker.parameters.should include default_parameters
23
- end
24
-
25
- end
26
-
27
- describe "#<<" do
28
-
29
- it "merges a hash into the parameters" do
30
- worker << { "foo" => "bar" }
31
- worker.parameters["foo"].should eql "bar"
32
- end
33
-
34
- end
35
-
36
- describe "#version=" do
37
-
38
- it "sets the Amazon API version" do
39
- worker.version = "foo"
40
- worker.parameters["Version"].should eql "foo"
41
- end
42
-
43
- end
44
-
45
- describe "#associate_tag" do
46
-
47
- it "returns the associate tag for the current locale" do
48
- worker.instance_variable_set(:@associate_tags, { :us => 'foo-bar'})
49
-
50
- worker.associate_tag.should eql 'foo-bar'
51
- end
52
-
53
- it "returns nil if an associate tag is not set for the current locale" do
54
- worker.associate_tag.should eql nil
55
- end
56
-
57
- end
58
-
59
- describe "#associate_tag=" do
60
-
61
- it "sets the associate tag for the current locale" do
62
- worker.associate_tag = "foo-bar"
63
- associate_tags = worker.instance_variable_get(:@associate_tags)
64
-
65
- associate_tags.keys.size.should eql 1
66
- associate_tags[:us].should eql 'foo-bar'
67
- worker.associate_tag.should eql 'foo-bar'
68
- end
69
-
70
- end
71
-
72
- describe "#associate_tags=" do
73
-
74
- it "sets associate tags for the locales" do
75
- tags = {
76
- :us => 'foo',
77
- :uk => 'bar',
78
- :de => 'baz',
79
- :ca => 'foo',
80
- :fr => 'bar',
81
- :jp => 'baz' }
82
- worker.associate_tags = tags
83
-
84
- worker.instance_variable_get(:@associate_tags).should eql tags
85
- end
86
-
87
- end
88
-
89
- describe "#curl_opts" do
90
-
91
- it "returns options for curl" do
92
- worker.curl_opts.should be_an_instance_of Hash
93
- end
94
-
95
- context "when given a block" do
96
-
97
- it "yields options for curl" do
98
- worker.curl_opts { |c| c.interface = "eth1" }
99
-
100
- worker.curl_opts[:interface].should eql "eth1"
101
- end
102
-
103
- end
104
-
105
- end
106
-
107
- describe "#get" do
108
-
109
- it "returns a response" do
110
- worker.get.class.ancestors.should include Response
111
- end
112
-
113
- it "raises an argument error if no key is provided" do
114
- worker.key = nil
115
- expect do
116
- worker.get
117
- end.to raise_error(/AWS access key missing/)
118
- end
119
-
120
- it "raises an argument error if no locale is provided" do
121
- worker.locale = nil
122
- expect do
123
- worker.get
124
- end.to raise_error(/Locale missing/)
125
- end
126
-
127
- it "sets options on curl" do
128
- easy = mock
129
- easy.should_receive(:interface=).once.with("eth1")
130
- Curl::Easy.stub!(:perform).and_yield(easy)
131
- Response.should_receive(:new).once
132
-
133
- worker.curl_opts { |c| c.interface = 'eth1' }
134
- worker.get
135
- end
136
-
137
- end
138
-
139
- describe "#get_all" do
140
-
141
- it "returns an array of responses" do
142
- responses = worker.get_all
143
-
144
- responses.should be_an_instance_of Array
145
- responses.each { |resp| resp.should be_an_instance_of Response }
146
- end
147
-
148
- context "when given a block" do
149
-
150
- it "yields responses" do
151
- count = 0
152
- worker.get_all do |resp|
153
- resp.should be_an_instance_of Response
154
- count += 1
155
- end
156
-
157
- count.should eql Request::HOSTS.size
158
- end
159
-
160
- end
161
-
162
- end
163
-
164
- describe "#key" do
165
-
166
- it "returns the Amazon AWS access key for the current locale" do
167
- worker.instance_variable_set(:@keys, { :us => 'foo' })
168
-
169
- worker.key.should eql 'foo'
170
- end
171
-
172
- end
173
-
174
- describe "#key=" do
175
-
176
- it "sets a global Amazon AWS access key" do
177
- worker.key = "foo"
178
- keys = worker.instance_variable_get(:@keys)
179
-
180
- keys.size.should eql Request::HOSTS.size
181
- keys.values.uniq.should eql ["foo"]
182
- end
183
-
184
- end
185
-
186
- describe "#keys=" do
187
-
188
- it "sets distinct Amazon AWS access keys for the locales" do
189
- keys = {
190
- :us => 'foo',
191
- :uk => 'bar',
192
- :de => 'baz',
193
- :ca => 'foo',
194
- :fr => 'bar',
195
- :jp => 'baz' }
196
- worker.keys = keys
197
-
198
- worker.instance_variable_get(:@keys).should eql keys
199
- end
200
-
201
- end
202
-
203
- context "private methods" do
204
-
205
- describe "#build_query" do
206
-
207
- let(:query) { worker.send(:build_query) }
208
-
209
- it "canonicalizes parameters" do
210
- query.should match /Service=([^&]+)&Timestamp=([^&]+)&Version=([^&]+)/
211
- end
212
-
213
- it "includes the key for the current locale" do
214
- worker.instance_variable_set(:@keys, { :us => 'foo' })
215
- query.should include 'AWSAccessKeyId=foo'
216
- end
217
-
218
- it "includes a timestamp" do
219
- query.should include 'Timestamp='
220
- end
221
-
222
- it "sorts parameters" do
223
- worker.parameters["AAA"] = "foo"
224
- query.should match /^AAA=foo/
225
- end
226
-
227
- it "converts a parameter whose value is an array to a string" do
228
- worker.parameters["Foo"] = ["bar", "baz"]
229
- query.should match /Foo=bar%2Cbaz/
230
- end
231
-
232
- it "handles integer parameter values" do
233
- worker.parameters["Foo"] = 1
234
- query.should match /Foo=1/
235
- end
236
-
237
- it "handles floating-point parameter values" do
238
- worker.parameters["Foo"] = 1.0
239
- query.should match /Foo=1/
240
- end
241
-
242
- end
243
-
244
- describe "#host" do
245
-
246
- it "returns a host" do
247
- worker.locale = "fr"
248
- worker.send(:host).should eql "ecs.amazonaws.fr"
249
- end
250
-
251
- end
252
-
253
- describe "#build_signed_query" do
254
-
255
- it "returns a signed query string" do
256
- query = worker.send :build_signed_query
257
- query.should match /&Signature=.*/
258
- end
259
-
260
- end
261
-
262
- describe "#timestamp" do
263
-
264
- it "returns a timestamp" do
265
- worker.send(:timestamp)["Timestamp"].should match /^\d+-\d+-\d+T\d+:\d+:\d+Z$/
266
- end
267
-
268
- end
269
-
270
- describe "#uri" do
271
-
272
- it "returns the URI with which to query Amazon" do
273
- worker.send(:uri).should be_an_instance_of URI::HTTP
274
- end
275
-
276
- end
277
-
278
- end
279
-
280
- end
281
-
282
- end