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 CHANGED
@@ -11,9 +11,10 @@ Examples
11
11
  Set up a worker.
12
12
 
13
13
  worker = Sucker.new(
14
- :locale => "us",
15
- :key => "API KEY",
16
- :secret => "API 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 and do something with the response.
31
+ Hit Amazon.
31
32
 
32
33
  response = worker.get
33
34
 
34
- # Response internals
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
- response.to_h("Item").each { |book| do_something }
41
- response.to_h("Error").each { |error| p error["Message"] }
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 'digest/md5'
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.include?("1.8")
11
+ $KCODE = "u" if RUBY_VERSION < "1.9"
12
12
 
13
- AMAZON_API_VERSION = "2009-11-01"
13
+ AMAZON_API_VERSION = "2010-06-01"
14
14
 
15
15
  def self.new(args={})
16
16
  Sucker::Request.new(args)
@@ -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=(key)
56
- parameters["AWSAccessKeyId"] = key
60
+ def key=(token)
61
+ parameters["AWSAccessKeyId"] = token
57
62
  end
58
63
 
59
64
  private
@@ -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
- # Hashifies XML document. Optionally, parses for a node name and returns a collection
14
- # of the hashified nodes.
15
- def to_h(path=nil)
16
- if path
17
- xml.xpath("//xmlns:#{path}").map { |node| content_to_string(node.to_hash[path]) }
18
- else
19
- content_to_string(xml.to_hash)
20
- end
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 :to_hash :to_h
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 content_to_string(node)
32
+ def strip_content(node)
32
33
  case node
33
34
  when Array
34
- node.map { |el| content_to_string(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 |el, key_value|
40
- el.merge({ key_value.first => content_to_string(key_value.last) })
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.to_h }
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.to_hash("Error")
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.to_hash("ItemAttributes")
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.to_h("Item").first
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.to_h("Item")
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.to_h("Item").first
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.to_h("Error").should be_empty
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.to_h("Item")
52
+ @items = @worker.get.node("Item")
52
53
  end
53
54
 
54
55
  it "returns two items" do
@@ -19,7 +19,7 @@ module Sucker
19
19
  context "single item" do
20
20
  before do
21
21
  @worker << { "ItemId" => "482224816X" }
22
- @item = @worker.get.to_h("Item").first
22
+ @item = @worker.get.node("Item").first
23
23
  end
24
24
 
25
25
  it "returns an array of items" do
@@ -16,7 +16,7 @@ module Sucker
16
16
 
17
17
  Sucker.stub(@worker)
18
18
 
19
- @listings = @worker.get.to_h("SellerListings").first
19
+ @listings = @worker.get.node("SellerListings").first
20
20
  end
21
21
 
22
22
  it "returns page count" do
@@ -28,7 +28,7 @@ module Sucker
28
28
 
29
29
  Sucker.stub(@worker)
30
30
 
31
- @items = @worker.get.to_hash("Item")
31
+ @items = @worker.get.node("Item")
32
32
  end
33
33
 
34
34
  it "returns 20 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 "#to_h" do
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.to_h.should be_an_instance_of Hash
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.to_h["book"]["title"].should be_an_instance_of String
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 "parses document for a node name and returns a collection of hashified nodes" do
55
- response = @response.to_hash("ItemAttributes")
56
- response.map { |book| book["ISBN"] }.should eql @asins
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.to_h["Title"].should eql "L'archéologie du savoir"
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.to_h["Title"].should eql "Kafka: Für eine kleine Literatur"
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.to_h["Title"].should eql "スティーブ・ジョブズ 驚異のプレゼン―人々を惹きつける18の法則"
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
- - 0
9
- version: 0.7.0
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-06 00:00:00 +01:00
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
- requirement: &id001 !ruby/object:Gem::Requirement
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
- type: :runtime
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
- requirement: &id002 !ruby/object:Gem::Requirement
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
- type: :runtime
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
- requirement: &id003 !ruby/object:Gem::Requirement
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
- type: :runtime
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: 4409148971650304022
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