jeff 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Guardfile +1 -1
- data/README.md +13 -31
- data/jeff.gemspec +2 -3
- data/lib/jeff.rb +33 -37
- data/lib/jeff/version.rb +1 -1
- data/spec/jeff_spec.rb +9 -21
- metadata +11 -30
- data/examples/amazon.yml.example +0 -3
- data/examples/debug.rb +0 -41
- data/lib/jeff/document.rb +0 -48
- data/lib/jeff/streamer.rb +0 -22
- data/spec/jeff/document_spec.rb +0 -49
- data/spec/jeff/streamer_spec.rb +0 -28
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# Jeff
|
2
2
|
|
3
3
|
**Jeff** is a light-weight module that mixes in client behaviour for
|
4
|
-
[Amazon Web Services (AWS)][aws]. It wraps [Excon][excon]
|
5
|
-
[
|
4
|
+
[Amazon Web Services (AWS)][aws]. It wraps [Excon][excon] and implements
|
5
|
+
[Signature Version 2][sign].
|
6
6
|
|
7
7
|
![jeff][jeff]
|
8
8
|
|
9
9
|
## Usage
|
10
10
|
|
11
|
-
Build a
|
11
|
+
Build a hypothetical client.
|
12
12
|
|
13
13
|
```ruby
|
14
14
|
class Client
|
@@ -20,15 +20,14 @@ Customise default headers and parameters.
|
|
20
20
|
|
21
21
|
```ruby
|
22
22
|
class Client
|
23
|
-
|
24
|
-
params 'Service' => 'SomeService',
|
23
|
+
params 'Service' => 'Service',
|
25
24
|
'Tag' => -> { tag }
|
26
25
|
|
27
26
|
attr_accessor :tag
|
28
27
|
end
|
29
28
|
```
|
30
29
|
|
31
|
-
Set
|
30
|
+
Set AWS endpoint and credentials.
|
32
31
|
|
33
32
|
```ruby
|
34
33
|
client = Client.new.tap do |config|
|
@@ -41,33 +40,16 @@ end
|
|
41
40
|
You should now be able to access the endpoint.
|
42
41
|
|
43
42
|
```ruby
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
puts res.status # => 200
|
48
|
-
puts res.body.root # => { 'Foo' => 'Bar' }
|
49
|
-
```
|
50
|
-
|
51
|
-
### Chunked Requests
|
52
|
-
|
53
|
-
You can upload large files performantly by passing a proc that delivers
|
54
|
-
chunks.
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
file = File.open 'data'
|
58
|
-
chunker = -> { file.read Excon::CHUNK_SIZE).to_s }
|
59
|
-
|
60
|
-
client.post query: {},
|
61
|
-
request_block: chunker
|
43
|
+
client.post query: { 'Foo' => 'Bar' },
|
44
|
+
body: 'data'
|
62
45
|
```
|
63
46
|
|
64
47
|
## Compatibility
|
65
48
|
|
66
|
-
**Jeff** is compatible with [
|
49
|
+
**Jeff** is compatible with [Ruby 1.9 flavours][travis].
|
67
50
|
|
68
|
-
[aws]:
|
69
|
-
[excon]:
|
70
|
-
[jeff]:
|
71
|
-
[
|
72
|
-
[
|
73
|
-
[travis]: http://travis-ci.org/#!/hakanensari/jeff
|
51
|
+
[aws]: http://aws.amazon.com/
|
52
|
+
[excon]: https://github.com/geemus/excon
|
53
|
+
[jeff]: http://f.cl.ly/items/0a3R3J0k1R2f423k1q2l/jeff.jpg
|
54
|
+
[sign]: http://docs.amazonwebservices.com/general/latest/gr/signature-version-2.html
|
55
|
+
[travis]: http://travis-ci.org/#!/hakanensari/jeff
|
data/jeff.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.email = ['hakan.ensari@papercavalier.com']
|
8
8
|
gem.description = %q{Minimum-viable Amazon Web Services (AWS) client}
|
9
9
|
gem.summary = %q{AWS client}
|
10
|
-
gem.homepage = 'https://github.com/
|
10
|
+
gem.homepage = 'https://github.com/papercavalier/jeff'
|
11
11
|
|
12
12
|
gem.files = `git ls-files`.split($\)
|
13
13
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -16,8 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ['lib']
|
17
17
|
gem.version = Jeff::VERSION
|
18
18
|
|
19
|
-
gem.add_dependency 'excon',
|
20
|
-
gem.add_dependency 'nokogiri', '~> 1.5'
|
19
|
+
gem.add_dependency 'excon', '~> 0.15.0'
|
21
20
|
gem.add_development_dependency 'guard-rspec'
|
22
21
|
gem.add_development_dependency 'rake'
|
23
22
|
gem.add_development_dependency 'rspec'
|
data/lib/jeff.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
require 'time'
|
2
|
-
|
3
1
|
require 'excon'
|
2
|
+
require 'time'
|
4
3
|
|
5
4
|
require 'jeff/secret'
|
6
|
-
require 'jeff/streamer'
|
7
5
|
require 'jeff/version'
|
8
6
|
|
7
|
+
# Jeff is a light-weight module that mixes in client behaviour for Amazon Web
|
8
|
+
# Services (AWS).
|
9
9
|
module Jeff
|
10
|
+
USER_AGENT = "Jeff/#{VERSION} (Language=Ruby; #{`hostname`.chomp})"
|
11
|
+
|
10
12
|
MissingEndpoint = Class.new ArgumentError
|
11
13
|
MissingKey = Class.new ArgumentError
|
12
14
|
MissingSecret = Class.new ArgumentError
|
@@ -15,6 +17,13 @@ module Jeff
|
|
15
17
|
|
16
18
|
def self.included(base)
|
17
19
|
base.extend ClassMethods
|
20
|
+
|
21
|
+
base.headers 'User-Agent' => USER_AGENT
|
22
|
+
|
23
|
+
base.params 'AWSAccessKeyId' => -> { key },
|
24
|
+
'SignatureVersion' => '2',
|
25
|
+
'SignatureMethod' => 'HmacSHA256',
|
26
|
+
'Timestamp' => -> { Time.now.utc.iso8601 }
|
18
27
|
end
|
19
28
|
|
20
29
|
# Internal: Builds a sorted query.
|
@@ -23,7 +32,7 @@ module Jeff
|
|
23
32
|
#
|
24
33
|
# Returns a query String.
|
25
34
|
def build_query(hsh)
|
26
|
-
|
35
|
+
params
|
27
36
|
.merge(hsh)
|
28
37
|
.sort
|
29
38
|
.map { |k, v| "#{k}=#{ escape v }" }
|
@@ -32,22 +41,10 @@ module Jeff
|
|
32
41
|
|
33
42
|
# Internal: Returns an Excon::Connection.
|
34
43
|
def connection
|
35
|
-
@connection ||= Excon.new endpoint, headers:
|
44
|
+
@connection ||= Excon.new endpoint, headers: headers,
|
36
45
|
idempotent: true
|
37
46
|
end
|
38
47
|
|
39
|
-
# Internal: Returns the Hash default request parameters.
|
40
|
-
def default_params
|
41
|
-
self.class.params.reduce({}) do |a, (k, v)|
|
42
|
-
a.update k => (v.is_a?(Proc) ? instance_exec(&v) : v)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Internal: Returns the Hash default headers.
|
47
|
-
def default_headers
|
48
|
-
self.class.headers
|
49
|
-
end
|
50
|
-
|
51
48
|
# Internal: Gets the String AWS endpoint.
|
52
49
|
#
|
53
50
|
# Raises a MissingEndpoint error if endpoint is missing.
|
@@ -58,6 +55,11 @@ module Jeff
|
|
58
55
|
# Sets the String AWS endpoint.
|
59
56
|
attr_writer :endpoint
|
60
57
|
|
58
|
+
# Internal: Returns the Hash default headers.
|
59
|
+
def headers
|
60
|
+
self.class.headers
|
61
|
+
end
|
62
|
+
|
61
63
|
# Internal: Gets the String AWS access key id.
|
62
64
|
#
|
63
65
|
# Raises a MissingKey error if key is missing.
|
@@ -68,6 +70,13 @@ module Jeff
|
|
68
70
|
# Sets the String AWS access key id.
|
69
71
|
attr_writer :key
|
70
72
|
|
73
|
+
# Internal: Returns the Hash default request parameters.
|
74
|
+
def params
|
75
|
+
self.class.params.reduce({}) do |a, (k, v)|
|
76
|
+
a.update k => (v.is_a?(Proc) ? instance_exec(&v) : v)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
71
80
|
# Internal: Gets the Jeff::Secret.
|
72
81
|
#
|
73
82
|
# Raises a MissingSecret error if secret is missing.
|
@@ -84,18 +93,13 @@ module Jeff
|
|
84
93
|
@secret = Secret.new key
|
85
94
|
end
|
86
95
|
|
87
|
-
# Generate HTTP request verb methods that sign queries and
|
88
|
-
#
|
89
|
-
Excon::HTTP_VERBS.
|
96
|
+
# Generate HTTP request verb methods that sign queries and return response
|
97
|
+
# bodies as IO objects.
|
98
|
+
Excon::HTTP_VERBS.each do |method|
|
90
99
|
eval <<-DEF
|
91
100
|
def #{method}(opts = {})
|
92
|
-
|
93
|
-
|
94
|
-
response_block: streamer
|
95
|
-
res = connection.request sign opts
|
96
|
-
res.body = streamer
|
97
|
-
|
98
|
-
res
|
101
|
+
opts.update method: :#{method}
|
102
|
+
connection.request sign opts
|
99
103
|
end
|
100
104
|
DEF
|
101
105
|
end
|
@@ -134,16 +138,13 @@ module Jeff
|
|
134
138
|
end
|
135
139
|
|
136
140
|
module ClassMethods
|
137
|
-
# Amazon recommends that libraries identify themselves via a User Agent.
|
138
|
-
USER_AGENT = "Jeff/#{VERSION} (Language=Ruby; #{`hostname`.chomp})"
|
139
|
-
|
140
141
|
# Gets/Updates the default headers.
|
141
142
|
#
|
142
143
|
# hsh - A Hash of headers.
|
143
144
|
#
|
144
145
|
# Returns the Hash headers.
|
145
146
|
def headers(hsh = nil)
|
146
|
-
@headers ||= {
|
147
|
+
@headers ||= {}
|
147
148
|
@headers.update hsh if hsh
|
148
149
|
|
149
150
|
@headers
|
@@ -155,12 +156,7 @@ module Jeff
|
|
155
156
|
#
|
156
157
|
# Returns the Hash parameters.
|
157
158
|
def params(hsh = nil)
|
158
|
-
@params ||= {
|
159
|
-
'AWSAccessKeyId' => -> { key },
|
160
|
-
'SignatureVersion' => '2',
|
161
|
-
'SignatureMethod' => 'HmacSHA256',
|
162
|
-
'Timestamp' => -> { Time.now.utc.iso8601 }
|
163
|
-
}
|
159
|
+
@params ||= {}
|
164
160
|
@params.update hsh if hsh
|
165
161
|
|
166
162
|
@params
|
data/lib/jeff/version.rb
CHANGED
data/spec/jeff_spec.rb
CHANGED
@@ -61,8 +61,8 @@ describe Jeff do
|
|
61
61
|
client.key = 'key'
|
62
62
|
end
|
63
63
|
|
64
|
-
describe '#
|
65
|
-
subject { client.
|
64
|
+
describe '#params' do
|
65
|
+
subject { client.params }
|
66
66
|
|
67
67
|
it 'should include the key' do
|
68
68
|
subject['AWSAccessKeyId'].should eql client.key
|
@@ -116,11 +116,13 @@ describe Jeff do
|
|
116
116
|
|
117
117
|
Excon::HTTP_VERBS.each do |method|
|
118
118
|
describe "##{method}" do
|
119
|
-
subject
|
119
|
+
subject do
|
120
|
+
client.send(method, mock: true).body
|
121
|
+
end
|
120
122
|
|
121
123
|
before do
|
122
124
|
Excon.stub({ method: method.to_sym }) do |params|
|
123
|
-
{ body:
|
125
|
+
{ body: method, status: 200 }
|
124
126
|
end
|
125
127
|
end
|
126
128
|
|
@@ -132,12 +134,12 @@ describe Jeff do
|
|
132
134
|
end
|
133
135
|
end
|
134
136
|
|
135
|
-
context 'given a
|
137
|
+
context 'given a temporary failure' do
|
136
138
|
before do
|
137
139
|
has_run = false
|
138
140
|
Excon.stub({ method: :get }) do |params|
|
139
141
|
if has_run
|
140
|
-
{ status: 200 }
|
142
|
+
{ body: 'ok', status: 200 }
|
141
143
|
else
|
142
144
|
has_run = true
|
143
145
|
raise Excon::Errors::SocketError.new Exception.new 'Mock Error'
|
@@ -148,21 +150,7 @@ describe Jeff do
|
|
148
150
|
after { Excon.stubs.clear }
|
149
151
|
|
150
152
|
it 'should retry' do
|
151
|
-
client.get(mock: true).
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
context 'given a malformed XML' do
|
156
|
-
before do
|
157
|
-
Excon.stub({ method: :get }) do |params|
|
158
|
-
{ body: "\n<?xml version=\"1.0\" ?><foo/>" }
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
after { Excon.stubs.clear }
|
163
|
-
|
164
|
-
it 'should not raise an error' do
|
165
|
-
client.get mock: true
|
153
|
+
client.get(mock: true).body.should eq 'ok'
|
166
154
|
end
|
167
155
|
end
|
168
156
|
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.4.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-30 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.
|
21
|
+
version: 0.15.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,23 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 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'
|
29
|
+
version: 0.15.0
|
46
30
|
- !ruby/object:Gem::Dependency
|
47
31
|
name: guard-rspec
|
48
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,20 +89,14 @@ files:
|
|
105
89
|
- LICENSE
|
106
90
|
- README.md
|
107
91
|
- Rakefile
|
108
|
-
- examples/amazon.yml.example
|
109
|
-
- examples/debug.rb
|
110
92
|
- jeff.gemspec
|
111
93
|
- lib/jeff.rb
|
112
|
-
- lib/jeff/document.rb
|
113
94
|
- lib/jeff/secret.rb
|
114
|
-
- lib/jeff/streamer.rb
|
115
95
|
- lib/jeff/version.rb
|
116
|
-
- spec/jeff/document_spec.rb
|
117
96
|
- spec/jeff/secret_spec.rb
|
118
|
-
- spec/jeff/streamer_spec.rb
|
119
97
|
- spec/jeff_spec.rb
|
120
98
|
- spec/spec_helper.rb
|
121
|
-
homepage: https://github.com/
|
99
|
+
homepage: https://github.com/papercavalier/jeff
|
122
100
|
licenses: []
|
123
101
|
post_install_message:
|
124
102
|
rdoc_options: []
|
@@ -130,12 +108,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
108
|
- - ! '>='
|
131
109
|
- !ruby/object:Gem::Version
|
132
110
|
version: '0'
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
hash: -177229951203254918
|
133
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
115
|
none: false
|
135
116
|
requirements:
|
136
117
|
- - ! '>='
|
137
118
|
- !ruby/object:Gem::Version
|
138
119
|
version: '0'
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
hash: -177229951203254918
|
139
123
|
requirements: []
|
140
124
|
rubyforge_project:
|
141
125
|
rubygems_version: 1.8.23
|
@@ -143,9 +127,6 @@ signing_key:
|
|
143
127
|
specification_version: 3
|
144
128
|
summary: AWS client
|
145
129
|
test_files:
|
146
|
-
- spec/jeff/document_spec.rb
|
147
130
|
- spec/jeff/secret_spec.rb
|
148
|
-
- spec/jeff/streamer_spec.rb
|
149
131
|
- spec/jeff_spec.rb
|
150
132
|
- spec/spec_helper.rb
|
151
|
-
has_rdoc:
|
data/examples/amazon.yml.example
DELETED
data/examples/debug.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
$:.unshift File.expand_path '../../lib', __FILE__
|
2
|
-
|
3
|
-
require 'yaml'
|
4
|
-
|
5
|
-
require 'jeff'
|
6
|
-
require 'pry'
|
7
|
-
require 'pry-doc'
|
8
|
-
|
9
|
-
class Client
|
10
|
-
include Jeff
|
11
|
-
|
12
|
-
params 'AssociateTag' => -> { tag },
|
13
|
-
'Service' => 'AWSECommerceService',
|
14
|
-
'Version' => '2011-08-01'
|
15
|
-
|
16
|
-
attr_accessor :tag
|
17
|
-
|
18
|
-
def initialize
|
19
|
-
self.key = config['key']
|
20
|
-
self.secret = config['secret']
|
21
|
-
self.tag = config['tag']
|
22
|
-
self.endpoint = 'http://ecs.amazonaws.com/onca/xml'
|
23
|
-
end
|
24
|
-
|
25
|
-
def find(asins)
|
26
|
-
params = {
|
27
|
-
'Operation' => 'ItemLookup',
|
28
|
-
'ItemId' => Array(asins).join(',')
|
29
|
-
}
|
30
|
-
|
31
|
-
get query: params
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def config
|
37
|
-
@config ||= YAML.load_file File.expand_path '../amazon.yml', __FILE__
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
binding.pry
|
data/lib/jeff/document.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'nokogiri'
|
2
|
-
|
3
|
-
module Jeff
|
4
|
-
class Document < Nokogiri::XML::SAX::Document
|
5
|
-
def characters(val)
|
6
|
-
val.strip!
|
7
|
-
unless val.empty?
|
8
|
-
(node['__content__'] ||= '') << val
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def end_element(key)
|
13
|
-
child = @stack.pop
|
14
|
-
|
15
|
-
if child.keys == ['__content__']
|
16
|
-
child = child['__content__']
|
17
|
-
end
|
18
|
-
|
19
|
-
case node[key]
|
20
|
-
when Array
|
21
|
-
node[key] << child
|
22
|
-
when Hash, String
|
23
|
-
node[key] = [node[key], child]
|
24
|
-
else
|
25
|
-
node[key] = child
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def start_element(key, attrs = [])
|
30
|
-
@stack << {}
|
31
|
-
attrs.each { |attr| node.store *attr }
|
32
|
-
end
|
33
|
-
|
34
|
-
def start_document
|
35
|
-
@stack = [{}]
|
36
|
-
end
|
37
|
-
|
38
|
-
def root
|
39
|
-
@stack.first
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def node
|
45
|
-
@stack.last
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/jeff/streamer.rb
DELETED
@@ -1,22 +0,0 @@
|
|
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/spec/jeff/document_spec.rb
DELETED
@@ -1,49 +0,0 @@
|
|
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
|
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
|
-
|
44
|
-
it 'should ignore space between tags' do
|
45
|
-
should_not have_key '__content__'
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/spec/jeff/streamer_spec.rb
DELETED
@@ -1,28 +0,0 @@
|
|
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
|