sucker 0.7.0 → 0.7.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.
- data/README.md +13 -7
- data/lib/sucker.rb +3 -3
- data/lib/sucker/request.rb +7 -2
- data/lib/sucker/response.rb +15 -14
- data/spec/benchmark/to_hash_implementations.rb +1 -1
- data/spec/integration/errors_spec.rb +2 -2
- data/spec/integration/france_spec.rb +2 -2
- data/spec/integration/images_spec.rb +35 -0
- data/spec/integration/item_lookup_spec.rb +4 -3
- data/spec/integration/japan_spec.rb +1 -1
- data/spec/integration/seller_listing_search_spec.rb +1 -1
- data/spec/integration/twenty_items_in_one_request_spec.rb +1 -1
- data/spec/unit/sucker/request_spec.rb +7 -0
- data/spec/unit/sucker/response_spec.rb +21 -13
- metadata +23 -16
data/README.md
CHANGED
@@ -11,9 +11,10 @@ Examples
|
|
11
11
|
Set up a worker.
|
12
12
|
|
13
13
|
worker = Sucker.new(
|
14
|
-
:locale
|
15
|
-
:key
|
16
|
-
:secret
|
14
|
+
:locale => "us",
|
15
|
+
:key => "API KEY",
|
16
|
+
:secret => "API SECRET",
|
17
|
+
:associate_tag => "ASSOCIATE TAG")
|
17
18
|
|
18
19
|
Fiddle with curl.
|
19
20
|
|
@@ -27,18 +28,23 @@ Set up a request.
|
|
27
28
|
"ItemId" => asin_batch,
|
28
29
|
"ResponseGroup" => ["ItemAttributes", "OfferFull"] }
|
29
30
|
|
30
|
-
Hit Amazon
|
31
|
+
Hit Amazon.
|
31
32
|
|
32
33
|
response = worker.get
|
33
34
|
|
34
|
-
|
35
|
+
View the internals of the response object.
|
36
|
+
|
35
37
|
p response.code,
|
36
38
|
response.time,
|
37
39
|
response.body,
|
38
40
|
response.xml
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
+
Work on the entire document or a particular node.
|
43
|
+
|
44
|
+
pp response.to_hash
|
45
|
+
|
46
|
+
response.node("Item").each { |book| do_something }
|
47
|
+
response.node("Error").each { |error| do_something }
|
42
48
|
|
43
49
|
Hit Amazon again.
|
44
50
|
|
data/lib/sucker.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require "active_support/xml_mini/nokogiri"
|
2
2
|
require "cgi"
|
3
3
|
require "curb"
|
4
|
-
require
|
4
|
+
require "digest/md5"
|
5
5
|
require "sucker/request"
|
6
6
|
require "sucker/response"
|
7
7
|
|
8
8
|
# = Sucker
|
9
9
|
# Sucker is a paper-thin Ruby wrapper to the Amazon Product Advertising API.
|
10
10
|
module Sucker
|
11
|
-
$KCODE = "u" if RUBY_VERSION
|
11
|
+
$KCODE = "u" if RUBY_VERSION < "1.9"
|
12
12
|
|
13
|
-
AMAZON_API_VERSION
|
13
|
+
AMAZON_API_VERSION = "2010-06-01"
|
14
14
|
|
15
15
|
def self.new(args={})
|
16
16
|
Sucker::Request.new(args)
|
data/lib/sucker/request.rb
CHANGED
@@ -34,6 +34,11 @@ module Sucker
|
|
34
34
|
self.parameters.merge!(hash)
|
35
35
|
end
|
36
36
|
|
37
|
+
# A helper method that sets the associate tag
|
38
|
+
def associate_tag=(token)
|
39
|
+
parameters["AssociateTag"] = token
|
40
|
+
end
|
41
|
+
|
37
42
|
# A reusable, configurable cURL object
|
38
43
|
def curl
|
39
44
|
@curl ||= Curl::Easy.new
|
@@ -52,8 +57,8 @@ module Sucker
|
|
52
57
|
end
|
53
58
|
|
54
59
|
# A helper method that sets the AWS Access Key ID
|
55
|
-
def key=(
|
56
|
-
parameters["AWSAccessKeyId"] =
|
60
|
+
def key=(token)
|
61
|
+
parameters["AWSAccessKeyId"] = token
|
57
62
|
end
|
58
63
|
|
59
64
|
private
|
data/lib/sucker/response.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Sucker
|
2
2
|
|
3
|
-
# A wrapper around the cURL response
|
3
|
+
# A Nokogiri-driven wrapper around the cURL response
|
4
4
|
class Response
|
5
5
|
attr_accessor :body, :code, :time, :xml
|
6
6
|
|
@@ -10,34 +10,35 @@ module Sucker
|
|
10
10
|
self.time = curl.total_time
|
11
11
|
end
|
12
12
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
# Queries an xpath and returns a collection of hashified matches
|
14
|
+
def node(path)
|
15
|
+
xml.xpath("//xmlns:#{path}").map { |node| strip_content(node.to_hash[path]) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Hashifies XML document or node
|
19
|
+
def to_hash
|
20
|
+
strip_content(xml.to_hash)
|
21
21
|
end
|
22
22
|
|
23
|
-
alias :
|
23
|
+
alias :to_h :to_hash
|
24
24
|
|
25
|
+
# The XML document
|
25
26
|
def xml
|
26
27
|
@xml ||= Nokogiri::XML(body)
|
27
28
|
end
|
28
29
|
|
29
30
|
private
|
30
31
|
|
31
|
-
def
|
32
|
+
def strip_content(node)
|
32
33
|
case node
|
33
34
|
when Array
|
34
|
-
node.map { |el|
|
35
|
+
node.map { |el| strip_content(el) }
|
35
36
|
when Hash
|
36
37
|
if node.keys.size == 1 && node["__content__"]
|
37
38
|
node["__content__"]
|
38
39
|
else
|
39
|
-
node.inject({}) do |
|
40
|
-
|
40
|
+
node.inject({}) do |coll, key_value|
|
41
|
+
coll.merge({ key_value.first => strip_content(key_value.last) })
|
41
42
|
end
|
42
43
|
end
|
43
44
|
else
|
@@ -34,5 +34,5 @@ response = worker.get
|
|
34
34
|
Benchmark.bm(100) do |x|
|
35
35
|
x.report("Crack") { Crack::XML.parse(response.body) }
|
36
36
|
x.report("SimpleXml") { XmlSimple.xml_in(response.body, { "ForceArray" => false }) }
|
37
|
-
x.report("AS + Nokogiri") { response.
|
37
|
+
x.report("AS + Nokogiri") { response.to_hash }
|
38
38
|
end
|
@@ -25,13 +25,13 @@ module Sucker
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it "returns two errors" do
|
28
|
-
errors = @response.
|
28
|
+
errors = @response.node("Error")
|
29
29
|
errors.size.should eql 2
|
30
30
|
errors.first["Message"].should include "not a valid value"
|
31
31
|
end
|
32
32
|
|
33
33
|
it "returns one item" do
|
34
|
-
items = @response.
|
34
|
+
items = @response.node("ItemAttributes")
|
35
35
|
items.size.should eql 1
|
36
36
|
end
|
37
37
|
end
|
@@ -24,7 +24,7 @@ module Sucker
|
|
24
24
|
context "single item" do
|
25
25
|
before do
|
26
26
|
@worker << { "ItemId" => "2070119874" }
|
27
|
-
@item = @worker.get.
|
27
|
+
@item = @worker.get.node("Item").first
|
28
28
|
end
|
29
29
|
|
30
30
|
it "returns an item" do
|
@@ -40,7 +40,7 @@ module Sucker
|
|
40
40
|
context "multiple items" do
|
41
41
|
before do
|
42
42
|
@worker << { "ItemId" => ["0816614024", "0143105825"] }
|
43
|
-
@items = @worker.get.
|
43
|
+
@items = @worker.get.node("Item")
|
44
44
|
end
|
45
45
|
|
46
46
|
it "returns two items" do
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Sucker
|
4
|
+
describe "Images response group" do
|
5
|
+
before do
|
6
|
+
@worker = Sucker.new(
|
7
|
+
:locale => "us",
|
8
|
+
:key => amazon["key"],
|
9
|
+
:secret => amazon["secret"])
|
10
|
+
|
11
|
+
@worker << {
|
12
|
+
"Operation" => "ItemLookup",
|
13
|
+
"IdType" => "ASIN",
|
14
|
+
"ResponseGroup" => "Images" }
|
15
|
+
|
16
|
+
Sucker.stub(@worker)
|
17
|
+
|
18
|
+
@worker << { "ItemId" => "0816614024" }
|
19
|
+
@response = @worker.get
|
20
|
+
@item = @response.node("Item").first
|
21
|
+
end
|
22
|
+
|
23
|
+
it "has an ASIN" do
|
24
|
+
@item["ASIN"].should eql "0816614024"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "has a large image URL" do
|
28
|
+
@item["LargeImage"]["URL"].should match /^http.*jpg$/
|
29
|
+
end
|
30
|
+
|
31
|
+
it "has an image set" do
|
32
|
+
@item["ImageSets"]["ImageSet"].should be_an_instance_of Hash
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -24,7 +24,7 @@ module Sucker
|
|
24
24
|
before do
|
25
25
|
@worker << { "ItemId" => "0816614024" }
|
26
26
|
@response = @worker.get
|
27
|
-
@item = @response.
|
27
|
+
@item = @response.node("Item").first
|
28
28
|
end
|
29
29
|
|
30
30
|
it "returns an item" do
|
@@ -41,14 +41,15 @@ module Sucker
|
|
41
41
|
end
|
42
42
|
|
43
43
|
it "returns no errors" do
|
44
|
-
@response.
|
44
|
+
@response.node("Error").should be_empty
|
45
|
+
@response.node("Error").should be_an_instance_of Array
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
48
49
|
context "multiple items" do
|
49
50
|
before do
|
50
51
|
@worker << { "ItemId" => ["0816614024", "0143105825"] }
|
51
|
-
@items = @worker.get.
|
52
|
+
@items = @worker.get.node("Item")
|
52
53
|
end
|
53
54
|
|
54
55
|
it "returns two items" do
|
@@ -25,6 +25,13 @@ module Sucker
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
context "#associate_tag=" do
|
29
|
+
it "sets the associate tag in the parameters" do
|
30
|
+
@worker.associate_tag = "foo"
|
31
|
+
@worker.parameters["AssociateTag"].should eql "foo"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
28
35
|
context "#curl" do
|
29
36
|
it "returns a cURL object" do
|
30
37
|
@worker.curl.should be_an_instance_of Curl::Easy
|
@@ -37,38 +37,46 @@ module Sucker
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
context "#
|
40
|
+
context "#node" do
|
41
|
+
it "returns a collection of hashified nodes" do
|
42
|
+
response = @response.node("ItemAttributes")
|
43
|
+
response.map { |book| book["ISBN"] }.should eql @asins
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns an empty array if there are no matches" do
|
47
|
+
response = @response.node("Foo")
|
48
|
+
response.should eql []
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "#to_hash" do
|
41
53
|
it "returns a hash" do
|
42
|
-
@response.
|
54
|
+
@response.to_hash.should be_an_instance_of Hash
|
43
55
|
end
|
44
56
|
|
45
57
|
it "converts a content hash to string" do
|
46
58
|
@response.body = "<book><title>A Thousand Plateaus</title></book>"
|
47
|
-
@response.
|
48
|
-
end
|
49
|
-
|
50
|
-
it "is aliased as to_hash" do
|
51
|
-
@response.to_hash.should eql @response.to_h
|
59
|
+
@response.to_hash["book"]["title"].should be_an_instance_of String
|
52
60
|
end
|
53
61
|
|
54
|
-
it "
|
55
|
-
response
|
56
|
-
response.
|
62
|
+
it "is aliased as to_h" do
|
63
|
+
@response.should respond_to :to_h
|
64
|
+
@response.to_h.should eql @response.to_hash
|
57
65
|
end
|
58
66
|
|
59
67
|
it "renders French" do
|
60
68
|
@response.body = "<Title>L'archéologie du savoir</Title>"
|
61
|
-
@response.
|
69
|
+
@response.to_hash["Title"].should eql "L'archéologie du savoir"
|
62
70
|
end
|
63
71
|
|
64
72
|
it "renders German" do
|
65
73
|
@response.body = "<Title>Kafka: Für eine kleine Literatur</Title>"
|
66
|
-
@response.
|
74
|
+
@response.to_hash["Title"].should eql "Kafka: Für eine kleine Literatur"
|
67
75
|
end
|
68
76
|
|
69
77
|
it "renders Japanese" do
|
70
78
|
@response.body = "<Title>スティーブ・ジョブズ 驚異のプレゼン―人々を惹きつける18の法則</Title>"
|
71
|
-
@response.
|
79
|
+
@response.to_hash["Title"].should eql "スティーブ・ジョブズ 驚異のプレゼン―人々を惹きつける18の法則"
|
72
80
|
end
|
73
81
|
end
|
74
82
|
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sucker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 1
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 7
|
8
|
-
-
|
9
|
-
version: 0.7.
|
9
|
+
- 1
|
10
|
+
version: 0.7.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Hakan Ensari
|
@@ -15,57 +16,60 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2010-08-
|
19
|
+
date: 2010-08-07 00:00:00 +01:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
23
|
+
prerelease: false
|
24
|
+
type: :runtime
|
22
25
|
name: activesupport
|
23
|
-
|
26
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
24
27
|
none: false
|
25
28
|
requirements:
|
26
29
|
- - ">="
|
27
30
|
- !ruby/object:Gem::Version
|
31
|
+
hash: 7712042
|
28
32
|
segments:
|
29
33
|
- 3
|
30
34
|
- 0
|
31
35
|
- 0
|
32
36
|
- rc
|
33
37
|
version: 3.0.0.rc
|
34
|
-
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: *id001
|
38
|
+
requirement: *id001
|
37
39
|
- !ruby/object:Gem::Dependency
|
40
|
+
prerelease: false
|
41
|
+
type: :runtime
|
38
42
|
name: nokogiri
|
39
|
-
|
43
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
40
44
|
none: false
|
41
45
|
requirements:
|
42
46
|
- - ">="
|
43
47
|
- !ruby/object:Gem::Version
|
48
|
+
hash: 113
|
44
49
|
segments:
|
45
50
|
- 1
|
46
51
|
- 4
|
47
52
|
- 3
|
48
53
|
- 1
|
49
54
|
version: 1.4.3.1
|
50
|
-
|
51
|
-
prerelease: false
|
52
|
-
version_requirements: *id002
|
55
|
+
requirement: *id002
|
53
56
|
- !ruby/object:Gem::Dependency
|
57
|
+
prerelease: false
|
58
|
+
type: :runtime
|
54
59
|
name: curb
|
55
|
-
|
60
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
56
61
|
none: false
|
57
62
|
requirements:
|
58
63
|
- - ">="
|
59
64
|
- !ruby/object:Gem::Version
|
65
|
+
hash: 105
|
60
66
|
segments:
|
61
67
|
- 0
|
62
68
|
- 7
|
63
69
|
- 7
|
64
70
|
- 1
|
65
71
|
version: 0.7.7.1
|
66
|
-
|
67
|
-
prerelease: false
|
68
|
-
version_requirements: *id003
|
72
|
+
requirement: *id003
|
69
73
|
description: A paper-thin Ruby wrapper to the Amazon Product Advertising API
|
70
74
|
email: code@papercavalier.com
|
71
75
|
executables: []
|
@@ -85,6 +89,7 @@ files:
|
|
85
89
|
- spec/benchmark/to_hash_implementations.rb
|
86
90
|
- spec/integration/errors_spec.rb
|
87
91
|
- spec/integration/france_spec.rb
|
92
|
+
- spec/integration/images_spec.rb
|
88
93
|
- spec/integration/item_lookup_spec.rb
|
89
94
|
- spec/integration/japan_spec.rb
|
90
95
|
- spec/integration/seller_listing_search_spec.rb
|
@@ -110,7 +115,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
110
115
|
requirements:
|
111
116
|
- - ">="
|
112
117
|
- !ruby/object:Gem::Version
|
113
|
-
hash:
|
118
|
+
hash: 3
|
114
119
|
segments:
|
115
120
|
- 0
|
116
121
|
version: "0"
|
@@ -119,6 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
124
|
requirements:
|
120
125
|
- - ">="
|
121
126
|
- !ruby/object:Gem::Version
|
127
|
+
hash: 3
|
122
128
|
segments:
|
123
129
|
- 0
|
124
130
|
version: "0"
|
@@ -133,6 +139,7 @@ test_files:
|
|
133
139
|
- spec/benchmark/to_hash_implementations.rb
|
134
140
|
- spec/integration/errors_spec.rb
|
135
141
|
- spec/integration/france_spec.rb
|
142
|
+
- spec/integration/images_spec.rb
|
136
143
|
- spec/integration/item_lookup_spec.rb
|
137
144
|
- spec/integration/japan_spec.rb
|
138
145
|
- spec/integration/seller_listing_search_spec.rb
|