vacuum 0.1.3 → 0.2.0.pre

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 (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
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ module Vacuum
4
+ module Request
5
+ module Signature
6
+ describe Builder do
7
+ let :url do
8
+ Addressable::URI.parse 'http://example.com/foo?bar=1'
9
+ end
10
+
11
+ let :builder do
12
+ described_class.new env, 'secret'
13
+ end
14
+
15
+ let :env do
16
+ { :method => :get, :url => url }
17
+ end
18
+
19
+ describe '#method' do
20
+ it 'returns an HTTP method' do
21
+ builder.method.should eql 'GET'
22
+ end
23
+ end
24
+
25
+ describe '#sign' do
26
+ it 'signs the request' do
27
+ builder.sign
28
+ builder.url.query.should include 'Signature='
29
+ end
30
+ end
31
+
32
+ describe '#signature' do
33
+ after do
34
+ builder.signature
35
+ end
36
+
37
+ it 'generates an HMAC-SHA signature' do
38
+ OpenSSL::HMAC.should_receive(:digest).and_return 'secret'
39
+ end
40
+
41
+ it 'base64-encodes generated signature' do
42
+ Base64.should_receive(:encode64).and_return 'a string'
43
+ end
44
+ end
45
+
46
+ describe '#sort_query' do
47
+ it 'sorts query values' do
48
+ url.query = 'baz=0&bar=1'
49
+ builder.sort_query
50
+ builder.url.query.should eql 'bar=1&baz=0'
51
+ end
52
+ end
53
+
54
+ describe '#string_to_sign' do
55
+ it 'concatenates the request method, host, path, and query' do
56
+ expected_string = %w(GET example.com /foo bar=1).join "\n"
57
+ builder.string_to_sign.should eql expected_string
58
+ end
59
+ end
60
+
61
+ describe '#timestamp' do
62
+ it 'timestamps the request' do
63
+ builder.timestamp
64
+ builder.url.query.should include 'Timestamp='
65
+ end
66
+ end
67
+
68
+ describe '#url' do
69
+ it 'returns the request URL' do
70
+ builder.url.should be_an Addressable::URI
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module Vacuum
4
+ module Request
5
+ describe Utils do
6
+ describe '.camelize' do
7
+ it 'camelizes an underscored String' do
8
+ Utils.camelize('foo_bar').should eql 'FooBar'
9
+ end
10
+ end
11
+
12
+ describe '.encode' do
13
+ it 'encodes reserved characters' do
14
+ Utils.encode(',').should eql '%2C'
15
+ end
16
+
17
+ it 'does not encode unreserved characters' do
18
+ Utils.encode('~').should eql '~'
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ module Vacuum
4
+ module Response
5
+ describe Base do
6
+ let(:body) do
7
+ '<?xml version="1.0" ?>
8
+ <children>
9
+ <child>
10
+ <name>foo</name>
11
+ </child>
12
+ <child>
13
+ <name>bar</name>
14
+ </child>
15
+ </children>'.gsub />\s+</, '><'
16
+ end
17
+
18
+ let(:response) do
19
+ described_class.new body, '200'
20
+ end
21
+
22
+ it_behaves_like 'a response'
23
+
24
+ describe '#find' do
25
+ it 'returns an array of matches' do
26
+ response.find('child').should_not be_empty
27
+ end
28
+
29
+ it 'yields matches to a block' do
30
+ names = response.find('child') do |child|
31
+ child['name']
32
+ end
33
+ names.should =~ %w(foo bar)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ module Vacuum
4
+ module Response
5
+ describe ProductAdvertising do
6
+ let(:response) do
7
+ path = File.expand_path('../../../fixtures/product_advertising', __FILE__)
8
+ body = File.read path
9
+
10
+ described_class.new body, '200'
11
+ end
12
+
13
+ it_behaves_like 'a response'
14
+
15
+ context 'when Amazon returns errors' do
16
+ before do
17
+ response.body = <<-EOF.gsub!(/>\s+</, '><').strip!
18
+ <?xml version=\"1.0\" ?>
19
+ <Response xmlns="http://example.com">
20
+ <Errors>
21
+ <Error>foo</Error>
22
+ </Errors>
23
+ </Response>
24
+ EOF
25
+ end
26
+
27
+ describe '#errors' do
28
+ it 'returns an Array of errors' do
29
+ response.errors.should =~ ['foo']
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#has_errors?' do
35
+ context 'when response does not contain any errors' do
36
+ before do
37
+ response.stub!(:errors).and_return([])
38
+ end
39
+
40
+ it 'returns false' do
41
+ response.should_not have_errors
42
+ end
43
+ end
44
+
45
+ context 'when response contains errors' do
46
+ before do
47
+ response.stub!(:errors).and_return([1])
48
+ end
49
+
50
+ it 'returns true' do
51
+ response.should have_errors
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ module Vacuum
4
+ module Response
5
+ describe Utils do
6
+ describe '.xml_to_hash' do
7
+ let(:hash) do
8
+ str = <<-XML.gsub!(/>\s+</, '><').strip!
9
+ <?xml version=\"1.0\" ?>
10
+ <ItemAttributes>
11
+ <Title>Anti-Oedipus</Title>
12
+ <Author>Gilles Deleuze</Author>
13
+ <Author>Felix Guattari</Author>
14
+ <Creator Role="Translator">Robert Hurley</Creator>
15
+ </ItemAttributes>
16
+ XML
17
+ xml = Nokogiri::XML.parse str
18
+
19
+ Utils.xml_to_hash xml
20
+ end
21
+
22
+ it 'returns a hash' do
23
+ hash.should be_an_instance_of Hash
24
+ end
25
+
26
+ it 'handles only childs' do
27
+ hash['Title'].should eql 'Anti-Oedipus'
28
+ end
29
+
30
+ it 'handles arrays' do
31
+ hash['Author'].should be_a Array
32
+ end
33
+
34
+ it 'handles attributes' do
35
+ node = hash['Creator']
36
+ node['Role'].should eql 'Translator'
37
+ node['__content__'].should eql 'Robert Hurley'
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
metadata CHANGED
@@ -1,46 +1,119 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vacuum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
5
- prerelease:
4
+ version: 0.2.0.pre
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Hakan Ensari
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-14 00:00:00.000000000 Z
12
+ date: 2012-04-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: knack
16
- requirement: &70162196586060 !ruby/object:Gem::Requirement
15
+ name: rake
16
+ requirement: &70296598618460 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '1.0'
21
+ version: '0.9'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70296598618460
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70296598617960 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '2.9'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70296598617960
36
+ - !ruby/object:Gem::Dependency
37
+ name: addressable
38
+ requirement: &70296598617500 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '2.2'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70296598617500
47
+ - !ruby/object:Gem::Dependency
48
+ name: faraday
49
+ requirement: &70296598617040 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.7.6
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70296598617040
58
+ - !ruby/object:Gem::Dependency
59
+ name: nokogiri
60
+ requirement: &70296598616580 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '1.5'
22
66
  type: :runtime
23
67
  prerelease: false
24
- version_requirements: *70162196586060
25
- description: Vacuum is a Ruby wrapper to the Amazon Product Advertising API.
68
+ version_requirements: *70296598616580
69
+ description: ! 'Vacuum is a wrapper to various Amazon Web Services (AWS) APIs, including
70
+
71
+ Product Advertising and Marketplace Web Services (MWS).
72
+
73
+ '
26
74
  email:
27
75
  - hakan.ensari@papercavalier.com
28
76
  executables: []
29
77
  extensions: []
30
78
  extra_rdoc_files: []
31
79
  files:
32
- - lib/vacuum/em.rb
33
- - lib/vacuum/request.rb
34
- - lib/vacuum/response.rb
80
+ - lib/vacuum/endpoint/base.rb
81
+ - lib/vacuum/endpoint/mws.rb
82
+ - lib/vacuum/endpoint/product_advertising.rb
83
+ - lib/vacuum/mws.rb
84
+ - lib/vacuum/product_advertising.rb
85
+ - lib/vacuum/request/base.rb
86
+ - lib/vacuum/request/mws.rb
87
+ - lib/vacuum/request/product_advertising.rb
88
+ - lib/vacuum/request/signature/authentication.rb
89
+ - lib/vacuum/request/signature/builder.rb
90
+ - lib/vacuum/request/utils.rb
91
+ - lib/vacuum/response/base.rb
92
+ - lib/vacuum/response/mws.rb
93
+ - lib/vacuum/response/product_advertising.rb
94
+ - lib/vacuum/response/utils.rb
35
95
  - lib/vacuum/version.rb
36
96
  - lib/vacuum.rb
37
97
  - LICENSE
38
98
  - README.md
39
- - spec/fixtures/http_response
99
+ - spec/fixtures/product_advertising
40
100
  - spec/spec_helper.rb
41
- - spec/vacuum/request_spec.rb
42
- - spec/vacuum/response_spec.rb
43
- homepage: http://github.com/hakanensari/vacuum
101
+ - spec/support/shared_examples/endpoint.rb
102
+ - spec/support/shared_examples/request.rb
103
+ - spec/support/shared_examples/response.rb
104
+ - spec/vacuum/endpoint/base_spec.rb
105
+ - spec/vacuum/endpoint/mws_spec.rb
106
+ - spec/vacuum/endpoint/product_advertising_spec.rb
107
+ - spec/vacuum/request/base_spec.rb
108
+ - spec/vacuum/request/mws_spec.rb
109
+ - spec/vacuum/request/product_advertising_spec.rb
110
+ - spec/vacuum/request/signature/authentication_spec.rb
111
+ - spec/vacuum/request/signature/builder_spec.rb
112
+ - spec/vacuum/request/utils_spec.rb
113
+ - spec/vacuum/response/base_spec.rb
114
+ - spec/vacuum/response/product_advertising_spec.rb
115
+ - spec/vacuum/response/utils_spec.rb
116
+ homepage: https://github.com/hakanensari/vacuum
44
117
  licenses: []
45
118
  post_install_message:
46
119
  rdoc_options: []
@@ -55,17 +128,30 @@ required_ruby_version: !ruby/object:Gem::Requirement
55
128
  required_rubygems_version: !ruby/object:Gem::Requirement
56
129
  none: false
57
130
  requirements:
58
- - - ! '>='
131
+ - - ! '>'
59
132
  - !ruby/object:Gem::Version
60
- version: '0'
133
+ version: 1.3.1
61
134
  requirements: []
62
135
  rubyforge_project:
63
136
  rubygems_version: 1.8.11
64
137
  signing_key:
65
138
  specification_version: 3
66
- summary: A Ruby wrapper to the Amazon Product Advertising API
139
+ summary: A wrapper to various Amazon Web Services APIs
67
140
  test_files:
68
- - spec/fixtures/http_response
141
+ - spec/fixtures/product_advertising
69
142
  - spec/spec_helper.rb
70
- - spec/vacuum/request_spec.rb
71
- - spec/vacuum/response_spec.rb
143
+ - spec/support/shared_examples/endpoint.rb
144
+ - spec/support/shared_examples/request.rb
145
+ - spec/support/shared_examples/response.rb
146
+ - spec/vacuum/endpoint/base_spec.rb
147
+ - spec/vacuum/endpoint/mws_spec.rb
148
+ - spec/vacuum/endpoint/product_advertising_spec.rb
149
+ - spec/vacuum/request/base_spec.rb
150
+ - spec/vacuum/request/mws_spec.rb
151
+ - spec/vacuum/request/product_advertising_spec.rb
152
+ - spec/vacuum/request/signature/authentication_spec.rb
153
+ - spec/vacuum/request/signature/builder_spec.rb
154
+ - spec/vacuum/request/utils_spec.rb
155
+ - spec/vacuum/response/base_spec.rb
156
+ - spec/vacuum/response/product_advertising_spec.rb
157
+ - spec/vacuum/response/utils_spec.rb
data/lib/vacuum/em.rb DELETED
@@ -1,21 +0,0 @@
1
- require 'vacuum'
2
- require 'em-http-request'
3
-
4
- module Vacuum
5
- class Request
6
- # Performs an async request.
7
- #
8
- # @param err [Proc] A callback to be executed if request fails
9
- # @yield Passes response to given block
10
- def aget(err = nil, &blk)
11
- http = EM::HttpRequest.new(url).get
12
- # @todo Consider using a SAX parser that can work on the chunks as they
13
- # come in?
14
- # http.stream { |chunk| parse chunk }
15
- http.callback do
16
- yield Response.new http.response, http.response_header.status
17
- end
18
- http.errback &err if err
19
- end
20
- end
21
- end
@@ -1,119 +0,0 @@
1
- module Vacuum
2
- # A wrapper around the request to the Amazon Product Advertising API.
3
- class Request
4
- # The latest Amazon API version.
5
- CURRENT_API_VERSION = '2011-08-01'
6
-
7
- # A list of Amazon endpoints.
8
- HOSTS = {
9
- :ca => 'ecs.amazonaws.ca',
10
- :cn => 'webservices.amazon.cn',
11
- :de => 'ecs.amazonaws.de',
12
- :es => 'webservices.amazon.es',
13
- :fr => 'ecs.amazonaws.fr',
14
- :it => 'webservices.amazon.it',
15
- :jp => 'ecs.amazonaws.jp',
16
- :uk => 'ecs.amazonaws.co.uk',
17
- :us => 'ecs.amazonaws.com'
18
- }
19
-
20
- # Creates a new request for given locale and credentials.
21
- #
22
- # @param [Hash] options
23
- # @option opts [#to_sym] :locale An Amazon locale
24
- # @option opts [String] :key An Amazon AWS access key ID
25
- # @option opts [String] :secret An Amazon AWS access secret key
26
- # @option opts [String] :tag An Amazon Associate tag
27
- # @raise [MissingKey] An Amazon AWS access key ID was not given
28
- # @raise [MissingSecret] An Amazon AWS secret key was not given
29
- # @raise [MissingTag] An Amazon Associate tag was not given
30
- # @return [self]
31
- def initialize(options)
32
- _reset!
33
-
34
- locale = (options[:locale] || :us).to_sym
35
- @host = HOSTS[locale] or raise BadLocale
36
- @key = options[:key] or raise MissingKey
37
- @secret = options[:secret] or raise MissingSecret
38
- @tag = options[:tag] or raise MissingTag
39
- end
40
-
41
- # Merges given parameters into the request query.
42
- #
43
- # @param [Hash] hsh Pairs of keys and values
44
- # @return [self]
45
- def build(hsh)
46
- hsh.each do |k, v|
47
- @params[k] = v.is_a?(Array) ? v.join(',') : v.to_s
48
- end
49
-
50
- self
51
- end
52
-
53
- # Replaces the request query with given parameters.
54
- #
55
- # @see(#build)
56
- def build!(hsh = {})
57
- _reset!
58
- build hsh
59
- end
60
-
61
- # Performs a request.
62
- #
63
- # @return [Vacuum::Response] A response
64
- def get
65
- res = Net::HTTP.get_response(url)
66
- Response.new(res.body, res.code)
67
- end
68
-
69
- # @return [Hash] The parameters that make up the request query.
70
- def params
71
- _default_params.merge(@params)
72
- end
73
-
74
- # @return [URI::HTTP] The URL for the API request
75
- def url
76
- URI::HTTP.build :host => @host,
77
- :path => '/onca/xml',
78
- :query => _query_string
79
- end
80
-
81
- private
82
-
83
- def _default_params
84
- default = {
85
- 'AWSAccessKeyId' => @key,
86
- 'AssociateTag' => @tag,
87
- 'Service' => 'AWSECommerceService',
88
- 'Timestamp' => _timestamp,
89
- 'Version' => CURRENT_API_VERSION
90
- }
91
- end
92
-
93
- def _escape(value)
94
- value.gsub(/([^a-zA-Z0-9_.~-]+)/) do
95
- '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
96
- end
97
- end
98
-
99
- def _query_string
100
- qs = params.sort.map { |k, v| "#{k}=" + _escape(v) }.join('&')
101
-
102
- # Sign query string.
103
- dig = OpenSSL::Digest::Digest.new 'sha256'
104
- req = ['GET', @host, '/onca/xml', qs]
105
- hmac = OpenSSL::HMAC.digest dig, @secret, req.join("\n")
106
- sig = _escape [hmac].pack('m').chomp
107
-
108
- "#{qs}&Signature=#{sig}"
109
- end
110
-
111
- def _reset!
112
- @params = {}
113
- end
114
-
115
- def _timestamp
116
- Time.now.utc.iso8601
117
- end
118
- end
119
- end
@@ -1,63 +0,0 @@
1
- module Vacuum
2
- # A wrapper around the API response.
3
- class Response
4
-
5
- # @return [String] body The response body
6
- attr_accessor :body
7
-
8
- # @return [Integer] code The HTTP status code of the response
9
- attr_accessor :code
10
-
11
- # Creates a new response.
12
- #
13
- # @param [String] body The response body
14
- # @param [#to_i] code The HTTP status code of the response
15
- def initialize(body, code)
16
- @body = body
17
- @code = code.to_i
18
- end
19
-
20
- # @return [Array] Errors in the response
21
- def errors
22
- find 'Error'
23
- end
24
-
25
- # Queries for a given attribute, yielding matching nodes to a block if
26
- # given one.
27
- #
28
- # @param [String] Query attribute to be queried
29
- # @yield Optionally passes matching nodes to a given block
30
- # @return [Array] Matches
31
- #
32
- # @example
33
- # items = res.find('Item')
34
- # asins = res.find('Item') { |item| item['ASIN'] }
35
- #
36
- def find(query)
37
- xml.xpath("//xmlns:#{query}").map do |node|
38
- hsh = Knack.from_xml node
39
- block_given? ? yield(hsh) : hsh
40
- end
41
- end
42
-
43
- # @return [true, false] Whether the response has errors
44
- def has_errors?
45
- errors.count > 0
46
- end
47
-
48
- # @return [Hash] A hash representation of the entire response.
49
- def to_hash
50
- Knack.from_xml xml
51
- end
52
-
53
- # @return [true, false] Whether the HTTP response is OK
54
- def valid?
55
- code == 200
56
- end
57
-
58
- # @return [Nokogiri::XML] the XML document
59
- def xml
60
- @xml ||= Nokogiri::XML @body
61
- end
62
- end
63
- end