jeff 0.7.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a66b88a3b909b10d4577dc89fe4da2bedf7905c7
4
- data.tar.gz: edc367e887327c4e66ac370b256bc739fb6be19b
3
+ metadata.gz: 24de0bfe87f97a2aea2d0c061de58a2c84baadfa
4
+ data.tar.gz: 64005a0efd8e83d49e5967dc17577610c9880833
5
5
  SHA512:
6
- metadata.gz: c5188fd0a85805c0201f81e0a5abc73f6dd3328d9194e5d643117a30474c8d4418650c5b42fb4500ae8c0fb006a30f1f7b7602867e1e5c031bb77b4a133dd102
7
- data.tar.gz: eaf0891056bf51aeda8b8483715ae14334b1389a46d854110a4adb9c9ef8616dd3a7c749a6a3ce52c267d5b86ac4b1c9d74a87c86834fc2a93db4ad9560e492c
6
+ metadata.gz: 6323103376a7887a697808d401e5e98240811854ea0a9f2b099d87569b2e789a4684bfb658d2be159b43da008894cfac31a5d58a0ed1259623f147bf00acb732
7
+ data.tar.gz: 57ec8babfe50c0d041ae0641880cbcd6573eb81d89b6264ee3cf17fdce7d273e2e12a9e3ed5cb751c18cac73cabad7827349320aa34a12082ee94e90f298768b
@@ -1,5 +1,5 @@
1
1
  rvm:
2
- - 2.0.0
3
2
  - 1.9.3
3
+ - 2.0.0
4
4
  - jruby-19mode
5
- - rbx-19mode
5
+ - rbx-2.1.1
data/README.md CHANGED
@@ -1,20 +1,26 @@
1
1
  # Jeff
2
2
 
3
- Jeff mixes in client behaviour for Amazon Web Services (AWS) which require
3
+ Jeff mixes in client behaviour for Amazon Web Services (AWS) that require
4
4
  [Signature version 2 authentication][sig].
5
5
 
6
- Monsieur Jeff couples with [Excon][exc].
6
+ Jeff builds on [Excon][exc].
7
7
 
8
8
  ![jeff][jef]
9
9
 
10
10
  ## Usage
11
11
 
12
- A somewhat contrived example:
12
+ A minimal example:
13
13
 
14
14
  ```ruby
15
- Service = Struct.new(:aws_access_key_id, :aws_secret_access_key) do
15
+ ProductsService = Struct.new(:aws_access_key_id, :aws_secret_access_key) do
16
16
  include Jeff
17
17
 
18
+ PARSER = /Status>([^<]+)/
19
+
20
+ def self.status
21
+ new('foo', 'bar').status
22
+ end
23
+
18
24
  def aws_endpoint
19
25
  'https://mws.amazonservices.com/Products/2011-10-01'
20
26
  end
@@ -22,13 +28,13 @@ Service = Struct.new(:aws_access_key_id, :aws_secret_access_key) do
22
28
  def status
23
29
  get(query: { 'Action' => 'GetServiceStatus' })
24
30
  .body
25
- .match(/Status>([^<]+)/)
31
+ .match(PARSER)
26
32
  .[](1)
27
33
  end
28
34
  end
29
35
 
30
- srv = Service.new('key', 'secret')
31
- srv.status # => "GREEN"
36
+ ProductsService.status
37
+ # => "GREEN"
32
38
  ```
33
39
 
34
40
  [Vacuum][vac] and [Peddler][ped] implement Jeff.
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new do |t|
5
5
  t.libs.push 'lib'
6
- t.test_files = FileList['spec/*_spec.rb']
6
+ t.test_files = FileList['test/test_*.rb']
7
7
  t.verbose = true
8
8
  end
9
9
 
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ['lib']
16
16
  gem.version = Jeff::VERSION
17
17
 
