vacuum 0.1.3 → 0.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.md +69 -27
  2. data/lib/vacuum/endpoint/base.rb +58 -0
  3. data/lib/vacuum/endpoint/mws.rb +59 -0
  4. data/lib/vacuum/endpoint/product_advertising.rb +34 -0
  5. data/lib/vacuum/mws.rb +8 -0
  6. data/lib/vacuum/product_advertising.rb +7 -0
  7. data/lib/vacuum/request/base.rb +96 -0
  8. data/lib/vacuum/request/mws.rb +24 -0
  9. data/lib/vacuum/request/product_advertising.rb +90 -0
  10. data/lib/vacuum/request/signature/authentication.rb +32 -0
  11. data/lib/vacuum/request/signature/builder.rb +70 -0
  12. data/lib/vacuum/request/utils.rb +24 -0
  13. data/lib/vacuum/response/base.rb +57 -0
  14. data/lib/vacuum/response/mws.rb +7 -0
  15. data/lib/vacuum/response/product_advertising.rb +19 -0
  16. data/lib/vacuum/response/utils.rb +48 -0
  17. data/lib/vacuum/version.rb +1 -1
  18. data/lib/vacuum.rb +31 -11
  19. data/spec/fixtures/{http_response → product_advertising} +0 -0
  20. data/spec/spec_helper.rb +5 -4
  21. data/spec/support/shared_examples/endpoint.rb +43 -0
  22. data/spec/support/shared_examples/request.rb +95 -0
  23. data/spec/support/shared_examples/response.rb +51 -0
  24. data/spec/vacuum/endpoint/base_spec.rb +19 -0
  25. data/spec/vacuum/endpoint/mws_spec.rb +47 -0
  26. data/spec/vacuum/endpoint/product_advertising_spec.rb +25 -0
  27. data/spec/vacuum/request/base_spec.rb +22 -0
  28. data/spec/vacuum/request/mws_spec.rb +26 -0
  29. data/spec/vacuum/request/product_advertising_spec.rb +104 -0
  30. data/spec/vacuum/request/signature/authentication_spec.rb +30 -0
  31. data/spec/vacuum/request/signature/builder_spec.rb +76 -0
  32. data/spec/vacuum/request/utils_spec.rb +23 -0
  33. data/spec/vacuum/response/base_spec.rb +38 -0
  34. data/spec/vacuum/response/product_advertising_spec.rb +57 -0
  35. data/spec/vacuum/response/utils_spec.rb +42 -0
  36. metadata +107 -21
  37. data/lib/vacuum/em.rb +0 -21
  38. data/lib/vacuum/request.rb +0 -119
  39. data/lib/vacuum/response.rb +0 -63
  40. data/spec/vacuum/request_spec.rb +0 -130
  41. data/spec/vacuum/response_spec.rb +0 -80
data/README.md CHANGED
@@ -2,42 +2,84 @@
2
2
 
3
3
  [![travis] [1]] [2]
4
4
 
5
- ![vacuum] [3]
5
+ Vacuum is a Ruby wrapper to [various Amazon Web Services (AWS) APIs] [3].
6
6
 
7
- Vacuum is a minimal Ruby wrapper to the [Amazon Product Advertising API] [4].
7
+ ![vacuum] [4]
8
8
 
9
- # Usage
9
+ ## Amazon Product Advertising API
10
+
11
+ Vacuum knows the [Amazon Product Advertising API] [5] [inside out] [6].
10
12
 
