acfs 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0522ae91e3c94fadb85d436d0526914db67fe867
4
- data.tar.gz: 5b867fc16138f4d4ae1800d14375877477571d36
3
+ metadata.gz: f5bd82876ac05bd67b6a8d95a8d9e0779eb7d1c1
4
+ data.tar.gz: 5234ed3bfb459596c3cc0533bec51a67013f0b69
5
5
  SHA512:
6
- metadata.gz: 16155101c03288a59633bacc3810454c34b5631ff71f2712fa2a3d7b384971e3798d8577dd3f01f0ccaeb3a5863e6673764a6e7d93db8d343835608e2009576d
7
- data.tar.gz: 9e17ca080917e2151e8d4ae0dc27c661f35322458b0295a8f9c4577cf6aec089f319ec533b86b0721c2ec296727e52ab638d28ff11db068df3f9fdd4739aa3f5
6
+ metadata.gz: e10f336df5c116c5155ab4c51dbbe8a746ce3235b87180c69a5a1651ed285c75abeccd219b95fc0dda1a5e65bd9ccd3bfa74a42f672ae9de60d8ec1f9f99f14a
7
+ data.tar.gz: 8631fdb5b179084f4f54ca953aa750f6b70242c5ebcd6aa6c52f97836663dd88e3d98c6dc30239c9144a63718913d3bac4041530a3aaccebedf8567f4404ef4e
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  *.gem
2
2
  *.rbc
3
+ .rbx
3
4
  .bundle
4
5
  .config
5
6
  .yardoc
