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
@@ -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