acfs 0.14.0 → 0.15.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: 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