11
13
  ```ruby
12
- require 'vacuum'
13
-
14
- # Create a request.
15
- req = Vacuum.new key: 'key',
16
- secret: 'secret',
17
- tag: 'tag',
18
- locale: 'us'
19
-
20
- # Build query.
21
- req.build 'Operation' => 'ItemSearch',
22
- 'SearchIndex' => 'All',
23
- 'Keywords' => 'Gilles Deleuze'
24
-
25
- # Execute.
26
- res = request.get
27
-
28
- # Consume response.
29
- if res.valid?
30
- # res.to_hash
31
- res.find('Item') do |item|
14
+ request = Vacuum.new(:product_advertising) do |config|
15
+ config.locale 'US'
16
+
17
+ config.key 'key'
18
+ config.secret 'secret'
19
+ config.tag 'tag'
20
+ end
21
+
22
+ # Use an alternative Faraday adapter.
23
+ # request.connection do |builder|
24
+ # builder.adapter :typhoeus
25
+ # end
26
+
27
+ # A barebone search request.
28
+ request.build operation: 'ItemSearch',
29
+ search_index: 'Books',
30
+ keywords: 'Deleuze'
31
+ response = request.get
32
+
33
+ # A less verbose search.
34
+ request.search :books, 'Deleuze'
35
+
36
+ if response.valid?
37
+ # response.code
38
+ # response.body
39
+ # response.errors
40
+ # response.xml # The Nokogiri XML doc
41
+ # response.to_hash
42
+ response.find('Item') do |item|
32
43
  p item['ASIN']
33
44
  end
34
45
  end
35
46
  ```
47
+ ## Amazon Marketplace Web Services API
48
+
49
+ The wrapper to the [Amazon Marketplace Web Services API] [7] is a
50
+ work-in-progress.
51
+
52
+ ```ruby
53
+ request = Vacuum.new(:mws_products) do |config|
54
+ config.locale 'US'
55
+
56
+ config.key 'key'
57
+ config.secret 'secret'
58
+ config.marketplace 'marketplace'
59
+ config.seller 'seller'
60
+ end
61
+
62
+ request.build 'Action' => 'GetLowestOfferListingsForASIN',
63
+ 'ASINList.ASIN.1' => '0231081596'
64
+ offers = request.get.find 'GetLowestOfferListingsForASINResult'
65
+ ```
66
+
67
+ ## Other AWS APIs
68
+
69
+ Vacuum should work with EC2, S3, IAM, SimpleDB, SQS, SNS, SES, ELB, CW, and so
70
+ on. Implement and send a pull request.
71
+
72
+ # Addendum
73
+
74
+ ![vacuums] [8]
36
75
 
37
- Read further [here] [5].
76
+ > Workers queuing to crawl AWS.
38
77
 
39
78
  [1]: https://secure.travis-ci.org/hakanensari/vacuum.png
40
79
  [2]: http://travis-ci.org/hakanensari/vacuum
