jeff 0.7.4 → 1.0.0

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