18
- gem.add_dependency 'excon', '~> 0.28.0'
18
+ gem.add_dependency 'excon', '~> 0.29.0'
19
19
  gem.add_development_dependency 'minitest'
20
20
  gem.add_development_dependency 'rake'
21
21
 
@@ -1,4 +1,4 @@
1
- # The only external dependency.
1
+ # Jeff's only external dependency.
2
2
  require 'excon'
3
3
 
4
4
  # Standard library dependencies.
@@ -10,8 +10,6 @@ require 'jeff/version'
10
10
 
11
11
  # Jeff mixes in client behaviour for Amazon Web Services (AWS) that require
12
12
  # Signature version 2 authentication.
13
- #
14
- # It's Jeff, as in Jeff Bezos.
15
13
  module Jeff
16
14
  # Converts a query value to a sorted query string.
17
15
  class Query
@@ -40,11 +38,11 @@ module Jeff
40
38
  end
41
39
 
42
40
  # Signs an AWS request.
43
- class Request
41
+ class Signer
44
42
  attr :method, :host, :path, :query_string
45
43
 
46
44
  def initialize(method, host, path, query_string)
47
- @method = method
45
+ @method = method.upcase
48
46
  @host = host
49
47
  @path = path
50
48
  @query_string = query_string
@@ -76,7 +74,7 @@ module Jeff
76
74
  end
77
75
  end
78
76
 
79
- # Because Ruby's CGI escapes ~, we have to resort to writing our own escape.
77
+ # Because Ruby's CGI escapes tilde, use a custom escape.
80
78
  module Utils
81
79
  UNRESERVED = /([^\w.~-]+)/
82
80
 
@@ -87,11 +85,9 @@ module Jeff
87
85
  end
88
86
  end
89
87
 
90
- # Amazon recommends to include a User-Agent header with every request to
88
+ # Amazon recommends to include a User-Agent header with every request to
91
89
  # identify the application, its version number, programming language, and
92
90
  # host.
93
- #
94
- # If not happy, override.
95
91
  USER_AGENT = "Jeff/#{VERSION} (Language=Ruby; #{`hostname`.chomp})"
96
92
 
97
93
  def self.included(base)
@@ -118,30 +114,16 @@ module Jeff
118
114
  )
119
115
  end
120
116
 
121
- # Accessors for required AWS attributes.
122
- attr_accessor :aws_endpoint, :aws_access_key_id, :aws_secret_access_key
123
-
124
- # Keep these deprecated attribute accessors around for a while.
125
- # TODO Remove when cutting v1.0.
126
- %w(
127
- endpoint aws_endpoint
128
- endpoint= aws_endpoint=
129
- key aws_access_key_id
130
- key= aws_access_key_id=
131
- secret aws_secret_access_key
132
- secret= aws_secret_access_key=
133
- ).each_slice(2) do |old, new|
134
- if old.end_with?('=')
135
- define_method(old) do |value|
136
- warn "[DEPRECATION] Use #{new}"
137
- self.send(new, value)
138
- end
139
- else
140
- define_method(old) do
141
- warn "[DEPRECATION] Use #{new}"
142
- self.send(new)
143
- end
144
- end
117
+ attr_accessor :aws_endpoint
118
+
119
+ attr_writer :aws_access_key_id, :aws_secret_access_key
120
+
121
+ def aws_access_key_id
122
+ @aws_access_key_id || ENV['AWS_ACCESS_KEY_ID']
123
+ end
124
+
125
+ def aws_secret_access_key
126
+ @aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY']
145
127
  end
146
128
 
147
129
  # Generate HTTP request verb methods.
@@ -157,36 +139,33 @@ module Jeff
157
139
  private
158
140
 
159
141
  def build_options(options)
160
- # Add a Content-MD5 header if we're uploading a file.
142
+ # Add Content-MD5 header if uploading a file.
161
143
  if options.has_key?(:body)
162
144
  md5 = Content.new(options[:body]).md5