41
- [3]: http://f.cl.ly/items/2k2X0e2u0G3k1c260D2u/vacuum.png
42
- [4]: https://affiliate-program.amazon.co.uk/gp/advertising/api/detail/main.html
43
- [5]: https://github.com/hakanensari/vacuum/blob/master/examples/
80
+ [3]: http://aws.amazon.com/
81
+ [4]: http://f.cl.ly/items/2k2X0e2u0G3k1c260D2u/vacuum.png
82
+ [5]: https://affiliate-program.amazon.co.uk/gp/advertising/api/detail/main.html
83
+ [6]: https://github.com/hakanensari/vacuum/blob/master/examples/product_advertising/
84
+ [7]: https://developer.amazonservices.com/gp/mws/docs.html
85
+ [8]: http://f.cl.ly/items/1Q3W372A0H3M0w2H1e0W/hoover.jpeg
@@ -0,0 +1,58 @@
1
+ module Vacuum
2
+ module Endpoint
3
+ # An Amazon Web Services (AWS) API endpoint.
4
+ class Base
5
+ LOCALES = %w(CA CN DE ES FR IT JP UK US)
6
+
7
+ # Raises a Not Implemented Error.
8
+ #
9
+ # When implemented, this should return a String AWS API host.
10
+ def host
11
+ raise NotImplementedError
12
+ end
13
+
14
+ # Returns the String AWS access key ID.
15
+ #
16
+ # Raises a Missing Key error if key is missing.
17
+ def key
18
+ @key or raise MissingKey
19
+ end
20
+
21
+ # Sets the String AWS access key ID.
22
+ attr_writer :key
23
+
24
+ # Returns the String AWS API locale (default: US).
25
+ #
26
+ # Raises a Bad Locale error if locale is not valid.
27
+ def locale
28
+ @locale ||= 'US'
29
+ LOCALES.include? @locale or raise BadLocale
30
+
31
+ @locale
32
+ end
33
+
34
+ # Sets the String AWS API locale.
35
+ attr_writer :locale
36
+
37
+ # Returns the String AWS access secret key.
38
+ #
39
+ # Raises a Missing Secret error if secret is missing.
40
+ def secret
41
+ @secret or raise MissingSecret
42
+ end
43
+
44
+ # Sets the String AWS access secret key.
45
+ attr_writer :secret
46
+
47
+ # Returns a String user agent for the AWS API request.
48
+ def user_agent
49
+ engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
50
+ language = [engine, RUBY_VERSION, "p#{RUBY_PATCHLEVEL}"].join ' '
51
+ hostname = `hostname`.chomp
52
+ version = Vacuum::VERSION
53
+
54
+ "Vacuum/#{version} (Language=#{language}; Host=#{hostname})"
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,59 @@
1
+ module Vacuum
2
+ module Endpoint
3
+ # A Marketplace Web Services (MWS) API endpoint.
4
+ class MWS < Base
5
+ # A list of MWS API hosts.
6
+ HOSTS = {
7
+ 'CA' => 'mws.amazonservices.ca',
8
+ 'CN' => 'mws.amazonservices.com.cn',
9
+ 'DE' => 'mws-eu.amazonservices.com',
10
+ 'ES' => 'mws-eu.amazonservices.com',
11
+ 'FR' => 'mws-eu.amazonservices.com',
12
+ 'IT' => 'mws-eu.amazonservices.com',
13
+ 'JP' => 'mws.amazonservices.jp',
14
+ 'UK' => 'mws-eu.amazonservices.com',
15
+ 'US' => 'mws.amazonservices.com'
16
+ }
17
+
18
+ # Internal: Gets/Sets the Symbol MWS API type.
19
+ attr_accessor :api
20
+
21
+ # Returns a String MWS API host.
22
+ def host
23
+ HOSTS[locale]
24
+ end
25
+
26
+ # Sets the String marketplace ID.
27
+ #
28
+ # Raises a Missing Marketplace error if marketplace ID is missing.
29
+ def marketplace
30
+ @marketplace or raise MissingMarketplace
31
+ end
32
+
33
+ # Sets the String marketplace ID tag.
34
+ attr_writer :marketplace
35
+
36
+ # Returns a String MWS API URL path.
37
+ #
38
+ # Raises a Not Implemented Error if API is not implemented.
39
+ def path
40
+ case api
41
+ when :products
42
+ '/Products/2011-10-01'
43
+ else
44
+ raise NotImplementedError
45
+ end
46
+ end
47
+
48
+ # Sets the String seller ID.
49
+ #
50
+ # Raises a Missing Seller error if seller ID is missing.
51
+ def seller
52
+ @seller or raise MissingSeller
53
+ end
54
+
55
+ # Sets the String seller ID tag.
56
+ attr_writer :seller
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ module Vacuum
2
+ module Endpoint
3
+ # A Product Advertising API endpoint.
4
+ class ProductAdvertising < Base
5
+ # A list of Product Advertising API hosts.
6
+ HOSTS = {
7
+ 'CA' => 'ecs.amazonaws.ca',
8
+ 'CN' => 'webservices.amazon.cn',
9
+ 'DE' => 'ecs.amazonaws.de',
10
+ 'ES' => 'webservices.amazon.es',
11
+ 'FR' => 'ecs.amazonaws.fr',
12
+ 'IT' => 'webservices.amazon.it',
13
+ 'JP' => 'ecs.amazonaws.jp',
14
+ 'UK' => 'ecs.amazonaws.co.uk',
15
+ 'US' => 'ecs.amazonaws.com'
16
+ }
17
+
18
+ # Returns a String Product Advertising API host.
19
+ def host
20
+ HOSTS[locale]
21
+ end
22
+
23
+ # Sets the String Associate tag.
24
+ #
25
+ # Raises a Missing Tag error if tag is missing.
26
+ def tag
27
+ @tag or raise MissingTag
28
+ end
29
+
30
+ # Sets the String Associate tag.
31
+ attr_writer :tag
32
+ end
33
+ end
34
+ end
data/lib/vacuum/mws.rb ADDED
@@ -0,0 +1,8 @@
1
+ %w(endpoint request response).each do |path|
2
+ require "vacuum/#{path}/mws"
3
+ end
4
+
5
+ module Vacuum
6
+ class MissingMarketplace < ArgumentError; end
7
+ class MissingSeller < ArgumentError; end
8
+ end
@@ -0,0 +1,7 @@
1
+ %w(endpoint request response).each do |path|
2
+ require "vacuum/#{path}/product_advertising"
3
+ end
4
+
5
+ module Vacuum
6
+ class MissingTag < ArgumentError; end
7
+ end
@@ -0,0 +1,96 @@
1
+ module Vacuum
2
+ module Request
3
+ # An Amazon Web Services (AWS) API request.
4
+ class Base
5
+ # Returns the AWS API Endpoint.
6
+ attr :endpoint
7
+
8
+ # Creates a new request for given locale and credentials.
9
+ #
10
+ # Yields the AWS API endpoint if a block is given.
11
+ def initialize(&blk)
12
+ @parameters = {}
13
+ @endpoint = Endpoint.const_get(class_basename).new
14
+
15
+ configure &blk if block_given?
16
+ end
17
+
18
+ # Adds given parameters to the request.
19
+ #
20
+ # hsh - A Hash of parameter key and value pairs.
21
+ #
22
+ # Returns self.
23
+ def build(hsh)
24
+ hsh.each do |k, v|
25
+ k = Utils.camelize k.to_s if k.is_a? Symbol
26
+ @parameters[k] = v.is_a?(Array) ? v.join(',') : v.to_s
27
+ end
28
+
29
+ self
30
+ end
31
+
32
+ # Resets the request to the given parameters.
33
+ #
34
+ # hsh - A Hash of parameter key and value pairs.
35
+ #
36
+ # Returns self.
37
+ def build!(hsh = {})
38
+ @parameters = {}
39
+ build hsh
40
+ end
41
+
42
+ # Yields the AWS API Endpoint.
43
+ #
44
+ # Returns nothing.
45
+ def configure(&blk)
46
+ yield @endpoint
47
+ end
48
+
49
+ # Returns a Faraday::Connection.
50
+ #
51
+ # Yields a Faraday::Builder to configure the connection if a block is
52
+ # given.
53
+ def connection
54
+ @connection ||= Faraday.new do |builder|
55
+ builder.use Signature::Authentication, endpoint.secret
56
+
57
+ yield builder if block_given?
58
+
59
+ unless builder.handlers.any? { |h| h.name.include? ':Adapter:' }
60
+ builder.adapter Faraday.default_adapter
61
+ end
62
+ end
63
+ end
64
+
65
+ # Performs the AWS API request.
66
+ #
67
+ # Returns a Vacuum::Response::Base or a subclass thereof.
68
+ def get
69
+ res = connection.get url
70
+ Response.const_get(class_basename).new res.body, res.status
71
+ end
72
+
73
+ # Returns the Hash parameters of the AWS API request.
74
+ def parameters
75
+ default_parameters.merge @parameters
76
+ end
77
+
78
+ # Raises a Not Implemented Error.
79
+ #
80
+ # When implemented, this should return an Addressable::URI.
81
+ def url
82
+ raise NotImplementedError
83
+ end
84
+
85
+ private
86
+
87
+ def class_basename
88
+ self.class.name.split('::').last
89
+ end
90
+
91
+ def default_parameters
92
+ { 'AWSAccessKeyId' => endpoint.key }
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,24 @@
1
+ module Vacuum
2
+ module Request
3
+ # A Marketplace Web Services (MWS) API request.
4
+ class MWS < Base
5
+ # Returns the Addressable::URI URL of the MWS API request.
6
+ def url
7
+ Addressable::URI.new :scheme => 'https',
8
+ :host => endpoint.host,
9
+ :path => endpoint.path,
10
+ :query_values => parameters
11
+ end
12
+
13
+ private
14
+
15
+ def default_parameters
16
+ super.merge 'MarketplaceId' => endpoint.marketplace,
17
+ 'SellerId' => endpoint.seller,
18
+ 'Service' => 'AWSECommerceService',
19
+ 'SignatureMethod' => 'HmacSHA256',
20
+ 'SignatureVersion' => 2
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,90 @@
1
+ module Vacuum
2
+ module Request
3
+ # A Product Advertising API request.
4
+ class ProductAdvertising < Base
5
+ # Looks up attributes of up to twenty items.
6
+ #
7
+ # item_ids - Splat Array of item IDs. The last element may optionally
8
+ # specify a Hash of parameter key and value pairs.
9
+ #
10
+ # Examples
11
+ #
12
+ # request.find '0679753354', response_group: 'Images'
13
+ #
14
+ # Returns a Vacuum::Response.
15
+ def find(*item_ids)
16
+ given_params = item_ids.last.is_a?(Hash) ? item_ids.pop : {}
17
+
18
+ params =
19
+ case item_ids.size
20
+ when 1..10
21
+ {
22
+ 'Operation' => 'ItemLookup',
23
+ 'ItemId' => item_ids
24
+ }.merge given_params
25
+ when 11..20
26
+ default = {
27
+ 'Operation' => 'ItemLookup',
28
+ 'ItemId.1.ItemId' => item_ids.shift(10),
29
+ 'ItemId.2.ItemId' => item_ids
30
+ }
31
+ given_params.reduce(default) do |a, (k, v)|
32
+ a.merge "ItemLookup.Shared.#{Utils.camelize k.to_s}" => v
33
+ end
34
+ else
35
+ raise ArgumentError, "Can't look up #{item_ids.size} items"
36
+ end
37
+ build! params
38
+
39
+ get
40
+ end
41
+
42
+ # Searches for items that satisfy the given criteria, including one or
43
+ # more search indices.
44
+ #
45
+ # index - Symbol search index
46
+ # query_or_params - String keyword query or Hash of parameter key and
47
+ # value pairs (default: nil).
48
+ #
49
+ # Examples
50
+ #
51
+ # # Search the entire Amazon catalog for Deleuze.
52
+ # request.search :all, 'Deleuze'
53
+ #
54
+ # # Search books for non-fiction titles authored by Deleuze and sort
55
+ # # results by relevance.
56
+ # request.search :books, power: 'author:lacan and not fiction',
57
+ # sort: 'relevancerank'
58
+ #
59
+ def search(index, query_or_params = nil)
60
+ params = case query_or_params
61
+ when String
62
+ { 'Keywords' => query_or_params }
63
+ else
64
+ query_or_params
65
+ end
66
+ build! params.merge! 'Operation' => 'ItemSearch',
67
+ 'SearchIndex' => Utils.camelize(index.to_s)
68
+
69
+ get
70
+ end
71
+
72
+ # Returns the Addressable::URI URL of the Product Advertising API
73
+ # request.
74
+ def url
75
+ Addressable::URI.new :scheme => 'http',
76
+ :host => endpoint.host,
77
+ :path => '/onca/xml',
78
+ :query_values => parameters
79
+ end
80
+
81
+ private
82
+
83
+ def default_parameters
84
+ super.merge 'AssociateTag' => endpoint.tag,
85
+ 'Service' => 'AWSECommerceService',
86
+ 'Version' => '2011-08-01'
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,32 @@
1
+ module Vacuum
2
+ module Request
3
+ module Signature
4
+ # Internal: Middleware that signs REST requests to various Amazon API
5
+ # endpoints.
6
+ class Authentication < Faraday::Middleware
7
+ # Initializes the middleware.
8
+ #
9
+ # app - An Object that responds to `call` and returns a
10
+ # Faraday::Response.
11
+ # secret - The String Amazon AWS access secret key.
12
+ def initialize(app, secret)
13
+ @secret = secret
14
+ super app
15
+ end
16
+
17
+ # Signs the request.
18
+ #
19
+ # env - A Hash that contains info about the request.
20
+ #
21
+ # Returns an Object that responds to `call` and returns a
22
+ # Faraday::Response.
23
+ def call(env)
24
+ builder = Builder.new env, @secret
25
+ builder.timestamp.sort_query.sign
26
+
27
+ @app.call builder.env
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,70 @@
1
+ module Vacuum
2
+ module Request
3
+ module Signature
4
+ # Internal: Signs a request to an Amazon API with an HMAC-SHA256
5
+ # signature.
6
+ class Builder
7
+ # Returns a Hash that contains info about the request.
8
+ attr :env
9
+
10
+ # Returns the String Amazon AWS access secret key.
11
+ attr :secret
12
+
13
+ # Initializes a new Builder.
14
+ #
15
+ # env - A Hash that contains info about the request.
16
+ def initialize(env, secret)
17
+ env[:url] = Addressable::URI.parse env[:url]
18
+ @env, @secret = env, secret
19
+ end
20
+
21
+ # Returns the String name of the HTTP method used by the request.
22
+ def method
23
+ env[:method].to_s.upcase
24
+ end
25
+
26
+ # Signs the request.
27
+ #
28
+ # Returns self.
29
+ def sign
30
+ url.query = url.query.to_s + "&Signature=#{Utils.encode signature}"
31
+ self
32
+ end
33
+
34
+ # Returns a String signature.
35
+ def signature
36
+ sha256 = OpenSSL::Digest::SHA256.new
37
+ hash = OpenSSL::HMAC.digest sha256, secret, string_to_sign
38
+
39
+ Base64.encode64(hash).chomp
40
+ end
41
+
42
+ # Sorts the URL query values of the request.
43
+ #
44
+ # Returns self.
45
+ def sort_query
46
+ url.query_values = url.query_values
47
+ self
48
+ end
49
+
50
+ # Returns a String to sign based on pseudo-grammar specified by Amazon.
51
+ def string_to_sign
52
+ [method, url.host, url.path, url.query].join "\n"
53
+ end
54
+
55
+ # Timestamps the request.
56
+ #
57
+ # Returns self.
58
+ def timestamp
59
+ url.query = url.query.to_s + "&Timestamp=#{Utils.encode Time.now.utc.iso8601}"
60
+ self
61
+ end
62
+
63
+ # Returns the Addressable::URI URL of the request.
64
+ def url
65
+ env[:url]
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,24 @@
1
+ module Vacuum
2
+ module Request
3
+ module Utils
4
+ # Camelizes a value.
5
+ #
6
+ # val - A String value.
7
+ #
8
+ # Returns an upper-camelcased String.
9
+ def self.camelize(val)
10
+ val.split('_').map(&:capitalize).join
11
+ end
12
+
13
+ # Percent encodes a URI component.
14
+ #
15
+ # component - The String URI component to encode.
16
+ #
17
+ # Returns the String encoded component.
18
+ def self.encode(component)
19
+ Addressable::URI.encode_component \
20
+ component, Addressable::URI::CharacterClasses::UNRESERVED
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,57 @@
1
+ module Vacuum
2
+ module Response
3
+ # An Amazon Web Services (AWS) API response.
4
+ class Base
5
+ # Gets/Sets the String response body.
6
+ attr_accessor :body
7
+
8
+ # Gets/Sets the Integer HTTP response code.
9
+ attr_accessor :code
10
+
11
+ # Initializes a new Response.
12
+ #
13
+ # body - The String response body.
14
+ # code - An HTTP response code that responds to to_i.
15
+ def initialize(body, code)
16
+ @body, @code = body, code.to_i
17
+ end
18
+
19
+ # Queries the response.
20
+ #
21
+ # query - String attribute to be queried.
22
+ #
23
+ # Yields matching nodes to a given block if one is given.
24
+ #
25
+ # Returns an Array of matching nodes or the return values of the yielded
26
+ # block if latter was given.
27
+ def find(query)
28
+ path = if xml.namespaces.empty?
29
+ "//#{query}"
30
+ else
31
+ "//xmlns:#{query}"
32
+ end
33
+
34
+ xml.xpath(path).map do |node|
35
+ hsh = Utils.xml_to_hash node
36
+ block_given? ? yield(hsh) : hsh
37
+ end
38
+ end
39
+ alias [] find
40
+
41
+ # Returns a Hash representation of the response.
42
+ def to_hash
43
+ Utils.xml_to_hash xml
44
+ end
45
+
46
+ # Returns whether the HTTP response is OK.
47
+ def valid?
48
+ code == 200
49
+ end
50
+
51
+ # Returns an XML document.
52
+ def xml
53
+ @xml ||= Nokogiri::XML.parse @body
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,7 @@
1
+ module Vacuum
2
+ module Response
3
+ # A Marketplace Web Services (MWS) API response.
4
+ class MWS < Base
5
+ end
6
+ end
7
+ end