acfs 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/acfs.rb +1 -0
- data/lib/acfs/adapter/typhoeus.rb +10 -1
- data/lib/acfs/errors.rb +17 -1
- data/lib/acfs/middleware/logger.rb +25 -0
- data/lib/acfs/model/attributes.rb +1 -1
- data/lib/acfs/model/query_methods.rb +16 -3
- data/lib/acfs/response.rb +13 -6
- data/lib/acfs/response/status.rb +33 -0
- data/lib/acfs/version.rb +1 -1
- data/spec/acfs/middleware/json_decoder_spec.rb +1 -1
- data/spec/acfs/middleware/msgpack_decoder_spec.rb +1 -1
- data/spec/acfs/model/query_methods_spec.rb +81 -29
- data/spec/acfs/response/formats_spec.rb +1 -1
- data/spec/acfs/response/status_spec.rb +69 -0
- data/spec/spec_helper.rb +4 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69903ec6f49f6fb0d192c60aa302ddc9ba16f940
|
4
|
+
data.tar.gz: bb15a106632b4d5f3681d829bee1b4e547d61039
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4acac2d53426422d60c59502fde95f17893fabced18c70d9236c3473c1250be5ac492f8ff6271db2073340af96725dee05748a6a5de43d9f92fa330fb3ef060d
|
7
|
+
data.tar.gz: d4aefee793caaad3a20afcc1c703b07882492805ad80ce70efb9fafda71700f425ed2246e8964e03c839cfd1f9ebe6da255872316afceabe15bea413161ad518
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ level and automatic request queuing and parallel processing. See Usage for more.
|
|
13
13
|
|
14
14
|
Add this line to your application's Gemfile:
|
15
15
|
|
16
|
-
gem 'acfs', '~> 0.
|
16
|
+
gem 'acfs', '~> 0.11.0'
|
17
17
|
|
18
18
|
**Note:** Acfs is under development. I'll try to avoid changes to the public
|
19
19
|
API but internal APIs may change quite often.
|
data/lib/acfs.rb
CHANGED
@@ -21,6 +21,12 @@ module Acfs
|
|
21
21
|
hydra.queue convert_request(req)
|
22
22
|
end
|
23
23
|
|
24
|
+
# Remove all requests from queue.
|
25
|
+
#
|
26
|
+
def clear
|
27
|
+
hydra.abort
|
28
|
+
end
|
29
|
+
|
24
30
|
protected
|
25
31
|
def hydra
|
26
32
|
@hydra ||= ::Typhoeus::Hydra.new
|
@@ -41,7 +47,10 @@ module Acfs
|
|
41
47
|
end
|
42
48
|
|
43
49
|
def convert_response(request, response)
|
44
|
-
Acfs::Response.new
|
50
|
+
Acfs::Response.new request,
|
51
|
+
status: response.code,
|
52
|
+
headers: response.headers,
|
53
|
+
body: response.body
|
45
54
|
end
|
46
55
|
end
|
47
56
|
end
|
data/lib/acfs/errors.rb
CHANGED
@@ -2,6 +2,22 @@ module Acfs
|
|
2
2
|
|
3
3
|
# Acfs base error.
|
4
4
|
#
|
5
|
-
class Error < StandardError
|
5
|
+
class Error < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
# Response error containing the responsible response object.
|
9
|
+
#
|
10
|
+
class ErroneousResponse < Error
|
11
|
+
attr_accessor :response
|
12
|
+
|
13
|
+
def initialize(response)
|
14
|
+
self.response = response
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Resource not found error raised on a 404 response
|
19
|
+
#
|
20
|
+
class ResourceNotFound < ErroneousResponse
|
21
|
+
end
|
6
22
|
|
7
23
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Acfs
|
4
|
+
module Middleware
|
5
|
+
|
6
|
+
# Log requests and responses.
|
7
|
+
#
|
8
|
+
class Logger < Base
|
9
|
+
|
10
|
+
def initialize(app, options = {})
|
11
|
+
super
|
12
|
+
@logger = options[:logger] if options[:logger]
|
13
|
+
end
|
14
|
+
|
15
|
+
def response(res, nxt)
|
16
|
+
logger.info "[ACFS] #{res.request.method.to_s.upcase} #{res.request.url} -> #{res.status}"
|
17
|
+
nxt.call res
|
18
|
+
end
|
19
|
+
|
20
|
+
def logger
|
21
|
+
@logger ||= ::Logger.new STDOUT
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -73,7 +73,7 @@ module Acfs::Model
|
|
73
73
|
#
|
74
74
|
def write_attribute(name, value, opts = {})
|
75
75
|
if (type = self.class.attribute_types[name.to_sym]).nil?
|
76
|
-
raise "Unknown attribute
|
76
|
+
raise "Unknown attribute `#{name}`."
|
77
77
|
end
|
78
78
|
|
79
79
|
write_raw_attribute name, value.nil? ? nil : type.cast(value), opts
|
@@ -58,9 +58,13 @@ module Acfs::Model
|
|
58
58
|
model = self.new
|
59
59
|
|
60
60
|
request = Acfs::Request.new url(id.to_s) do |response|
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
if response.success?
|
62
|
+
model.attributes = response.data
|
63
|
+
model.loaded!
|
64
|
+
block.call model unless block.nil?
|
65
|
+
else
|
66
|
+
raise_error_for response
|
67
|
+
end
|
64
68
|
end
|
65
69
|
service.queue request
|
66
70
|
|
@@ -81,6 +85,15 @@ module Acfs::Model
|
|
81
85
|
end
|
82
86
|
end
|
83
87
|
end
|
88
|
+
|
89
|
+
def raise_error_for(response)
|
90
|
+
case response.code
|
91
|
+
when 404
|
92
|
+
raise ::Acfs::ResourceNotFound.new response
|
93
|
+
else
|
94
|
+
raise ::Acfs::ErroneousResponse.new response
|
95
|
+
end
|
96
|
+
end
|
84
97
|
end
|
85
98
|
end
|
86
99
|
end
|
data/lib/acfs/response.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'acfs/response/formats'
|
2
|
+
require 'acfs/response/status'
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
2
4
|
|
3
5
|
module Acfs
|
4
6
|
|
@@ -7,15 +9,20 @@ module Acfs
|
|
7
9
|
#
|
8
10
|
class Response
|
9
11
|
attr_accessor :data
|
10
|
-
attr_reader :
|
12
|
+
attr_reader :headers, :body
|
11
13
|
|
12
14
|
include Response::Formats
|
15
|
+
include Response::Status
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
#delegate :status, :status_message, :success?, :modified?, :timed_out?,
|
18
|
+
# :response_body, :response_headers, :response_code, :headers,
|
19
|
+
# to: :response
|
20
|
+
|
21
|
+
def initialize(request, data = {})
|
22
|
+
@request = request
|
23
|
+
@status = data[:status]
|
24
|
+
@headers = data[:headers]
|
25
|
+
@body = data[:body]
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Acfs
|
2
|
+
class Response
|
3
|
+
|
4
|
+
# Method to fetch information about response status.
|
5
|
+
#
|
6
|
+
module Status
|
7
|
+
|
8
|
+
# Return response status code. Will return zero if
|
9
|
+
# request was not executed or failed on client side.
|
10
|
+
#
|
11
|
+
def status_code
|
12
|
+
return @status.to_i if defined? :@status
|
13
|
+
#return response.response_code unless response.nil?
|
14
|
+
#0
|
15
|
+
end
|
16
|
+
alias :code :status_code
|
17
|
+
|
18
|
+
# Return true if response was successful indicated by
|
19
|
+
# response status code.
|
20
|
+
#
|
21
|
+
def success?
|
22
|
+
code >= 200 && code < 300
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return true unless response status code indicates that
|
26
|
+
# resource was not modified according to send precondition headers.
|
27
|
+
#
|
28
|
+
def modified?
|
29
|
+
code != 304
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/acfs/version.rb
CHANGED
@@ -5,7 +5,7 @@ describe Acfs::Middleware::JsonDecoder do
|
|
5
5
|
let(:body) { data.to_param }
|
6
6
|
let(:headers) { {} }
|
7
7
|
let(:request) { Acfs::Request.new "fubar" }
|
8
|
-
let(:response) { Acfs::Response.new request, 200, headers, body }
|
8
|
+
let(:response) { Acfs::Response.new request, status: 200, headers: headers, body: body }
|
9
9
|
let(:decoder) { Acfs::Middleware::JsonDecoder.new lambda { |req| req } }
|
10
10
|
|
11
11
|
before do
|
@@ -5,7 +5,7 @@ describe Acfs::Middleware::MessagePackDecoder do
|
|
5
5
|
let(:body) { data.to_param }
|
6
6
|
let(:headers) { {} }
|
7
7
|
let(:request) { Acfs::Request.new "fubar" }
|
8
|
-
let(:response) { Acfs::Response.new request, 200, headers, body }
|
8
|
+
let(:response) { Acfs::Response.new request, status: 200, headers: headers, body: body }
|
9
9
|
let(:decoder) { Acfs::Middleware::MessagePackDecoder.new lambda { |req| req } }
|
10
10
|
|
11
11
|
before do
|
@@ -5,28 +5,63 @@ describe Acfs::Model::QueryMethods do
|
|
5
5
|
|
6
6
|
describe '.find' do
|
7
7
|
context 'with single id' do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
context 'with successful response' do
|
9
|
+
before do
|
10
|
+
stub_request(:get, 'http://users.example.org/users/1').to_return(
|
11
|
+
body: MessagePack.dump({ id: 1, name: 'Anon', age: 12 }),
|
12
|
+
headers: {'Content-Type' => 'application/x-msgpack'})
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should load a single remote resource' do
|
16
|
+
user = model.find 1
|
17
|
+
Acfs.run
|
18
|
+
|
19
|
+
expect(user.attributes).to be == { id: 1, name: 'Anon', age: 12 }.stringify_keys
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should invoke callback after model is loaded' do
|
23
|
+
proc = Proc.new { }
|
24
|
+
proc.should_receive(:call) do |user|
|
25
|
+
expect(user).to be === @user
|
26
|
+
expect(user).to be_loaded
|
27
|
+
end
|
28
|
+
|
29
|
+
@user = model.find 1, &proc
|
30
|
+
Acfs.run
|
31
|
+
end
|
12
32
|
end
|
13
33
|
|
14
|
-
|
15
|
-
|
16
|
-
|
34
|
+
context 'with 404 response' do
|
35
|
+
before do
|
36
|
+
stub_request(:get, 'http://users.example.org/users/1').to_return(
|
37
|
+
status: 404,
|
38
|
+
body: MessagePack.dump({ error: 'not found' }),
|
39
|
+
headers: {'Content-Type' => 'application/x-msgpack'})
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should raise a NotFound error' do
|
43
|
+
@user = model.find 1
|
17
44
|
|
18
|
-
|
45
|
+
expect { Acfs.run }.to raise_error(Acfs::ResourceNotFound)
|
46
|
+
|
47
|
+
expect(@user).to_not be_loaded
|
48
|
+
end
|
19
49
|
end
|
20
50
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
51
|
+
context 'with 500 response' do
|
52
|
+
before do
|
53
|
+
stub_request(:get, 'http://users.example.org/users/1').to_return(
|
54
|
+
status: 500,
|
55
|
+
headers: {'Content-Type' => 'text/plain'})
|
26
56
|
end
|
27
57
|
|
28
|
-
|
29
|
-
|
58
|
+
it 'should raise a response error' do
|
59
|
+
@user = model.find 1
|
60
|
+
|
61
|
+
expect { Acfs.run }.to raise_error(Acfs::ErroneousResponse)
|
62
|
+
|
63
|
+
expect(@user).to_not be_loaded
|
64
|
+
end
|
30
65
|
end
|
31
66
|
end
|
32
67
|
|
@@ -40,25 +75,42 @@ describe Acfs::Model::QueryMethods do
|
|
40
75
|
headers: {'Content-Type' => 'application/x-msgpack'})
|
41
76
|
end
|
42
77
|
|
43
|
-
|
44
|
-
|
45
|
-
|
78
|
+
context 'with successful response' do
|
79
|
+
it 'should load a multiple remote resources' do
|
80
|
+
users = model.find 1, 2
|
81
|
+
Acfs.run
|
46
82
|
|
47
|
-
|
48
|
-
|
49
|
-
|
83
|
+
expect(users.size).to be == 2
|
84
|
+
expect(users[0].attributes).to be == { id: 1, name: 'Anon', age: 12 }.stringify_keys
|
85
|
+
expect(users[1].attributes).to be == { id: 2, name: 'Johnny', age: 42 }.stringify_keys
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should invoke callback after all models are loaded' do
|
89
|
+
proc = Proc.new { }
|
90
|
+
proc.should_receive(:call) do |users|
|
91
|
+
expect(users).to be === @users
|
92
|
+
expect(users.size).to be == 2
|
93
|
+
expect(users).to be_loaded
|
94
|
+
end
|
95
|
+
|
96
|
+
@users = model.find 1, 2, &proc
|
97
|
+
Acfs.run
|
98
|
+
end
|
50
99
|
end
|
51
100
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
101
|
+
context 'with one 404 response' do
|
102
|
+
before do
|
103
|
+
stub_request(:get, 'http://users.example.org/users/1').to_return(
|
104
|
+
status: 404,
|
105
|
+
body: MessagePack.dump({ error: 'not found' }),
|
106
|
+
headers: {'Content-Type' => 'application/x-msgpack'})
|
58
107
|
end
|
59
108
|
|
60
|
-
|
61
|
-
|
109
|
+
it 'should raise resource not found error' do
|
110
|
+
model.find 1, 2
|
111
|
+
|
112
|
+
expect { Acfs.run }.to raise_error(Acfs::ResourceNotFound)
|
113
|
+
end
|
62
114
|
end
|
63
115
|
end
|
64
116
|
end
|
@@ -6,7 +6,7 @@ describe Acfs::Response::Formats do
|
|
6
6
|
let(:headers) { { 'Content-Type' => mime_type } }
|
7
7
|
let(:request) { Acfs::Request.new 'fubar' }
|
8
8
|
let(:body) { nil }
|
9
|
-
let(:response) { Acfs::Response.new request, status, headers, body }
|
9
|
+
let(:response) { Acfs::Response.new request, status: status, headers: headers, body: body }
|
10
10
|
|
11
11
|
context 'without Content-Type header' do
|
12
12
|
let(:headers) { {} }
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Acfs::Response::Status do
|
4
|
+
let(:status) { 200 }
|
5
|
+
let(:mime_type) { 'application/unknown' }
|
6
|
+
let(:headers) { { 'Content-Type' => mime_type } }
|
7
|
+
let(:request) { Acfs::Request.new 'fubar' }
|
8
|
+
let(:body) { nil }
|
9
|
+
let(:response) { Acfs::Response.new request, status: status, headers: headers, body: body }
|
10
|
+
|
11
|
+
describe '#status_code alias #code' do
|
12
|
+
context 'when given' do
|
13
|
+
let(:status) { 200 }
|
14
|
+
|
15
|
+
it 'should return status code' do
|
16
|
+
expect(response.code).to be == 200
|
17
|
+
expect(response.status_code).to be == 200
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when nil' do
|
22
|
+
let(:status) { nil }
|
23
|
+
|
24
|
+
it 'should return zero' do
|
25
|
+
expect(response.code).to be == 0
|
26
|
+
expect(response.status_code).to be == 0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#success?' do
|
32
|
+
context 'with success status code' do
|
33
|
+
let(:status) { 200 }
|
34
|
+
it { expect(response).to be_success }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with error status code' do
|
38
|
+
let(:status) { 500 }
|
39
|
+
it { expect(response).to_not be_success }
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with zero status code' do
|
43
|
+
let(:status) { nil }
|
44
|
+
it { expect(response).to_not be_success }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#modified?' do
|
49
|
+
context 'with success status code' do
|
50
|
+
let(:status) { 200 }
|
51
|
+
it { expect(response).to be_modified }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with not modified status code' do
|
55
|
+
let(:status) { 304 }
|
56
|
+
it { expect(response).to_not be_modified }
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with error status code' do
|
60
|
+
let(:status) { 500 }
|
61
|
+
it { expect(response).to be_modified }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with zero status code' do
|
65
|
+
let(:status) { nil }
|
66
|
+
it { expect(response).to be_modified }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acfs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Graichen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- lib/acfs/middleware/base.rb
|
205
205
|
- lib/acfs/middleware/json_decoder.rb
|
206
206
|
- lib/acfs/middleware/json_encoder.rb
|
207
|
+
- lib/acfs/middleware/logger.rb
|
207
208
|
- lib/acfs/middleware/msgpack_decoder.rb
|
208
209
|
- lib/acfs/middleware/msgpack_encoder.rb
|
209
210
|
- lib/acfs/middleware/print.rb
|
@@ -224,6 +225,7 @@ files:
|
|
224
225
|
- lib/acfs/request/callbacks.rb
|
225
226
|
- lib/acfs/response.rb
|
226
227
|
- lib/acfs/response/formats.rb
|
228
|
+
- lib/acfs/response/status.rb
|
227
229
|
- lib/acfs/service.rb
|
228
230
|
- lib/acfs/service/middleware.rb
|
229
231
|
- lib/acfs/service/request_handler.rb
|
@@ -240,6 +242,7 @@ files:
|
|
240
242
|
- spec/acfs/request/callbacks_spec.rb
|
241
243
|
- spec/acfs/request_spec.rb
|
242
244
|
- spec/acfs/response/formats_spec.rb
|
245
|
+
- spec/acfs/response/status_spec.rb
|
243
246
|
- spec/acfs/service_spec.rb
|
244
247
|
- spec/acfs_spec.rb
|
245
248
|
- spec/spec_helper.rb
|
@@ -281,6 +284,7 @@ test_files:
|
|
281
284
|
- spec/acfs/request/callbacks_spec.rb
|
282
285
|
- spec/acfs/request_spec.rb
|
283
286
|
- spec/acfs/response/formats_spec.rb
|
287
|
+
- spec/acfs/response/status_spec.rb
|
284
288
|
- spec/acfs/service_spec.rb
|
285
289
|
- spec/acfs_spec.rb
|
286
290
|
- spec/spec_helper.rb
|