163
145
  (options[:headers] ||= {}).store('Content-MD5', md5)
164
146
  end
165
147
 
166
- # Build the query string.
167
- values = self.class.params
168
- .reduce({}) { |a, (k, v)|
169
- a.update(k => (v.respond_to?(:call) ? instance_exec(&v) : v))
170
- }
171
- .merge(options.fetch(:query, {}))
172
- query_string = Query.new(values).to_s
173
-
174
- # Generate a signature.
175
- signature = Request
176
- .new(
177
- options[:method].upcase,
178
- connection.data[:host],
179
- options[:path] || connection.data[:path],
180
- query_string
181
- )
148
+ # Build query string.
149
+ query_values = default_query_values.merge(options.fetch(:query, {}))
150
+ query_string = Query.new(query_values).to_s
151
+
152
+ # Generate signature.
153
+ signature = Signer
154
+ .new(options[:method], connection.data[:host], options[:path] || connection.data[:path], query_string)
182
155
  .sign_with(aws_secret_access_key)
183
156
 
184
157
  # Return options after appending an escaped signature to query.
185
158
  options.merge(query: "#{query_string}&Signature=#{Utils.escape(signature)}")
186
159
  end
187
160
 
161
+ def default_query_values
162
+ self.class.params.reduce({}) { |a, (k, v)|
163
+ a.update(k => (v.respond_to?(:call) ? instance_exec(&v) : v))
164
+ }
165
+ end
166
+
188
167
  module ClassMethods
189
- # Gets and optionally updates the default request parameters.
168
+ # Gets/updates default request parameters.
190
169
  def params(hsh = {})
191
170
  (@params ||= {}).update(hsh)
192
171
  end
@@ -1,3 +1,3 @@
1
1
  module Jeff
2
- VERSION = '0.7.4'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -0,0 +1,75 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require 'jeff'
4
+
5
+ Excon.defaults[:mock] = true
6
+
7
+ class TestJeff < Minitest::Test
8
+ def setup
9
+ @klass = Class.new { include Jeff }
10
+ end
11
+
12
+ def test_delegates_unset_aws_credential_to_env_vars
13
+ key = '123456'
14
+ client = @klass.new
15
+ %w(aws_access_key_id aws_secret_access_key).each do |attr|
16
+ ENV[attr.upcase] = key
17
+ assert_equal key, client.send(attr)
18
+ ENV[attr.upcase] = nil
19
+ refute_equal key, client.send(attr)
20
+ end
21
+ end
22
+
23
+ def test_has_required_request_query_parameters
24
+ %w(AWSAccessKeyId SignatureMethod SignatureVersion Timestamp).each do |key|
25
+ assert @klass.params.has_key?(key)
26
+ end
27
+ end
28
+
29
+ def test_configures_request_query_parameters
30
+ @klass.instance_eval do
31
+ params 'Foo' => 'bar'
32
+ end
33
+ assert @klass.params.has_key?('Foo')
34
+ end
35
+
36
+ def test_requires_signature
37
+ signature = Jeff::Signature.new(nil)
38
+ assert_raises(ArgumentError) { signature.sign('foo') }
39
+ end
40
+
41
+ def test_sorts_request_query_parameters_lexicographically
42
+ query = Jeff::Query.new('A10' => 1, 'A1' => 1)
43
+ assert_equal 'A1=1&A10=1', query.to_s
44
+ end
45
+
46
+ def test_sets_user_agent_header
47
+ client = @klass.new
48
+ client.aws_endpoint = 'http://example.com/'
49
+ refute_nil client.connection.data[:headers]['User-Agent']
50
+ end
51
+
52
+ Excon::HTTP_VERBS.each do |method|
53
+ define_method "test_makes_#{method}_request" do
54
+ Excon.stub({ }, { status: 200 })
55
+ client = @klass.new
56
+ client.aws_endpoint = 'http://example.com/'
57
+ client.aws_access_key_id = 'foo'
58
+ client.aws_secret_access_key = 'bar'
59
+ assert_equal 200, client.send(method).status
60
+ Excon.stubs.clear
61
+ end
62
+ end
63
+
64
+ def test_adds_contet_md5_request_header_if_given_a_request_body
65
+ Excon.stub({ }) do |params|
66
+ { body: params[:headers]['Content-MD5'] }
67
+ end
68
+ client = @klass.new
69
+ client.aws_endpoint = 'http://example.com/'
70
+ client.aws_access_key_id = 'foo'
71
+ client.aws_secret_access_key = 'bar'
72
+ refute_empty client.post(body: 'foo').body
73
+ Excon.stubs.clear
74
+ end
75
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jeff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hakan Ensari
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-06 00:00:00.000000000 Z
11
+ date: 2013-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: excon
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: 0.28.0
19
+ version: 0.29.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: 0.28.0
26
+ version: 0.29.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -68,7 +68,7 @@ files:
68
68
  - jeff.gemspec
