jeff 0.2.5 → 0.3.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.
- data/README.md +19 -48
- data/jeff.gemspec +2 -1
- data/lib/jeff.rb +9 -3
- data/lib/jeff/document.rb +45 -0
- data/lib/jeff/streamer.rb +22 -0
- data/lib/jeff/version.rb +1 -1
- data/spec/jeff/document_spec.rb +45 -0
- data/spec/jeff/streamer_spec.rb +28 -0
- data/spec/jeff_spec.rb +3 -7
- metadata +26 -4
data/README.md
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
# Jeff
|
2
2
|
|
3
|
-
**Jeff** is a light-weight module that mixes in client behaviour for
|
4
|
-
Web Services (AWS)][aws]. It wraps
|
5
|
-
implements [Signature Version
|
3
|
+
**Jeff** is a light-weight module that mixes in client behaviour for
|
4
|
+
[Amazon Web Services (AWS)][aws]. It wraps [Excon][excon], parses
|
5
|
+
responses with [Nokogiri][nokogiri], and implements [Signature Version
|
6
|
+
2][sign].
|
6
7
|
|
7
8
|
![jeff][jeff]
|
8
9
|
|
9
10
|
## Usage
|
10
11
|
|
11
|
-
|
12
|
+
Build a a hypothetical client.
|
12
13
|
|
13
14
|
```ruby
|
14
15
|
class Client
|
@@ -41,13 +42,17 @@ end
|
|
41
42
|
You should now be able to access the endpoint.
|
42
43
|
|
43
44
|
```ruby
|
44
|
-
client.post query: {},
|
45
|
-
|
45
|
+
res = client.post query: {},
|
46
|
+
body: 'data'
|
47
|
+
|
48
|
+
puts res.status # => 200
|
49
|
+
puts res.body.root # => { 'Foo' => 'Bar' }
|
46
50
|
```
|
47
51
|
|
48
52
|
### Chunked Requests
|
49
53
|
|
50
|
-
You can upload large files performantly by passing a proc that delivers
|
54
|
+
You can upload large files performantly by passing a proc that delivers
|
55
|
+
chunks.
|
51
56
|
|
52
57
|
```ruby
|
53
58
|
file = File.open 'data'
|
@@ -57,47 +62,13 @@ client.post query: {},
|
|
57
62
|
request_block: chunker
|
58
63
|
```
|
59
64
|
|
60
|
-
### Streaming Responses
|
61
|
-
|
62
|
-
Similarly, you can download and parse large files performantly by passing a
|
63
|
-
block that will receive chunks.
|
64
|
-
|
65
|
-
```ruby
|
66
|
-
streamer = ->(chunk, remaining, total) { puts chunk }
|
67
|
-
|
68
|
-
client.get query: {},
|
69
|
-
response_block: streamer
|
70
|
-
```
|
71
|
-
|
72
|
-
### Instrumentation
|
73
|
-
|
74
|
-
Requests can be instrumented.
|
75
|
-
|
76
|
-
```ruby
|
77
|
-
class Logger
|
78
|
-
def self.instrument(name, params = {})
|
79
|
-
$stderr.puts name, params
|
80
|
-
yield if block_given?
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
client.get query: {}, instrumentor: Logger
|
85
|
-
```
|
86
|
-
|
87
|
-
### Miscellaneous
|
88
|
-
|
89
|
-
* HTTP connections are persistent.
|
90
|
-
* By default, Jeff will retry failed requests 4 times.
|
91
|
-
|
92
|
-
For more detailed configuration options, read [excon][excon].
|
93
|
-
|
94
65
|
## Compatibility
|
95
66
|
|
96
|
-
**Jeff** is
|
97
|
-
Rubinius in 1.9 mode][travis].
|
67
|
+
**Jeff** is compatible with [all Ruby 1.9 flavours][travis].
|
98
68
|
|
99
|
-
[aws]:
|
100
|
-
[excon]:
|
101
|
-
[
|
102
|
-
[
|
103
|
-
[
|
69
|
+
[aws]: http://aws.amazon.com/
|
70
|
+
[excon]: https://github.com/geemus/excon
|
71
|
+
[jeff]: http://f.cl.ly/items/0a3R3J0k1R2f423k1q2l/jeff.jpg
|
72
|
+
[nokogiri]: http://nokogiri.org/
|
73
|
+
[sign]: http://docs.amazonwebservices.com/general/latest/gr/signature-version-2.html
|
74
|
+
[travis]: http://travis-ci.org/#!/hakanensari/jeff
|
data/jeff.gemspec
CHANGED
@@ -16,7 +16,8 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ['lib']
|
17
17
|
gem.version = Jeff::VERSION
|
18
18
|
|
19
|
-
gem.add_dependency 'excon',
|
19
|
+
gem.add_dependency 'excon', '~> 0.14.0'
|
20
|
+
gem.add_dependency 'nokogiri', '~> 1.5'
|
20
21
|
gem.add_development_dependency 'guard-rspec'
|
21
22
|
gem.add_development_dependency 'rake'
|
22
23
|
gem.add_development_dependency 'rspec'
|
data/lib/jeff.rb
CHANGED
@@ -3,6 +3,7 @@ require 'time'
|
|
3
3
|
require 'excon'
|
4
4
|
|
5
5
|
require 'jeff/secret'
|
6
|
+
require 'jeff/streamer'
|
6
7
|
require 'jeff/version'
|
7
8
|
|
8
9
|
module Jeff
|
@@ -87,9 +88,14 @@ module Jeff
|
|
87
88
|
# request to Excon.
|
88
89
|
Excon::HTTP_VERBS. each do |method|
|
89
90
|
eval <<-DEF
|
90
|
-
def #{method}(opts = {}
|
91
|
-
|
92
|
-
|
91
|
+
def #{method}(opts = {})
|
92
|
+
streamer = Streamer.new
|
93
|
+
opts.update method: :#{method},
|
94
|
+
response_block: streamer
|
95
|
+
res = connection.request sign opts
|
96
|
+
res.body = streamer
|
97
|
+
|
98
|
+
res
|
93
99
|
end
|
94
100
|
DEF
|
95
101
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Jeff
|
4
|
+
class Document < Nokogiri::XML::SAX::Document
|
5
|
+
def characters(val)
|
6
|
+
(node['__content__'] ||= '') << val
|
7
|
+
end
|
8
|
+
|
9
|
+
def end_element(key)
|
10
|
+
child = @stack.pop
|
11
|
+
|
12
|
+
if child.keys == ['__content__']
|
13
|
+
child = child['__content__']
|
14
|
+
end
|
15
|
+
|
16
|
+
case node[key]
|
17
|
+
when Array
|
18
|
+
node[key] << child
|
19
|
+
when Hash, String
|
20
|
+
node[key] = [node[key], child]
|
21
|
+
else
|
22
|
+
node[key] = child
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def start_element(key, attrs = [])
|
27
|
+
@stack << {}
|
28
|
+
attrs.each { |attr| node.store *attr }
|
29
|
+
end
|
30
|
+
|
31
|
+
def start_document
|
32
|
+
@stack = [{}]
|
33
|
+
end
|
34
|
+
|
35
|
+
def root
|
36
|
+
@stack.first
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def node
|
42
|
+
@stack.last
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'jeff/document'
|
2
|
+
|
3
|
+
module Jeff
|
4
|
+
class Streamer
|
5
|
+
def initialize
|
6
|
+
@parser = Nokogiri::XML::SAX::PushParser.new Document.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(chunk, remaining_bytes, total_bytes)
|
10
|
+
@parser << chunk.sub(/\n/, '')
|
11
|
+
@parser.finish if remaining_bytes == 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def document
|
15
|
+
@parser.document
|
16
|
+
end
|
17
|
+
|
18
|
+
def root
|
19
|
+
document.root
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/jeff/version.rb
CHANGED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Jeff
|
4
|
+
describe Document do
|
5
|
+
let(:io) do
|
6
|
+
StringIO.new %{
|
7
|
+
<?xml version=\"1.0\" ?>
|
8
|
+
<ItemAttributes>
|
9
|
+
<Title>Anti-Oedipus</Title>
|
10
|
+
<Author>Gilles Deleuze</Author>
|
11
|
+
<Author>Felix Guattari</Author>
|
12
|
+
<Creator Role="Translator">Robert Hurley</Creator>
|
13
|
+
</ItemAttributes>
|
14
|
+
}.strip.gsub />\s+</, '><'
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:doc) { described_class.new }
|
18
|
+
let(:parser) { Nokogiri::XML::SAX::Parser.new doc }
|
19
|
+
|
20
|
+
before do
|
21
|
+
io.rewind
|
22
|
+
parser.parse io
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#root' do
|
26
|
+
subject { doc.root['ItemAttributes'] }
|
27
|
+
|
28
|
+
it { should be_a Hash }
|
29
|
+
|
30
|
+
it 'should handle only children' do
|
31
|
+
subject['Title'].should eql 'Anti-Oedipus'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should hande arrays' do
|
35
|
+
subject['Author'].should be_an Array
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should handle attributes' do
|
39
|
+
creator = subject['Creator']
|
40
|
+
creator['Role'].should eql 'Translator'
|
41
|
+
creator['__content__'].should eql 'Robert Hurley'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Jeff
|
4
|
+
describe Streamer do
|
5
|
+
let(:streamer) { Streamer.new }
|
6
|
+
|
7
|
+
let(:xml) do
|
8
|
+
%{
|
9
|
+
<?xml version="1.0" ?>
|
10
|
+
<foo>
|
11
|
+
<bar>1</bar>
|
12
|
+
</foo>
|
13
|
+
}.strip.gsub />\s+</, '><'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should parse a stream' do
|
17
|
+
bytes_sent = 0
|
18
|
+
total_bytes = xml.size
|
19
|
+
|
20
|
+
xml.scan(/.{1,8}/m).each do |chunk|
|
21
|
+
bytes_sent += chunk.size
|
22
|
+
streamer.call chunk, total_bytes - bytes_sent, total_bytes
|
23
|
+
end
|
24
|
+
|
25
|
+
streamer.root.should have_key 'foo'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/jeff_spec.rb
CHANGED
@@ -120,22 +120,18 @@ describe Jeff do
|
|
120
120
|
|
121
121
|
Excon::HTTP_VERBS.each do |method|
|
122
122
|
describe "##{method}" do
|
123
|
-
subject { client.send(method, mock: true).body }
|
123
|
+
subject { client.send(method, mock: true).body.root['request'] }
|
124
124
|
|
125
125
|
before do
|
126
126
|
Excon.stub({ method: method.to_sym }) do |params|
|
127
|
-
{ body: params }
|
127
|
+
{ body: "<request>#{params[:method]}</request>" }
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
131
|
after { Excon.stubs.clear }
|
132
132
|
|
133
133
|
it "should make a #{method.upcase} request" do
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'should append a signature' do
|
138
|
-
subject[:query].should match /.+&Signature=[^&]+$/
|
134
|
+
should eql method
|
139
135
|
end
|
140
136
|
end
|
141
137
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jeff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: excon
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.14.
|
21
|
+
version: 0.14.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,23 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.14.
|
29
|
+
version: 0.14.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: nokogiri
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.5'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.5'
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: guard-rspec
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -93,9 +109,13 @@ files:
|
|
93
109
|
- examples/debug.rb
|
94
110
|
- jeff.gemspec
|
95
111
|
- lib/jeff.rb
|
112
|
+
- lib/jeff/document.rb
|
96
113
|
- lib/jeff/secret.rb
|
114
|
+
- lib/jeff/streamer.rb
|
97
115
|
- lib/jeff/version.rb
|
116
|
+
- spec/jeff/document_spec.rb
|
98
117
|
- spec/jeff/secret_spec.rb
|
118
|
+
- spec/jeff/streamer_spec.rb
|
99
119
|
- spec/jeff_spec.rb
|
100
120
|
- spec/spec_helper.rb
|
101
121
|
homepage: https://github.com/hakanensari/jeff
|
@@ -123,7 +143,9 @@ signing_key:
|
|
123
143
|
specification_version: 3
|
124
144
|
summary: AWS client
|
125
145
|
test_files:
|
146
|
+
- spec/jeff/document_spec.rb
|
126
147
|
- spec/jeff/secret_spec.rb
|
148
|
+
- spec/jeff/streamer_spec.rb
|
127
149
|
- spec/jeff_spec.rb
|
128
150
|
- spec/spec_helper.rb
|
129
151
|
has_rdoc:
|