data/README.md CHANGED
@@ -2,12 +2,9 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/acfs.png)](http://badge.fury.io/rb/acfs) [![Build Status](https://travis-ci.org/jgraichen/acfs.png?branch=master)](https://travis-ci.org/jgraichen/acfs) [![Coverage Status](https://coveralls.io/repos/jgraichen/acfs/badge.png?branch=master)](https://coveralls.io/r/jgraichen/acfs) [![Code Climate](https://codeclimate.com/github/jgraichen/acfs.png)](https://codeclimate.com/github/jgraichen/acfs) [![Dependency Status](https://gemnasium.com/jgraichen/acfs.png)](https://gemnasium.com/jgraichen/acfs)
4
4
 
5
- Acfs is a library to develop API client libraries for single services within a
6
- larger service oriented application.
5
+ Acfs is a library to develop API client libraries for single services within a larger service oriented application.
7
6
 
8
- Acfs covers model and service abstraction, convenient query and filter methods,
9
- full middleware stack for pre-processing requests and responses on a per service
10
- level and automatic request queuing and parallel processing. See Usage for more.
7
+ Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses on a per service level and automatic request queuing and parallel processing. See Usage for more.
11
8
 
12
9
  ## Installation
13
10
 
@@ -15,8 +12,7 @@ Add this line to your application's Gemfile:
15
12
 
16
13
  gem 'acfs', '~> 0.14.0'
17
14
 
18
- **Note:** Acfs is under development. I'll try to avoid changes to the public
19
- API but internal APIs may change quite often.
15
+ **Note:** Acfs is under development. I'll try to avoid changes to the public API but internal APIs may change quite often.
20
16
 
21
17
  And then execute:
22
18
 
@@ -42,8 +38,7 @@ class UserService < Acfs::Service
42
38
  end
43
39
  ```
44
40
 
45
- This specifies where the `UserService` is located. You can now create some
46
- models representing resources served by the `UserService`.
41
+ This specifies where the `UserService` is located. You can now create some models representing resources served by the `UserService`.
47
42
 
48
43
  ```ruby
49
44
  class User
@@ -63,8 +58,7 @@ class User
63
58
  end
64
59
  ```
65
60
 
66
- The service and model classes can be shipped as a gem or git submodule to be
67
- included by the frontend application(s).
61
+ The service and model classes can be shipped as a gem or git submodule to be included by the frontend application(s).
68
62
 
69
63
  You can use the model there:
70
64
 
@@ -87,8 +81,7 @@ Acfs.run # Will request `http://users.myapp.org/users`
87
81
  @users #=> [<User>, ...]
88
82
  ```
89
83
 
90
- If you need multiple resources or dependent resources first define a "plan"
91
- how they can be loaded:
84
+ If you need multiple resources or dependent resources first define a "plan" how they can be loaded:
92
85
 
93
86
  ```ruby
94
87
  @user = User.find(5) do |user|
@@ -141,6 +134,57 @@ Acfs has basic update support using `PUT` requests:
141
134
  @user.persisted? # => true
142
135
  ```
143
136
 
137
+ ## Stubbing
138
+
139
+ You can stub resources in applications using an Acfs service client:
140
+
141
+ ```ruby
142
+ # Enable stubs in spec helper
143
+ Acfs::Stub.enable
144
+
145
+ before do
146
+ Acfs::Stub.resource MyUser, :read, with: { id: 1 }, return: { id: 1, name: 'John Smith', age: 32 }
147
+ Acfs::Stub.resource MyUser, :read, with: { id: 2 }, raise: :not_found
148
+ Acfs::Stub.resource Session, :create, with: { ident: 'john@exmaple.org', password: 's3cr3t' }, return: { id: 'longhash', user: 1 }
149
+ end
150
+
151
+ it 'should find user number one' do
152
+ user = MyUser.find 1
153
+ Acfs.run
154
+
155
+ expect(user.id).to be == 1
156
+ expect(user.name).to be == 'John Smith'
157
+ expect(user.age).to be == 32
158
+ end
159
+
160
+ it 'should not find user number two' do
161
+ MyUser.find 3
162
+
163
+ expect { Acfs.run }.to raise_error(Acfs::ResourceNotFound)
164
+ end
165
+
166
+ it 'should allow stub resource creation' do
167
+ session = Session.create! ident: 'john@exmaple.org', password: 's3cr3t'
168
+
169
+ expect(session.id).to be == 'longhash'
170
+ expect(session.user).to be == 1
171
+ end
172
+ ```
173
+
174
+ By default Acfs raises an error when a non stubbed resource should be requested. You can switch of the behavior:
175
+
176
+ ```ruby
177
+ before do
178
+ Acfs::Stub.allow_requests = true
179
+ end
180
+
181
+ it 'should find user number one' do
182
+ user = MyUser.find 1
183
+ Acfs.run # Would have raised Acfs::RealRequestNotAllowedError
184
+ # Will run real request to user service instead.
185
+ end
186
+ ```
187
+
144
188
  ## Roadmap
145
189
 
146
190
  * Update
@@ -157,7 +201,6 @@ Acfs has basic update support using `PUT` requests:
157
201
  Modified Headers, conflict detection, ...
158
202
  * Pagination? Filtering? (If service API provides such features.)
159
203
  * Messaging Queue support for services and models
160
- * Allow stubbing of resources as objects for testing services.
161
204
  * Documentation
162
205
 
163
206
  ## Contributing
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency 'actionpack'
24
24
  spec.add_runtime_dependency 'multi_json'
25
25
  spec.add_runtime_dependency 'typhoeus'
26
+ spec.add_runtime_dependency 'rack'
26
27
 
27
28
  spec.add_development_dependency 'bundler', '~> 1.3'
28
29
  end
@@ -1,16 +1,24 @@
1
1
  require 'active_support'
2
- require 'active_support/core_ext'
2
+ require 'active_support/core_ext/class'
3
+ require 'active_support/core_ext/hash'
4
+ require 'active_support/core_ext/module'
5
+
3
6
  require 'acfs/version'
4
7
  require 'acfs/errors'
8
+ require 'acfs/global'
5
9
 
6
10
  module Acfs
7
11
  extend ActiveSupport::Autoload
12
+ extend Global
8
13
 
9
14
  autoload :Collection
10
15
  autoload :Model
11
16
  autoload :Request
12
17
  autoload :Response
13
18
  autoload :Service
19
+ autoload :Stub
20
+ autoload :Operation
21
+ autoload :Runner
14
22
 
15
23
  module Middleware
16
24
  extend ActiveSupport::Autoload
@@ -27,19 +35,8 @@ module Acfs
27
35
  module Adapter
28
36
  extend ActiveSupport::Autoload
29
37
 
38
+ autoload :Base
30
39
  autoload :Typhoeus
31
40
  end
32
-
33
- class << self
34
-
35
- # Run all queued
36
- def run
37
- adapter.run
38
- end
39
-
40
- def adapter
41
- @adapter ||= Adapter::Typhoeus.new
42
- end
43
- end
44
41
  end
45
42
 
@@ -0,0 +1,28 @@
1
+ module Acfs::Adapter
2
+
3
+ # Base adapter handling operation queuing
4
+ # and processing.
5
+ #
6
+ class Base
7
+
8
+ # Start processing queued requests.
9
+ #
10
+ def start
11
+ end
12
+
13
+ # Abort running and queued requests.
14
+ #
15
+ def abort
16
+ end
17
+
18
+ # Run request right now skipping queue.
19
+ #
20
+ def run(_)
21
+ end
22
+
23
+ # Enqueue request to be run later.
24
+ #
25
+ def queue(_)
26
+ end
27
+ end
28
+ end
@@ -5,26 +5,22 @@ module Acfs
5
5
 
6
6
  # Adapter for Typhoeus.
7
7
  #
8
- class Typhoeus
8
+ class Typhoeus < Base
9
9
 
10
- # Run all queued requests.
11
- #
12
- def run(request = nil)
13
- return hydra.run unless request
10
+ def start
11
+ hydra.run
12
+ end
14
13
 
15
- convert_request(request).run
14
+ def abort
15
+ hydra.abort
16
16
  end
17
17
 
18
- # Add a new request or URL to the queue.
19
- #
20
- def queue(req)
21
- hydra.queue convert_request(req)
18
+ def run(request)
19
+ convert_request(request).run
22
20
  end
23
21
 
24
- # Remove all requests from queue.
25
- #
26
- def clear
27
- hydra.abort
22
+ def queue(request)
23
+ hydra.queue convert_request request
28
24
  end
29
25
 
30
26
  protected
@@ -28,4 +28,6 @@ module Acfs
28
28
  super
29
29
  end
30
30
  end
31
+
32
+ class RealRequestNotAllowedError < StandardError; end
31
33
  end
@@ -0,0 +1,17 @@
1
+ module Acfs
2
+
3
+ # Global Acfs module methods.
4
+ #
5
+ module Global
6
+
7
+ def runner
8
+ @runner ||= Runner.new Adapter::Typhoeus.new
9
+ end
10
+
11
+ # Run all queued operations.
12
+ #
13
+ def run
14
+ runner.start
15
+ end
16
+ end
17
+ end
@@ -5,6 +5,7 @@ require 'acfs/model/dirty'
5
5
  require 'acfs/model/loadable'
6
6
  require 'acfs/model/locatable'
7
7
  require 'acfs/model/persistence'
8
+ require 'acfs/model/operational'
8
9
  require 'acfs/model/query_methods'
9
10
  require 'acfs/model/relations'
10
11
  require 'acfs/model/service'
@@ -30,6 +31,7 @@ module Acfs
30
31
  include Model::Loadable
31
32
  include Model::Persistence
32
33
  include Model::Locatable
34
+ include Model::Operational
33
35
  include Model::QueryMethods
34
36
  include Model::Relations
35
37
  include Model::Service
@@ -0,0 +1,19 @@
1
+ module Acfs::Model
2
+
3
+ # Provide methods for creating and processing CRUD operations and
4
+ # handling responses. That includes error handling as well as
5
+ # handling stubbed resources.
6
+ #
7
+ # Should only be used internal.
8
+ #
9
+ module Operational
10
+ extend ActiveSupport::Concern
11
+ delegate :operation, to: :'self.class'
12
+
13
+ module ClassMethods
14
+ def operation(action, opts = {}, &block)
15
+ Acfs.runner.process ::Acfs::Operation.new self, action, opts, &block
16
+ end
17
+ end
18
+ end
19
+ end
@@ -52,16 +52,9 @@ module Acfs
52
52
 
53
53
  opts[:data] = attributes unless opts[:data]
54
54
 
55
- request = new? ? create_request(opts) : put_request(opts)
56
- request.on_complete do |response|
57
- if response.success?
58
- update_with response.data
59
- else
60
- self.class.raise! response
61
- end
55
+ operation (new? ? :create : :update), opts do |data|
56
+ update_with data
62
57
  end
63
-
64
- self.class.service.run request
65
58
  end
66
59
 
67
60
  module ClassMethods
@@ -96,14 +89,6 @@ module Acfs
96
89
  self.attributes = data
97
90
  loaded!
98
91
  end
99
-
100
- def create_request(opts = {})
101
- Acfs::Request.new self.class.url, method: :post, data: opts[:data]
102
- end
103
-
104
- def put_request(opts = {})
105
- Acfs::Request.new url, method: :put, data: opts[:data]
106
- end
107
92
  end
108
93
  end
109
94
  end
@@ -37,8 +37,8 @@ module Acfs::Model
37
37
  def all(params = {}, &block)
38
38
  collection = ::Acfs::Collection.new
39
39
 
40
- request = Acfs::Request.new(url, params: params) do |response|
41
- response.data.each do |obj|
40
+ operation :list, params: params do |data|
41
+ data.each do |obj|
42
42
  collection << self.new.tap do |m|
43
43
  m.attributes = obj
44
44
  m.loaded!
@@ -47,37 +47,23 @@ module Acfs::Model
47
47
  collection.loaded!
48
48
  block.call collection unless block.nil?
49
49
  end
50
- service.queue request
51
50
 
52
51
  collection
53
52
  end
54
53
  alias :where :all
55
54
 
56
- def raise!(response)
57
- case response.code
58
- when 404
59
- raise ::Acfs::ResourceNotFound.new response: response
60
- when 422
61
- raise ::Acfs::InvalidResource.new response: response, errors: response.data['errors']
62
- else
63
- raise ::Acfs::ErroneousResponse.new response: response
64
- end
65
- end
66
-
67
55
  private
68
56
  def find_single(id, opts, &block)
69
57
  model = self.new
70
58
 
71
- request = Acfs::Request.new url(id.to_s) do |response|
72
- if response.success?
73
- model.attributes = response.data
74
- model.loaded!
75
- block.call model unless block.nil?
76
- else
77
- raise! response
78
- end
59
+ opts[:params] ||= {}
60
+ opts[:params].merge!({ id: id })
61
+
62
+ operation :read, opts do |data|
63
+ model.attributes = data
64
+ model.loaded!
65
+ block.call model unless block.nil?
79
66
  end
80
- service.queue request
81
67
 
82
68
  model
83
69
  end
@@ -0,0 +1,60 @@
1
+ module Acfs
2
+
3
+ # Describes a CRUD operation. Handle request creation and response
4
+ # processing as well as error handling and stubbing.
5
+ #
6
+ class Operation
7
+ attr_reader :action, :params, :resource, :data, :callback
8
+ delegate :service, to: :resource
9
+
10
+ def initialize(resource, action, opts = {}, &block)
11
+ @resource = resource
12
+ @action = action.to_sym
13
+ @params = opts[:params] || {}
14
+ @data = opts[:data] || {}
15
+ @callback = block
16
+
17
+ raise ArgumentError, 'ID parameter required for READ, UPDATE and DELETE operation.' if single? and id.nil?
18
+ end
19
+
20
+ def single?
21
+ [:read, :update, :delete].include? action
22
+ end
23
+
24
+ def synchronous?
25
+ [:update, :delete, :create].include? action
26
+ end
27
+
28
+ def id
29
+ @id ||= params.delete(:id) || data[:id]
30
+ end
31
+
32
+ def url
33
+ single? ? resource.url(id) : resource.url
34
+ end
35
+
36
+ def method
37
+ { read: :get, list: :get, update: :put, create: :post, delete: :delete }[action]
38
+ end
39
+
40
+ def request
41
+ request = ::Acfs::Request.new url, method: method, params: params, data: data
42
+ request.on_complete do |response|
43
+ handle_failure response unless response.success?
44
+ callback.call response.data
45
+ end
46
+ request
47
+ end
48
+
49
+ def handle_failure(response)
50
+ case response.code
51
+ when 404
52
+ raise ::Acfs::ResourceNotFound.new response: response
53
+ when 422
54
+ raise ::Acfs::InvalidResource.new response: response, errors: response.data.try(:[], 'errors')
55
+ else
56
+ raise ::Acfs::ErroneousResponse.new response: response
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,76 @@
1
+ require 'acfs/service/middleware'
2
+
3
+ module Acfs
4
+
5
+ class Runner
6
+ include Service::Middleware
7
+ attr_reader :adapter
8
+
9
+ def initialize(adapter)
10
+ @adapter = adapter
11
+ @running = false
12
+ end
13
+
14
+ # Process an operation. Synchronous operations will be run
15
+ # and parallel operations will be queued.
16
+ #
17
+ def process(op)
18
+ op.synchronous? ? run(op) : enqueue(op)
19
+ end
20
+
21
+ # Run operation right now skipping queue.
22
+ #
23
+ def run(op)
24
+ op_request(op) { |req| adapter.run req }
25
+ end
26
+
27
+ # List of current queued operations.
28
+ #
29
+ def queue
30
+ @queue ||= []
31
+ end
32
+
33
+ # Enqueue operation to be run later.
34
+ #
35
+ def enqueue(op)
36
+ if running?
37
+ op_request(op) { |req| adapter.queue req }
38
+ else
39
+ queue << op
40
+ end
41
+ end
42
+
43
+ # Return true if queued operations are currently processed.
44
+ #
45
+ def running?
46
+ @running
47
+ end
48
+
49
+ # Start processing queued operations.
50
+ #
51
+ def start
52
+ enqueue_operations
53
+
54
+ @running = true
55
+ adapter.start
56
+ @running = false
57
+ end
58
+
59
+ def clear
60
+ queue.clear
61
+ adapter.abort
62
+ end
63
+
64
+ private
65
+ def enqueue_operations
66
+ while (op = queue.shift)
67
+ op_request(op) { |req| adapter.queue req }
68
+ end
69
+ end
70
+
71
+ def op_request(op)
72
+ return if Acfs::Stub.enabled? and Acfs::Stub.stubbed(op)
73
+ yield prepare op.service.prepare op.request
74
+ end
75
+ end
76
+ end
@@ -1,4 +1,3 @@
1
- require 'acfs/service/request_handler'
2
1
  require 'acfs/service/middleware'
3
2
 
4
3
  module Acfs
@@ -9,7 +8,6 @@ module Acfs
9
8
  attr_accessor :options
10
9
  class_attribute :base_url
11
10
 
12
- include Service::RequestHandler
13
11
  include Service::Middleware
14
12
 
15
13
  def initialize(options = {})
@@ -8,8 +8,8 @@ module Acfs
8
8
  module Middleware
9
9
  extend ActiveSupport::Concern
10
10
 
11
- def prepare(_) # :nodoc:
12
- self.class.middleware.call super
11
+ def prepare(request) # :nodoc:
12
+ self.class.middleware.call request
13
13
  end
14
14
 
15
15
  module ClassMethods
@@ -40,7 +40,7 @@ module Acfs
40
40
  #
41
41
  def clear
42
42
  @middleware = nil
43
- @middlewares = nil
43
+ @middlewares = []
44
44
  end
45
45
  end
46
46
  end
@@ -0,0 +1,85 @@
1
+ require 'rack/utils'
2
+
3
+ module Acfs
4
+
5
+ # Global handler for stubbing resources.
6
+ #
7
+ class Stub
8
+ ACTIONS = [ :read, :create, :update, :delete, :list ]
9
+
10
+ class << self
11
+
12
+ # Stub a resource with given handler block. An already created handler
13
+ # for same resource class will be overridden.
14
+ #
15
+ def resource(klass, action, opts = {}, &block)
16
+ action = action.to_sym
17
+ raise ArgumentError, "Unknown action `#{action}`." unless ACTIONS.include? action
18
+
19
+ stubs[klass] ||= {}
20
+ stubs[klass][action] ||= []
21
+ stubs[klass][action] << opts
22
+ end
23
+
24
+ def allow_requests=(allow)
25
+ @allow_requests = allow ? true : false
26
+ end
27
+
28
+ def allow_requests?
29
+ @allow_requests ||= false
30
+ end
31
+
32
+ def enabled?
33
+ @enabled ||= false
34
+ end
35
+
36
+ def enable; @enabled = true end
37
+ def disable; @enabled = false end
38
+
39
+ # Clear all stubs.
40
+ #
41
+ def clear(klass = nil)
42
+ klass.nil? ? stubs.clear : stubs[klass].try(:clear)
43
+ end
44
+
45
+ def stubs
46
+ @stubs ||= {}
47
+ end
48
+
49
+ def stub_for(op)
50
+ return false unless (classes = stubs[op.resource])
51
+ return false unless (actions = classes[op.action])
52
+
53
+ params = op.params
54
+ params.merge! id: op.id unless op.id.nil?
55
+ actions.select! { |stub| stub[:with] == params || stub[:with] == op.data }
56
+ actions.first
57
+ end
58
+
59
+ def stubbed(op)
60
+ stub = stub_for op
61
+ unless stub
62
+ return false if allow_requests?
63
+ raise RealRequestNotAllowedError, "No stub found for `#{op.resource.name}` with params `#{op.params}`"
64
+ end
65
+
66
+ if (data = stub[:return])
67
+ op.callback.call data
68
+ elsif (err = stub[:raise])
69
+ raise_error op, err, stub[:return]
70
+ else
71
+ raise ArgumentError, 'Unsupported stub.'
72
+ end
73
+
74
+ true
75
+ end
76
+
77
+ private
78
+ def raise_error(op, name, data)
79
+ raise name if name.is_a? Class
80
+
81
+ op.handle_failure ::Acfs::Response.new nil, status: Rack::Utils.status_code(name), data: data
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,7 +1,7 @@
1
1
  module Acfs
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 14
4
+ MINOR = 15
5
5
  PATCH = 0
6
6
  STAGE = nil
7
7
 
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ class TestMiddleware < Acfs::Middleware::Base
4
+ end
5
+
6
+ describe Acfs::Service::Middleware do
7
+ let(:srv_class) { Class.new(Acfs::Service) }
8
+ let(:options) { {} }
9
+ let(:service) { srv_class.new options }
10
+ let(:middleware) { TestMiddleware }
11
+
12
+ describe '.use' do
13
+ let(:options) { { abc: 'cde' } }
14
+
15
+ it 'should add middleware to list' do
16
+ srv_class.use middleware
17
+
18
+ expect(srv_class.instance_variable_get(:@middlewares)).to include(middleware)
19
+ end
20
+
21
+ it 'should add middleware to stack' do
22
+ srv_class.use middleware
23
+
24
+ expect(srv_class.middleware).to be_a(middleware)
25
+ end
26
+
27
+ it 'should instantiate middleware object' do
28
+ middleware.should_receive(:new).with(anything, options)
29
+
30
+ srv_class.use middleware, options
31
+ end
32
+ end
33
+
34
+ describe '.clear' do
35
+ before { srv_class.use middleware }
36
+
37
+ it 'should clear middleware list' do
38
+ srv_class.clear
39
+
40
+ expect(srv_class.instance_variable_get(:@middlewares)).to be_empty
41
+ end
42
+
43
+ it 'should reset middleware stack' do
44
+ srv_class.clear
45
+
46
+ expect(srv_class.instance_variable_get(:@middleware)).to be_nil
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ class SpecialCustomError < StandardError; end
4
+
5
+ describe Acfs::Stub do
6
+ let(:stub) { Class.new(Acfs::Stub) }
7
+
8
+ before(:all) { Acfs::Stub.enable }
9
+ after(:all) { Acfs::Stub.disable }
10
+
11
+ before do
12
+ Acfs::Stub.allow_requests = false
13
+
14
+ #Acfs::Stub.read(MyUser).with(id: 5).and_return({ id: 5, name: 'John', age: 32 })
15
+ #Acfs::Stub.read(MyUser).with(id: 6).and_raise(:not_found)
16
+ #Acfs::Stub.create(MyUser).with(name: '', age: 12).and_return(:invalid, errors: { name: [ 'must be present ']})
17
+ end
18
+
19
+ describe '.resource' do
20
+ context 'with read action' do
21
+ before do
22
+ Acfs::Stub.resource MyUser, :read, with: { id: 1 }, return: { id: 1, name: 'John Smith', age: 32 }
23
+ Acfs::Stub.resource MyUser, :read, with: { id: 2 }, raise: SpecialCustomError
24
+ Acfs::Stub.resource MyUser, :read, with: { id: 3 }, raise: :not_found
25
+ end
26
+
27
+ it 'should allow to stub resource reads' do
28
+ user = MyUser.find 1
29
+ Acfs.run
30
+
31
+ expect(user.id).to be == 1
32
+ expect(user.name).to be == 'John Smith'
33
+ expect(user.age).to be == 32
34
+ end
35
+
36
+ context 'with error' do
37
+ it 'should allow to raise errors' do
38
+ MyUser.find 2
39
+
40
+ expect { Acfs.run }.to raise_error(SpecialCustomError)
41
+ end
42
+
43
+ it 'should allow to raise symbolic errors' do
44
+ MyUser.find 3
45
+
46
+ expect { Acfs.run }.to raise_error(Acfs::ResourceNotFound)
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'with create action' do
52
+ before do
53
+ Acfs::Stub.resource Session, :create, with: { ident: 'john@exmaple.org', password: 's3cr3t' }, return: { id: 'longhash', user: 1 }
54
+ Acfs::Stub.resource Session, :create, with: { ident: 'john@exmaple.org', password: 'wrong' }, raise: 422
55
+ end
56
+
57
+ it 'should allow stub resource creation' do
58
+ session = Session.create! ident: 'john@exmaple.org', password: 's3cr3t'
59
+
60
+ expect(session.id).to be == 'longhash'
61
+ expect(session.user).to be == 1
62
+ end
63
+
64
+ it 'should allow to raise error' do
65
+ expect {
66
+ Session.create! ident: 'john@exmaple.org', password: 'wrong'
67
+ }.to raise_error(::Acfs::InvalidResource)
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '.allow_requests=' do
73
+ context 'when enabled' do
74
+ before do
75
+ Acfs::Stub.allow_requests = true
76
+ stub_request(:get, 'http://users.example.org/users/2').to_return response({ id: 2, name: 'John', age: 26 })
77
+ end
78
+
79
+ it 'should allow real requests' do
80
+ @user = MyUser.find 2
81
+ expect { Acfs.run }.to_not raise_error
82
+ end
83
+ end
84
+
85
+ context 'when disabled' do
86
+ before do
87
+ Acfs::Stub.allow_requests = false
88
+ end
89
+
90
+ it 'should allow real requests' do
91
+ @user = MyUser.find 2
92
+ expect { Acfs.run }.to raise_error(Acfs::RealRequestNotAllowedError)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,19 +1,18 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Acfs" do
3
+ describe 'Acfs' do
4
4
 
5
5
  before do
6
- headers = {}
7
- stub_request(:get, "http://users.example.org/users").to_return response([{ id: 1, name: "Anon", age: 12 }, { id: 2, name: "John", age: 26 }])
8
- stub_request(:get, "http://users.example.org/users/2").to_return response({ id: 2, name: "John", age: 26 })
9
- stub_request(:get, "http://users.example.org/users/3").to_return response({ id: 3, name: "Miraculix", age: 122 })
10
- stub_request(:get, "http://users.example.org/users/100").to_return response({ id:100, name: "Jimmy", age: 45 })
11
- stub_request(:get, "http://users.example.org/users/2/friends").to_return response([{ id: 1, name: "Anon", age: 12 }])
12
- stub_request(:get, "http://comments.example.org/comments?user=2").to_return response([{ id: 1, text: "Comment #1"}, { id: 2, text: "Comment #2" }])
6
+ stub_request(:get, 'http://users.example.org/users').to_return response([{ id: 1, name: 'Anon', age: 12 }, { id: 2, name: 'John', age: 26 }])
7
+ stub_request(:get, 'http://users.example.org/users/2').to_return response({ id: 2, name: 'John', age: 26 })
8
+ stub_request(:get, 'http://users.example.org/users/3').to_return response({ id: 3, name: 'Miraculix', age: 122 })
9
+ stub_request(:get, 'http://users.example.org/users/100').to_return response({ id:100, name: 'Jimmy', age: 45 })
10
+ stub_request(:get, 'http://users.example.org/users/2/friends').to_return response([{ id: 1, name: 'Anon', age: 12 }])
11
+ stub_request(:get, 'http://comments.example.org/comments?user=2').to_return response([{ id: 1, text: 'Comment #1' }, { id: 2, text: 'Comment #2' }])
13
12
  end
14
13
 
15
14
  it 'should update single resource synchronously' do
16
- stub = stub_request(:put, "http://users.example.org/users/2")
15
+ stub = stub_request(:put, 'http://users.example.org/users/2')
17
16
  .to_return { |request| { body: request.body, headers: {'Content-Type' => request.headers['Content-Type']}} }
18
17
 
19
18
  @user = MyUser.find 2
@@ -22,7 +21,7 @@ describe "Acfs" do
22
21
  expect(@user).to_not be_changed
23
22
  expect(@user).to be_persisted
24
23
 
25
- @user.name = "Johnny"
24
+ @user.name = 'Johnny'
26
25
 
27
26
  expect(@user).to be_changed
28
27
  expect(@user).to_not be_persisted
@@ -35,7 +34,7 @@ describe "Acfs" do
35
34
  end
36
35
 
37
36
  it 'should create a single resource synchronously' do
38
- stub = stub_request(:post, "http://users.example.org/sessions").to_return response({id: 'sessionhash', user: 1})
37
+ stub = stub_request(:post, 'http://users.example.org/sessions').to_return response({id: 'sessionhash', user: 1})
39
38
 
40
39
  session = Session.create ident: 'Anon'
41
40
 
@@ -104,7 +103,7 @@ describe "Acfs" do
104
103
  end
105
104
 
106
105
  it 'should load associated resources' do
107
- pending "TODO: Implement high level feature"
106
+ pending 'TODO: Implement high level feature'
108
107
 
109
108
  @user = MyUser.find(2) do |user|
110
109
  @friends = user.friends.all
@@ -29,6 +29,7 @@ RSpec.configure do |config|
29
29
  end
30
30
 
31
31
  config.before :each do
32
- Acfs.adapter.clear
32
+ Acfs.runner.clear
33
+ Acfs::Stub.clear
33
34
  end
34
35
  end
@@ -1,3 +1,4 @@
1
+ require 'json'
1
2
 
2
3
  def response(data = nil, opts = {})
3
4
  if data
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.14.0
4
+ version: 0.15.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-05-02 00:00:00.000000000 Z
11
+ date: 2013-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: bundler
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -113,9 +127,11 @@ files:
113
127
  - gemfiles/Gemfile.rails-3-2
114
128
  - gemfiles/Gemfile.rails-4-0
115
129
  - lib/acfs.rb
130
+ - lib/acfs/adapter/base.rb
116
131
  - lib/acfs/adapter/typhoeus.rb
117
132
  - lib/acfs/collection.rb
118
133
  - lib/acfs/errors.rb
134
+ - lib/acfs/global.rb
119
135
  - lib/acfs/middleware/base.rb
120
136
  - lib/acfs/middleware/json_decoder.rb
121
137
  - lib/acfs/middleware/json_encoder.rb
@@ -132,18 +148,21 @@ files:
132
148
  - lib/acfs/model/initialization.rb
133
149
  - lib/acfs/model/loadable.rb
134
150
  - lib/acfs/model/locatable.rb
151
+ - lib/acfs/model/operational.rb
135
152
  - lib/acfs/model/persistence.rb
136
153
  - lib/acfs/model/query_methods.rb
137
154
  - lib/acfs/model/relations.rb
138
155
  - lib/acfs/model/service.rb
156
+ - lib/acfs/operation.rb
139
157
  - lib/acfs/request.rb
140
158
  - lib/acfs/request/callbacks.rb
141
159
  - lib/acfs/response.rb
142
160
  - lib/acfs/response/formats.rb
143
161
  - lib/acfs/response/status.rb
162
+ - lib/acfs/runner.rb
144
163
  - lib/acfs/service.rb
145
164
  - lib/acfs/service/middleware.rb
146
- - lib/acfs/service/request_handler.rb
165
+ - lib/acfs/stub.rb
147
166
  - lib/acfs/version.rb
148
167
  - spec/acfs/middleware/json_decoder_spec.rb
149
168
  - spec/acfs/middleware/msgpack_decoder_spec.rb
@@ -158,7 +177,9 @@ files:
158
177
  - spec/acfs/request_spec.rb
159
178
  - spec/acfs/response/formats_spec.rb
160
179
  - spec/acfs/response/status_spec.rb
180
+ - spec/acfs/service/middleware_spec.rb
161
181
  - spec/acfs/service_spec.rb
182
+ - spec/acfs/stub_spec.rb
162
183
  - spec/acfs_spec.rb
163
184
  - spec/spec_helper.rb
164
185
  - spec/support/response.rb
@@ -201,7 +222,9 @@ test_files:
201
222
  - spec/acfs/request_spec.rb
202
223
  - spec/acfs/response/formats_spec.rb
203
224
  - spec/acfs/response/status_spec.rb
225
+ - spec/acfs/service/middleware_spec.rb
204
226
  - spec/acfs/service_spec.rb
227
+ - spec/acfs/stub_spec.rb
205
228
  - spec/acfs_spec.rb
206
229
  - spec/spec_helper.rb
207
230
  - spec/support/response.rb
@@ -1,27 +0,0 @@
1
- module Acfs
2
- class Service
3
-
4
- # Methods to queue or executed request through this service.
5
- #
6
- module RequestHandler
7
-
8
- # Queue a new request on global adapter queue.
9
- #
10
- def queue(request)
11
- Acfs.adapter.queue prepare request
12
- end
13
-
14
- # Executes a request now.
15
- #
16
- def run(request)
17
- Acfs.adapter.run prepare request
18
- end
19
-
20
- # Prepares a request to be processed through this service.
21
- #
22
- def prepare(request)
23
- request
24
- end
25
- end
26
- end
27
- end