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.
- data/README.md +13 -19
- data/lib/sucker.rb +4 -4
- data/lib/sucker/parameters.rb +28 -0
- data/lib/sucker/request.rb +100 -88
- data/lib/sucker/response.rb +10 -0
- data/lib/sucker/version.rb +1 -1
- data/spec/fixtures/cassette_library/{unit → spec}/sucker/request.yml +4 -4
- data/spec/fixtures/cassette_library/spec/sucker/response.yml +26 -0
- data/spec/sucker/parameters_spec.rb +61 -0
- data/spec/sucker/request_spec.rb +276 -0
- data/spec/{unit/sucker → sucker}/response_spec.rb +39 -60
- data/spec/{unit/sucker_spec.rb → sucker_spec.rb} +0 -4
- data/spec/support/vcr.rb +0 -1
- metadata +69 -114
- data/spec/fixtures/cassette_library/integration/alternate_versions.yml +0 -26
- data/spec/fixtures/cassette_library/integration/errors.yml +0 -26
- data/spec/fixtures/cassette_library/integration/france.yml +0 -26
- data/spec/fixtures/cassette_library/integration/images.yml +0 -26
- data/spec/fixtures/cassette_library/integration/item_lookup/multiple.yml +0 -26
- data/spec/fixtures/cassette_library/integration/item_lookup/single.yml +0 -26
- data/spec/fixtures/cassette_library/integration/item_search.yml +0 -26
- data/spec/fixtures/cassette_library/integration/japan.yml +0 -26
- data/spec/fixtures/cassette_library/integration/keyword_search.yml +0 -26
- data/spec/fixtures/cassette_library/integration/kindle.yml +0 -26
- data/spec/fixtures/cassette_library/integration/kindle_2.yml +0 -26
- data/spec/fixtures/cassette_library/integration/multiple_locales.yml +0 -151
- data/spec/fixtures/cassette_library/integration/power_search.yml +0 -26
- data/spec/fixtures/cassette_library/integration/related_items/child.yml +0 -26
- data/spec/fixtures/cassette_library/integration/related_items/parent.yml +0 -26
- data/spec/fixtures/cassette_library/integration/seller_listings_search.yml +0 -26
- data/spec/fixtures/cassette_library/integration/twenty_items.yml +0 -26
- data/spec/fixtures/cassette_library/unit/sucker/response.yml +0 -26
- data/spec/integration/alternate_versions_spec.rb +0 -35
- data/spec/integration/errors_spec.rb +0 -40
- data/spec/integration/france_spec.rb +0 -42
- data/spec/integration/images_spec.rb +0 -41
- data/spec/integration/item_lookup_spec.rb +0 -71
- data/spec/integration/item_search_spec.rb +0 -41
- data/spec/integration/japan_spec.rb +0 -35
- data/spec/integration/keyword_search_spec.rb +0 -33
- data/spec/integration/kindle_spec.rb +0 -55
- data/spec/integration/multiple_locales_spec.rb +0 -70
- data/spec/integration/power_search_spec.rb +0 -41
- data/spec/integration/related_items_spec.rb +0 -53
- data/spec/integration/seller_listing_search_spec.rb +0 -32
- data/spec/integration/twenty_items_spec.rb +0 -49
- 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
|