acfs 0.10.0 → 0.11.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: 9983fabc6b3c91e17cf08ac15da6e51aecf584b5
4
- data.tar.gz: 53f6525a13c507bd7cac8132d2618a2b92e551ca
3
+ metadata.gz: 69903ec6f49f6fb0d192c60aa302ddc9ba16f940
4
+ data.tar.gz: bb15a106632b4d5f3681d829bee1b4e547d61039
5
5
  SHA512:
6
- metadata.gz: 7869d4338121f188461dd2e2b4383f1175ebec3255e868a36cd53c63df31cbf6bb746135c8aaddebefb3f3ec5185e565606330a23ebcd75f3f4f838a94ec3591
7
- data.tar.gz: d9c7a0575f86fd9e2b71fc98336166aef3e770e82fe6c7f5365b7751eeee78a987fa8eb433b326e228ceccc62c2daba610268664594b5992127911676f5aa735
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.10.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.
@@ -17,6 +17,7 @@ module Acfs
17
17
 
18
18
  autoload :Base
19
19
  autoload :Print
20
+ autoload :Logger
20
21
  autoload :JsonDecoder
21
22
  autoload :MessagePackDecoder, 'acfs/middleware/msgpack_decoder'
22
23
  autoload :JsonEncoder
@@ -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(request, response.code, response.headers, response.body)
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
@@ -2,6 +2,22 @@ module Acfs
2
2
 
3
3
  # Acfs base error.
4
4
  #
5
- class Error < StandardError; end
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 #{name}."
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
- model.attributes = response.data
62
- model.loaded!
63
- block.call model unless block.nil?
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
@@ -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 :request, :status, :headers, :body
12
+ attr_reader :headers, :body
11
13
 
12
14
  include Response::Formats
15
+ include Response::Status
13
16
 
14
- def initialize(request, status = 200, headers = {}, body = nil)
15
- @request = request
16
- @status = status
17
- @headers = headers
18
- @body = body
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
@@ -1,7 +1,7 @@
1
1
  module Acfs
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 10
4
+ MINOR = 11
5
5
  PATCH = 0
6
6
  STAGE = nil
7
7
 
@@ -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
- before do
9
- stub_request(:get, 'http://users.example.org/users/1').to_return(
10
- body: MessagePack.dump({ id: 1, name: 'Anon', age: 12 }),
11
- headers: {'Content-Type' => 'application/x-msgpack'})
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
- it 'should load a single remote resource' do
15
- user = model.find 1
16
- Acfs.run
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
- expect(user.attributes).to be == { id: 1, name: 'Anon', age: 12 }.stringify_keys
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
- it 'should invoke callback after model is loaded' do
22
- proc = Proc.new { }
23
- proc.should_receive(:call) do |user|
24
- expect(user).to be === @user
25
- expect(user).to be_loaded
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
- @user = model.find 1, &proc
29
- Acfs.run
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
- it 'should load a multiple remote resources' do
44
- users = model.find 1, 2
45
- Acfs.run
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
- expect(users.size).to be == 2
48
- expect(users[0].attributes).to be == { id: 1, name: 'Anon', age: 12 }.stringify_keys
49
- expect(users[1].attributes).to be == { id: 2, name: 'Johnny', age: 42 }.stringify_keys
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
- it 'should invoke callback after all models are loaded' do
53
- proc = Proc.new { }
54
- proc.should_receive(:call) do |users|
55
- expect(users).to be === @users
56
- expect(users.size).to be == 2
57
- expect(users).to be_loaded
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
- @users = model.find 1, 2, &proc
61
- Acfs.run
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
@@ -27,4 +27,8 @@ RSpec.configure do |config|
27
27
  # Only allow expect syntax
28
28
  c.syntax = :expect
29
29
  end
30
+
31
+ config.before :each do
32
+ Acfs.adapter.clear
33
+ end
30
34
  end
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.10.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-04-24 00:00:00.000000000 Z
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