69
69
  - lib/jeff.rb
70
70
  - lib/jeff/version.rb
71
- - spec/jeff_spec.rb
71
+ - test/test_jeff.rb
72
72
  homepage: https://github.com/papercavalier/jeff
73
73
  licenses: []
74
74
  metadata: {}
@@ -93,4 +93,4 @@ signing_key:
93
93
  specification_version: 4
94
94
  summary: An AWS client
95
95
  test_files:
96
- - spec/jeff_spec.rb
96
+ - test/test_jeff.rb
@@ -1,65 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'minitest/pride'
3
- require 'jeff'
4
-
5
- Excon.defaults[:mock] = true
6
-
7
- describe Jeff do
8
- before do
9
- @klass = Class.new do
10
- include Jeff
11
- end
12
- end
13
-
14
- it 'has the required request query parameters' do
15
- %w(AWSAccessKeyId SignatureMethod SignatureVersion Timestamp)
16
- .each { |key| assert @klass.params.has_key?(key) }
17
- end
18
-
19
- it 'configures the request query parameters' do
20
- @klass.instance_eval do
21
- params 'Foo' => 'bar'
22
- end
23
- assert @klass.params.has_key?('Foo')
24
- end
25
-
26
- it 'requires a signature' do
27
- sig = Jeff::Signature.new(nil)
28
- proc { sig.sign('foo') }.must_raise ArgumentError
29
- end
30
-
31
- it 'sorts the request query parameters of the client lexicographically' do
32
- query = Jeff::Query.new('A10' => 1, 'A1' => 1)
33
- query.to_s.must_equal('A1=1&A10=1')
34
- end
35
-
36
- it 'sets a User-Agent header for the client connection' do
37
- client = @klass.new
38
- client.aws_endpoint = 'http://example.com/'
39
- client.connection.data[:headers]['User-Agent'].wont_be_nil
40
- end
41
-
42
- Excon::HTTP_VERBS.each do |method|
43
- it "makes a #{method.upcase} request" do
44
- Excon.stub({ }, { status: 200 })
45
- client = @klass.new
46
- client.aws_endpoint = 'http://example.com/'
47
- client.aws_access_key_id = 'foo'
48
- client.aws_secret_access_key = 'bar'
49
- client.send(method).status.must_equal 200
50
- Excon.stubs.clear
51
- end
52
- end
53
-
54
- it 'adds a Content-MD5 request header if there is a request body' do
55
- Excon.stub({ }) do |params|
56
- { body: params[:headers]['Content-MD5'] }
57
- end
58
- client = @klass.new
59
- client.aws_endpoint = 'http://example.com/'
60
- client.aws_access_key_id = 'foo'
61
- client.aws_secret_access_key = 'bar'
62
- client.post(body: 'foo').body.wont_be_empty
63
- Excon.stubs.clear
64
- end
65
- end