resource_kit 0.0.5 → 0.1.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: ee001caaa4f22885773d1f6e6ae076ec900762e2
4
- data.tar.gz: b373c40127c0a4d7312ecea86ae634acd1ccbe85
3
+ metadata.gz: 4dba252d8a80b4ce9ecf96972dada6a221824b05
4
+ data.tar.gz: bb36d233fe0ae8e623b5f3c4c26a8a5dc74d2c81
5
5
  SHA512:
6
- metadata.gz: dfcc677710aef8a376ac1d47e795b1267f2131ddd969f70352421b5b80788e7099a73dc04cba6282f5fe828ddc2f52ccc19e7856f0fbe591c8463381f4f89eb4
7
- data.tar.gz: 987624fc56bb90b02ae917c91b8802fcc54697497b1776695fecd915d8b4e3f38dc73a98318bed5cebcc73b08c5c824c8a24f21e993324c8a2757277e0e17534
6
+ metadata.gz: 63d61a484700d8bacf02569c3fe4c9cb5942f8cfc37e3fa3689b29a980e2726d0f2b190a2ea24cc9907e1b03a70be95f825ebf47c83cbdc40863b8656f435fcb
7
+ data.tar.gz: 6f6b4d111147cf08a39c5a10a8114838cdffef0b45667e645690c5419f6b15ed2c5f96b353d35deda2fa386828f179514e530456ff08a4b7c06886af105fe957
data/README.md CHANGED
@@ -31,26 +31,26 @@ When you're able to answer these questions, you can describe them in your resour
31
31
  ```ruby
32
32
  class DropletResource < ResourceKit::Resource
33
33
  resources do
34
- default_handler(422) {|response| ErrorMapping.extract_single(response.body, :read) }
35
- default_handler(:ok, :created) {|response| DropletMapping.extract_single(response.body, :read) }
34
+ default_handler(422) { |response| ErrorMapping.extract_single(response.body, :read) }
35
+ default_handler(:ok, :created) { |response| DropletMapping.extract_single(response.body, :read) }
36
36
 
37
37
  # Defining actions will create instance methods on the resource class to call them.
38
38
  action :find do
39
39
  verb :get # get is assumed if this is omitted
40
40
  path '/droplets/:id'
41
- handler(200) {|response| DropletMapping.extract_single(response.body, :read) }
41
+ handler(200) { |response| DropletMapping.extract_single(response.body, :read) }
42
42
  end
43
43
 
44
44
  action :all do
45
45
  path '/droplets'
46
- handler(200) {|body| DropletMapping.extract_collection(body, :read) }
46
+ handler(200) { |body| DropletMapping.extract_collection(body, :read) }
47
47
  end
48
48
 
49
49
  action :create do
50
50
  path '/droplets'
51
51
  verb :post
52
- body {|object| DropletMapping.representation_for(:create, object) } # Generate a response body from a passed object
53
- handler(202) {|response| DropletMapping.extract_single(response.body, :read) }
52
+ body { |object| DropletMapping.representation_for(:create, object) } # Generate a response body from a passed object
53
+ handler(202) { |response| DropletMapping.extract_single(response.body, :read) }
54
54
  end
55
55
  end
56
56
  end
@@ -86,6 +86,56 @@ single_droplet = resource.find(id: 123)
86
86
  create = resource.create(Droplet.new)
87
87
  ```
88
88
 
89
+ ## Scope
90
+
91
+ ResourceKit classes give you the option to pass in an optional scope object, so that you may interact with the resource with it that way.
92
+
93
+ For example, you may want to use this for nested resources:
94
+
95
+ ```ruby
96
+ class CommentResource < ResourceKit::Resource
97
+ resources do
98
+ action :all do
99
+ path { "/users/#{user_id}/comments" }
100
+ handler(200) { |resp| CommentMapping.extract_collection(resp.body, :read) }
101
+ end
102
+ end
103
+
104
+ def user_id
105
+ scope.user_id
106
+ end
107
+ end
108
+
109
+ user = User.find(123)
110
+ resource = CommentResource.new(connection, user)
111
+ comments = resource.all #=> Will fetch from /users/123/comments
112
+ ```
113
+
114
+ ## Test Helpers
115
+
116
+ ResourceKit supplys test helpers that assist in certain things you'd want your resource classes to do.
117
+
118
+ Make sure you:
119
+
120
+ require 'resource_kit/testing'
121
+
122
+ Testing a certain action:
123
+
124
+ ```ruby
125
+ # Tag the spec with resource_kit to bring in the helpers
126
+ RSpec.describe MyResourceClass, resource_kit: true do
127
+ it 'has an all action' do
128
+ expect(MyResourceClass).to have_action(:all).that_handles(:ok, :no_content).at_path('/users')
129
+ end
130
+
131
+ it 'handles a 201 with response body' do
132
+ expect(MyResourceClass).to handle_response(:create).with(status: 201, body: '{"users":[]}') do |handled|
133
+ expect(handled).to all(be_kind_of(User))
134
+ end
135
+ end
136
+ end
137
+ ```
138
+
89
139
  ### Nice to have's
90
140
 
91
141
  Things we've thought about but just haven't implemented are:
@@ -36,7 +36,7 @@ class DropletResource < ResourceKit::Resource
36
36
  action :create do
37
37
  verb :post
38
38
  path '/v2/droplets'
39
- body {|object| DropletMapping.representation_for(:create, object) }
39
+ body { |object| DropletMapping.representation_for(:create, object) }
40
40
  handler(202) { |response| DropletMapping.extract_single(response.body, :read) }
41
41
  end
42
42
  end
@@ -14,8 +14,10 @@ module ResourceKit
14
14
  @verb
15
15
  end
16
16
 
17
- def path(path = nil)
17
+ def path(path = nil, &block)
18
+ raise "You must pass either a block or a string for paths" if path and block_given?
18
19
  @path = path if path
20
+ @path = block if block_given?
19
21
  @path
20
22
  end
21
23
 
@@ -41,5 +43,22 @@ module ResourceKit
41
43
  @body_handler = block if block_given?
42
44
  @body_handler
43
45
  end
46
+
47
+ def hooks
48
+ @hooks ||= {}
49
+ end
50
+
51
+ def before_request(method_name = nil, &block)
52
+ hooks[:before] ||= []
53
+
54
+ if block_given?
55
+ hooks[:before] << block
56
+ else
57
+ raise "Must include a method name" unless method_name
58
+ hooks[:before] << method_name
59
+ end
60
+
61
+ nil
62
+ end
44
63
  end
45
64
  end
@@ -1,21 +1,22 @@
1
1
  module ResourceKit
2
2
  class ActionInvoker
3
- attr_reader :action, :connection, :args, :options
3
+ attr_reader :action, :connection, :args, :options, :resource
4
4
 
5
- def initialize(action, connection, *args)
5
+ def initialize(action, resource, *args)
6
6
  @action = action
7
- @connection = connection
7
+ @resource = resource
8
+ @connection = resource.connection
8
9
  @args = args
9
10
  @options = args.last.kind_of?(Hash) ? args.last : {}
10
11
  end
11
12
 
12
- def self.call(action, connection, *args)
13
- new(action, connection, *args).handle_response
13
+ def self.call(action, resource, *args)
14
+ new(action, resource, *args).handle_response
14
15
  end
15
16
 
16
17
  def handle_response
17
18
  if handler = action.handlers[response.status]
18
- handler.call(response)
19
+ resource.instance_exec(response, &handler)
19
20
  else
20
21
  response.body
21
22
  end
@@ -30,18 +31,28 @@ module ResourceKit
30
31
 
31
32
  raise ArgumentError, "Verb '#{action.verb}' is not allowed" unless action.verb.in?(ALLOWED_VERBS)
32
33
 
33
- if action.body and action.verb.in?([:post, :put, :patch])
34
- # This request is going to have a response body. Handle it.
35
- @response = connection.send(action.verb, resolver.resolve(options)) do |request|
36
- request.body = construct_body
37
- end
38
- else
39
- @response = connection.send(action.verb, resolver.resolve(options))
34
+ @response = connection.send(action.verb, resolver.resolve(options)) do |request|
35
+ request.body = construct_body if action.body and action.verb.in?([:post, :put, :patch])
36
+ append_hooks(:before, request)
40
37
  end
41
38
  end
42
39
 
43
40
  def resolver
44
- EndpointResolver.new(path: action.path, query_param_keys: action.query_keys)
41
+ path = action.path.kind_of?(Proc) ? resource.instance_eval(&action.path) : action.path
42
+ EndpointResolver.new(path: path, query_param_keys: action.query_keys)
43
+ end
44
+
45
+ private
46
+
47
+ def append_hooks(hook_type, request)
48
+ (action.hooks[hook_type] || []).each do |hook|
49
+ case hook
50
+ when Proc
51
+ resource.instance_exec(*args, request, &hook)
52
+ when Symbol
53
+ resource.send(hook, *args, request)
54
+ end
55
+ end
45
56
  end
46
57
  end
47
58
  end
@@ -13,7 +13,7 @@ module ResourceKit
13
13
 
14
14
  def self.method_for_action(action, invoker)
15
15
  Proc.new do |*args|
16
- invoker.call(action, connection, *args)
16
+ invoker.call(action, self, *args)
17
17
  end
18
18
  end
19
19
  end
@@ -2,17 +2,21 @@ module ResourceKit
2
2
  class Resource
3
3
  class_attribute :_resources
4
4
 
5
- attr_reader :connection
5
+ attr_reader :connection, :scope
6
6
 
7
- def initialize(connection)
7
+ def initialize(connection: nil, scope: nil)
8
8
  @connection = connection
9
+ @scope = scope
9
10
  end
10
11
 
11
12
  def self.resources(&block)
12
13
  self._resources ||= ResourceCollection.new
13
- self._resources.instance_eval(&block) if block_given?
14
14
 
15
- MethodFactory.construct(self, self._resources)
15
+ if block_given?
16
+ self._resources.instance_eval(&block)
17
+ MethodFactory.construct(self, self._resources)
18
+ end
19
+
16
20
  self._resources
17
21
  end
18
22
 
@@ -11,7 +11,7 @@ module ResourceKit
11
11
  action = Action.new(name, *parse_verb_and_path(verb_and_path))
12
12
  action.handlers.merge!(default_handlers.dup)
13
13
  action.instance_eval(&block) if block_given?
14
- action.tap {|a| self << a }
14
+ action.tap { |a| self << a }
15
15
  end
16
16
 
17
17
  def default_handler(*response_codes, &block)
@@ -53,7 +53,7 @@ module ResourceKit
53
53
  }
54
54
 
55
55
  def self.code_for(symbol)
56
- MAP[symbol]
56
+ MAP[symbol] || symbol
57
57
  end
58
58
  end
59
59
  end
@@ -0,0 +1,34 @@
1
+ module ResourceKit
2
+ module Testing
3
+ class ActionHandlerMatchers
4
+ ResponseStub = Class.new(OpenStruct)
5
+
6
+ attr_reader :action, :response_stub
7
+
8
+ def initialize(action)
9
+ @action = action
10
+ end
11
+
12
+ def with(options, &block)
13
+ @response_stub = ResponseStub.new(options)
14
+ @handled_block = block
15
+
16
+ self
17
+ end
18
+
19
+ def matches?(subject, &block)
20
+ @handled_block ||= block
21
+ action = subject.resources.find_action(self.action)
22
+ return false unless action
23
+
24
+ status_code = response_stub.status || 200
25
+ return false unless action.handlers[status_code]
26
+
27
+ handled_response = action.handlers[status_code].call(response_stub)
28
+ @handled_block.call(handled_response)
29
+
30
+ true
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,68 @@
1
+ module ResourceKit
2
+ module Testing
3
+ class HaveActionMatchers
4
+ attr_reader :action, :path, :verb
5
+
6
+ def initialize(action)
7
+ @action = action
8
+ end
9
+
10
+ def that_handles(*codes)
11
+ codes.each do |code|
12
+ handler_codes << StatusCodeMapper.code_for(code)
13
+ end
14
+
15
+ self
16
+ end
17
+
18
+ def at_path(path)
19
+ @path = path
20
+ self
21
+ end
22
+
23
+ def with_verb(verb)
24
+ @verb = verb
25
+ self
26
+ end
27
+
28
+ def matches?(subject)
29
+ action = subject.resources.find_action(self.action)
30
+ return false unless action
31
+
32
+ %i(check_keys check_path check_verb).inject(true) do |rv, method_name|
33
+ break false unless send(method_name, action)
34
+ true
35
+ end
36
+ end
37
+
38
+ def handler_codes
39
+ @handler_codes ||= []
40
+ end
41
+
42
+ private
43
+
44
+ def check_keys(action)
45
+ keys = action.handlers.keys
46
+
47
+ if !handler_codes.empty?
48
+ handler_codes.each do |handler_code|
49
+ return false unless keys.include?(handler_code)
50
+ end
51
+ end
52
+
53
+ true
54
+ end
55
+
56
+ def check_path(action)
57
+ return true unless self.path
58
+ action.path == self.path
59
+ end
60
+
61
+ def check_verb(action)
62
+ return true unless self.verb
63
+ checked_verb = self.verb.kind_of?(String) ? self.verb.downcase.to_sym : self.verb
64
+ action.verb == checked_verb
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,20 @@
1
+ require 'resource_kit/testing/have_action_matchers'
2
+ require 'resource_kit/testing/action_handler_matchers'
3
+
4
+ module ResourceKit
5
+ module Testing
6
+ def have_action(action)
7
+ HaveActionMatchers.new(action)
8
+ end
9
+
10
+ def handle_response(action, &block)
11
+ ActionHandlerMatchers.new(action)
12
+ end
13
+ end
14
+ end
15
+
16
+ if defined?(RSpec)
17
+ RSpec.configure do |config|
18
+ config.include ResourceKit::Testing, resource_kit: true
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module ResourceKit
2
- VERSION = "0.0.5"
2
+ VERSION = "0.1.0"
3
3
  end
data/resource_kit.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = ''
13
13
  spec.homepage = "https://github.com/digitaloceancloud/resource_kit"
14
14
  spec.license = "MIT"
15
+ spec.required_ruby_version = '>= 2.0'
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0")
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -26,5 +27,6 @@ Gem::Specification.new do |spec|
26
27
  spec.add_development_dependency "rake"
27
28
  spec.add_development_dependency "rspec", "~> 3.0"
28
29
  spec.add_development_dependency "webmock", "~> 1.18.0"
29
- spec.add_development_dependency "kartograph", "~> 0"
30
+ spec.add_development_dependency "kartograph", "~> 0.0.8"
31
+ spec.add_development_dependency "pry", "~> 0.10.1"
30
32
  end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ class DummyResourceActions < ResourceKit::Resource
4
+ resources do
5
+ action :dummy, 'GET /dummy' do
6
+ handler(200) { |resp| resp.body.upcase }
7
+ end
8
+
9
+ action :headered, 'GET /headered' do
10
+ before_request { |req| req.headers['Added-Header'] = self.value }
11
+ end
12
+ end
13
+
14
+ def value
15
+ scope.value
16
+ end
17
+ end
18
+
19
+ RSpec.describe 'Resource Actions' do
20
+ let(:connection) { Faraday.new { |b| b.adapter :test, stubs } }
21
+ let(:scoped) { double('scope', value: 'bunk') }
22
+ let(:stubs) do
23
+ Faraday::Adapter::Test::Stubs.new do |stub|
24
+ stub.get('/dummy') { |env| [200, {}, 'dummies'] }
25
+ stub.get('/headered') { |env| [200, {}, env[:request_headers]['Added-Header']] }
26
+ end
27
+ end
28
+
29
+ it 'Retrieving /dummy returns the body as uppercased' do
30
+ resource = DummyResourceActions.new(connection: connection, scope: scoped)
31
+ response = resource.dummy
32
+ expect(response).to eq('DUMMIES')
33
+ end
34
+
35
+ it 'adds the header before the request happens' do
36
+ resource = DummyResourceActions.new(connection: connection, scope: scoped)
37
+ response = resource.headered
38
+
39
+ expect(response).to eq(scoped.value)
40
+ end
41
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe ResourceKit::ActionInvoker do
4
- let(:connection) { Faraday.new {|b| b.adapter :test, stubs } }
4
+ let(:connection) { Faraday.new { |b| b.adapter :test, stubs } }
5
5
  let(:stubs) do
6
6
  Faraday::Adapter::Test::Stubs.new do |stub|
7
7
  stub.get('/users') { |env| [200, {}, 'all users'] }
@@ -9,25 +9,35 @@ RSpec.describe ResourceKit::ActionInvoker do
9
9
  stub.get('/users/12') { |env| [200, {}, 'user 12'] }
10
10
  stub.post('/users') { |env| [200, {}, env[:body]] }
11
11
  stub.get('/paged') { |env| [200, {}, env[:url].to_s] }
12
+ stub.get('/before_hooks') { |env| [200, {}, env[:request_headers]['Owner-Id']] }
13
+ stub.get('/block_based') { |env| [200, {}, 'block based path'] }
12
14
  end
13
15
  end
14
16
  let(:action) { ResourceKit::Action.new(:find) }
17
+ let(:resource) { double('resource instance', connection: connection) }
15
18
 
16
19
  describe '.call' do
17
20
  it 'performs a request to the specfied URL' do
18
21
  action.verb :get
19
22
  action.path '/users'
20
23
 
21
- result = ResourceKit::ActionInvoker.call(action, connection)
24
+ result = ResourceKit::ActionInvoker.call(action, resource)
22
25
 
23
26
  expect(result).to eq('all users')
24
27
  end
25
28
 
29
+ it 'performs a request to the correct url when using a block for path' do
30
+ action.path { '/block_based' }
31
+ result = ResourceKit::ActionInvoker.call(action, resource)
32
+
33
+ expect(result).to eq('block based path')
34
+ end
35
+
26
36
  it 'substitues params on call' do
27
37
  action.verb :get
28
38
  action.path '/users/:id'
29
39
 
30
- result = ResourceKit::ActionInvoker.call(action, connection, id: 12)
40
+ result = ResourceKit::ActionInvoker.call(action, resource, id: 12)
31
41
  expect(result).to eq('user 12')
32
42
  end
33
43
 
@@ -35,9 +45,9 @@ RSpec.describe ResourceKit::ActionInvoker do
35
45
  it 'returns the handler block' do
36
46
  action.verb :get
37
47
  action.path '/users'
38
- action.handler(200) {|response| 'changed' }
48
+ action.handler(200) { |response| 'changed' }
39
49
 
40
- result = ResourceKit::ActionInvoker.call(action, connection)
50
+ result = ResourceKit::ActionInvoker.call(action, resource)
41
51
 
42
52
  expect(result).to eq('changed')
43
53
  end
@@ -45,9 +55,9 @@ RSpec.describe ResourceKit::ActionInvoker do
45
55
  it 'uses the correct handler on status codes' do
46
56
  action.verb :get
47
57
  action.path '/users/bad_page'
48
- action.handler(404) {|response| '404ed' }
58
+ action.handler(404) { |response| '404ed' }
49
59
 
50
- result = ResourceKit::ActionInvoker.call(action, connection)
60
+ result = ResourceKit::ActionInvoker.call(action, resource)
51
61
  expect(result).to eq('404ed')
52
62
  end
53
63
  end
@@ -56,18 +66,18 @@ RSpec.describe ResourceKit::ActionInvoker do
56
66
  it 'uses the body handler when present' do
57
67
  action.verb :post
58
68
  action.path '/users'
59
- action.body {|object| 'i am a banana' }
69
+ action.body { |object| 'i am a banana' }
60
70
 
61
- result = ResourceKit::ActionInvoker.call(action, connection, 'echo me')
71
+ result = ResourceKit::ActionInvoker.call(action, resource, 'echo me')
62
72
  expect(result).to eq('i am a banana')
63
73
  end
64
74
 
65
75
  it 'uses the body handler with multiple arity when present' do
66
76
  action.verb :post
67
77
  action.path '/users'
68
- action.body {|first, second| first + second }
78
+ action.body { |first, second| first + second }
69
79
 
70
- result = ResourceKit::ActionInvoker.call(action, connection, 'echo me', ' another')
80
+ result = ResourceKit::ActionInvoker.call(action, resource, 'echo me', ' another')
71
81
  expect(result).to eq('echo me another')
72
82
  end
73
83
  end
@@ -77,11 +87,61 @@ RSpec.describe ResourceKit::ActionInvoker do
77
87
  action.query_keys :per_page, :page
78
88
  action.path '/paged'
79
89
 
80
- result = ResourceKit::ActionInvoker.call(action, connection, page: 3, per_page: 300)
90
+ result = ResourceKit::ActionInvoker.call(action, resource, page: 3, per_page: 300)
81
91
  addressed = Addressable::URI.parse(result)
82
92
 
83
93
  expect(addressed.query_values).to include('per_page' => '300', 'page' => '3')
84
94
  end
85
95
  end
96
+
97
+ context 'for actions that have before request hooks' do
98
+ it 'calls the before request with the request object' do
99
+ action.path '/before_hooks'
100
+ action.verb :get
101
+ action.before_request { |req| req.headers['Owner-Id'] = 'bojangles' }
102
+
103
+ result = ResourceKit::ActionInvoker.call(action, resource)
104
+ expect(result).to eq('bojangles')
105
+ end
106
+
107
+ it 'calls the before request with the request object and arguments' do
108
+ action.path '/before_hooks'
109
+ action.verb :get
110
+ action.before_request { |one, two, req| req.headers['Owner-Id'] = "#{one} #{two}" }
111
+
112
+ result = ResourceKit::ActionInvoker.call(action, resource, 'one', 'two')
113
+ expect(result).to eq('one two')
114
+ end
115
+
116
+ context 'for passing a symbol' do
117
+ it 'calls the method on the context of the action' do
118
+ def resource.kickit(request)
119
+ request.headers['Owner-Id'] = 'btabes'
120
+ end
121
+
122
+ action.path '/before_hooks'
123
+ action.verb :get
124
+ action.before_request(:kickit)
125
+
126
+ invoker = ResourceKit::ActionInvoker.new(action, resource)
127
+
128
+ expect(invoker.handle_response).to eq('btabes')
129
+ end
130
+
131
+ it 'calls the action with arguments passed' do
132
+ def resource.kickit(one, two, request)
133
+ request.headers['Owner-Id'] = "#{one} #{two}"
134
+ end
135
+
136
+ action.path '/before_hooks'
137
+ action.verb :get
138
+ action.before_request(:kickit)
139
+
140
+ invoker = ResourceKit::ActionInvoker.new(action, resource, 'bobby', 'tables')
141
+
142
+ expect(invoker.handle_response).to eq('bobby tables')
143
+ end
144
+ end
145
+ end
86
146
  end
87
147
  end
@@ -24,6 +24,14 @@ RSpec.describe ResourceKit::Action do
24
24
  action.path '/something/sammy'
25
25
  expect(action.path).to eq('/something/sammy')
26
26
  end
27
+
28
+ it 'sets the path to a block when passed' do
29
+ action = described_class.new(:find)
30
+ proc = Proc.new { '/users/:id/comments' }
31
+ action.path(&proc)
32
+
33
+ expect(action.path).to be(proc)
34
+ end
27
35
  end
28
36
 
29
37
  describe '#handler' do
@@ -46,7 +54,7 @@ RSpec.describe ResourceKit::Action do
46
54
 
47
55
  describe '#body' do
48
56
  it 'stores a proc for handling requests with bodies' do
49
- handler = Proc.new {|object| 'whut whut' }
57
+ handler = Proc.new { |object| 'whut whut' }
50
58
  action.body(&handler)
51
59
 
52
60
  expect(action.body).to be(handler)
@@ -59,4 +67,21 @@ RSpec.describe ResourceKit::Action do
59
67
  expect(action.query_keys).to include(:per_page, :page)
60
68
  end
61
69
  end
70
+
71
+ describe '#before_request' do
72
+ context 'with a block' do
73
+ it 'sets a block to happen before the request happens' do
74
+ proc = Proc.new {}
75
+ action.before_request(&proc)
76
+ expect(action.hooks[:before]).to include(proc)
77
+ end
78
+ end
79
+
80
+ context 'with a symbol' do
81
+ it 'adds the symbol to the before hooks' do
82
+ action.before_request(:foo)
83
+ expect(action.hooks[:before]).to include(:foo)
84
+ end
85
+ end
86
+ end
62
87
  end
@@ -44,7 +44,7 @@ RSpec.describe ResourceKit::MethodFactory do
44
44
  instance = klass.new
45
45
  instance.connection = double('connection')
46
46
  instance.bunk('something', 'something else')
47
- expect(invoker).to have_received(:call).with(action, instance.connection, 'something', 'something else')
47
+ expect(invoker).to have_received(:call).with(action, instance, 'something', 'something else')
48
48
  end
49
49
  end
50
50
  end
@@ -5,7 +5,7 @@ RSpec.describe ResourceKit::ResourceCollection do
5
5
 
6
6
  describe '#default_handler' do
7
7
  it 'adds the passed black to a hash of handlers on the resource collection' do
8
- handler_block = Proc.new {|b| 'whut whut' }
8
+ handler_block = Proc.new { |b| 'whut whut' }
9
9
  collection.default_handler(:ok, :no_content, &handler_block)
10
10
 
11
11
  expect(collection.default_handlers[200]).to eq(handler_block)
@@ -15,7 +15,7 @@ RSpec.describe ResourceKit::ResourceCollection do
15
15
 
16
16
  describe '#action' do
17
17
  it 'yields an action to the block' do
18
- expect {|b| collection.action(:all, &b) }.to yield_with_args(instance_of(ResourceKit::Action))
18
+ expect { |b| collection.action(:all, &b) }.to yield_with_args(instance_of(ResourceKit::Action))
19
19
  end
20
20
 
21
21
  it 'adds the action to the collection' do
@@ -31,7 +31,7 @@ RSpec.describe ResourceKit::ResourceCollection do
31
31
  end
32
32
 
33
33
  context 'when default handlers have been specified on the collection' do
34
- let(:handler) { Proc.new {|response| 'sure' } }
34
+ let(:handler) { Proc.new { |response| 'sure' } }
35
35
 
36
36
  before { collection.default_handler(:ok, &handler) }
37
37
 
@@ -20,7 +20,7 @@ RSpec.describe ResourceKit::Resource do
20
20
  end
21
21
  end
22
22
 
23
- subject(:droplet_resource) { DropletResource.new(double) }
23
+ subject(:droplet_resource) { DropletResource.new }
24
24
 
25
25
  it "defines the action method" do
26
26
  expect(droplet_resource).to respond_to(:find)
@@ -31,10 +31,20 @@ RSpec.describe ResourceKit::Resource do
31
31
  describe '#initialize' do
32
32
  it 'initializes with a connection' do
33
33
  faraday = Faraday.new(url: 'http://lol.com')
34
- instance = ResourceKit::Resource.new(faraday)
34
+ instance = ResourceKit::Resource.new(connection: faraday)
35
35
 
36
36
  expect(instance.connection).to be(faraday)
37
37
  end
38
+
39
+ it 'initializes with an optional scope object' do
40
+ connection = double('conn')
41
+ scope = double('scope')
42
+
43
+ instance = ResourceKit::Resource.new(connection: connection, scope: scope)
44
+
45
+ expect(instance.connection).to be(connection)
46
+ expect(instance.scope).to be(scope)
47
+ end
38
48
  end
39
49
 
40
50
  describe '#action' do
@@ -47,7 +57,7 @@ RSpec.describe ResourceKit::Resource do
47
57
  end
48
58
  end
49
59
 
50
- instance = DummyResource.new(faraday)
60
+ instance = DummyResource.new(connection: faraday)
51
61
 
52
62
  expect(instance.action(:find)).to be_kind_of(ResourceKit::Action)
53
63
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ RSpec.describe ResourceKit::Testing::ActionHandlerMatchers do
5
+ let(:resource_class) { Class.new(ResourceKit::Resource) }
6
+ subject(:matcher) { described_class.new(:all) }
7
+
8
+ describe '#initialize' do
9
+ it 'initializes with a resource class' do
10
+ action = :all
11
+ instance = described_class.new(action)
12
+ expect(instance.action).to be(action)
13
+ end
14
+ end
15
+
16
+ describe '#with' do
17
+ it 'sets the passed response params' do
18
+ matcher.with(status: 200, body: 'Hello World')
19
+ expect(matcher.response_stub.status).to eq(200)
20
+ expect(matcher.response_stub.body).to eq('Hello World')
21
+ end
22
+ end
23
+
24
+ describe '#matches?' do
25
+ it 'matches when the resource handles the block correctly' do
26
+ resource_class.resources.action :all, 'GET /all' do
27
+ handler(200) { |response| JSON.load(response.body) }
28
+ end
29
+
30
+ change_var = nil
31
+ matcher.with(status: 200, body: '{"hello": "world"}') do |handled|
32
+ expect(handled['hello']).to eq('world')
33
+ change_var = true
34
+ end
35
+
36
+ expect(matcher.matches?(resource_class)).to be_truthy
37
+ expect(change_var).to be(true)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ResourceKit::Testing::HaveActionMatchers do
4
+ describe '#initialize' do
5
+ it 'initializes with a resource class' do
6
+ action = :all
7
+ instance = described_class.new(action)
8
+ expect(instance.action).to be(action)
9
+ end
10
+ end
11
+
12
+ describe '#that_handles' do
13
+ subject(:testing_matcher) { described_class.new(:all) }
14
+
15
+ it 'returns itself' do
16
+ expect(testing_matcher.that_handles(200)).to be(testing_matcher)
17
+ end
18
+
19
+ it 'accepts a status code' do
20
+ testing_matcher.that_handles(200)
21
+ expect(testing_matcher.handler_codes).to include(200)
22
+ end
23
+
24
+ it 'accepts a magic status code' do
25
+ testing_matcher.that_handles(:ok)
26
+ expect(testing_matcher.handler_codes).to include(200)
27
+ end
28
+
29
+ it 'accepts multiple status codes' do
30
+ testing_matcher.that_handles(:ok, 201)
31
+ expect(testing_matcher.handler_codes).to include(200, 201)
32
+ end
33
+ end
34
+
35
+ describe '#at_path' do
36
+ subject(:testing_matcher) { described_class.new(:all) }
37
+
38
+ it 'sets the path we\'re specing' do
39
+ testing_matcher.at_path('/hello')
40
+ expect(testing_matcher.path).to eq('/hello')
41
+ end
42
+
43
+ it 'returns itself' do
44
+ expect(testing_matcher.at_path('/')).to be(testing_matcher)
45
+ end
46
+ end
47
+
48
+ describe '#with_verb' do
49
+ subject(:testing_matcher) { described_class.new(:all) }
50
+
51
+ it 'sets the verb to test for' do
52
+ testing_matcher.with_verb('POST')
53
+ expect(testing_matcher.verb).to eq('POST')
54
+ end
55
+ end
56
+
57
+ describe '#matches?' do
58
+ subject(:testing_matcher) { described_class.new(:all) }
59
+
60
+ context 'for a resource that has the defined action' do
61
+ let(:resource_class) { Class.new(ResourceKit::Resource) }
62
+
63
+ it 'matches with both code and path' do
64
+ resource_class.resources do
65
+ action :all, 'GET /hello' do
66
+ handler(200) { }
67
+ end
68
+ end
69
+
70
+ matcher = described_class.new(:all)
71
+ matcher.that_handles(:ok).at_path('/hello').with_verb('GET')
72
+
73
+ expect(matcher.matches?(resource_class)).to be_truthy
74
+ end
75
+
76
+ it 'matches with only code' do
77
+ resource_class.resources do
78
+ action :all, 'GET /hello' do
79
+ handler(200) { }
80
+ end
81
+ end
82
+
83
+ matcher = described_class.new(:all)
84
+ matcher.that_handles(:ok)
85
+
86
+ expect(matcher.matches?(resource_class)).to be_truthy
87
+ end
88
+
89
+ it 'matches with only path' do
90
+ resource_class.resources { action :all, 'GET /hello' }
91
+
92
+ matcher = described_class.new(:all)
93
+ matcher.at_path('/hello')
94
+
95
+ expect(matcher.matches?(resource_class)).to be_truthy
96
+ end
97
+
98
+ it 'matches with only the verb' do
99
+ resource_class.resources { action :all, 'GET /hello' }
100
+
101
+ matcher = described_class.new(:all)
102
+ matcher.with_verb('GET')
103
+
104
+ expect(matcher.matches?(resource_class)).to be_truthy
105
+ end
106
+ end
107
+ end
108
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'resource_kit'
2
+ require 'resource_kit/testing'
3
+ require 'pry'
2
4
 
3
5
  RSpec.configure do |config|
4
6
  config.order = :random
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resource_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Ross
@@ -9,120 +9,134 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-08-06 00:00:00.000000000 Z
12
+ date: 2014-08-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - '>='
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
20
  version: '0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - '>='
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: activesupport
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - '>='
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
34
  version: '3.0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - '>='
39
+ - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '3.0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: addressable
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - ~>
46
+ - - "~>"
47
47
  - !ruby/object:Gem::Version
48
48
  version: 2.3.6
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - ~>
53
+ - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: 2.3.6
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: bundler
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ~>
60
+ - - "~>"
61
61
  - !ruby/object:Gem::Version
62
62
  version: '1.6'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ~>
67
+ - - "~>"
68
68
  - !ruby/object:Gem::Version
69
69
  version: '1.6'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rake
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - '>='
74
+ - - ">="
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - '>='
81
+ - - ">="
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: rspec
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - ~>
88
+ - - "~>"
89
89
  - !ruby/object:Gem::Version
90
90
  version: '3.0'
91
91
  type: :development
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - ~>
95
+ - - "~>"
96
96
  - !ruby/object:Gem::Version
97
97
  version: '3.0'
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: webmock
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - ~>
102
+ - - "~>"
103
103
  - !ruby/object:Gem::Version
104
104
  version: 1.18.0
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - ~>
109
+ - - "~>"
110
110
  - !ruby/object:Gem::Version
111
111
  version: 1.18.0
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: kartograph
114
114
  requirement: !ruby/object:Gem::Requirement
115
115
  requirements:
116
- - - ~>
116
+ - - "~>"
117
117
  - !ruby/object:Gem::Version
118
- version: '0'
118
+ version: 0.0.8
119
119
  type: :development
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
- - - ~>
123
+ - - "~>"
124
124
  - !ruby/object:Gem::Version
125
- version: '0'
125
+ version: 0.0.8
126
+ - !ruby/object:Gem::Dependency
127
+ name: pry
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: 0.10.1
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: 0.10.1
126
140
  description: ''
127
141
  email:
128
142
  - rross@digitalocean.com
@@ -131,8 +145,8 @@ executables: []
131
145
  extensions: []
132
146
  extra_rdoc_files: []
133
147
  files:
134
- - .gitignore
135
- - .rspec
148
+ - ".gitignore"
149
+ - ".rspec"
136
150
  - Gemfile
137
151
  - LICENSE.txt
138
152
  - README.md
@@ -146,8 +160,12 @@ files:
146
160
  - lib/resource_kit/resource.rb
147
161
  - lib/resource_kit/resource_collection.rb
148
162
  - lib/resource_kit/status_code_mapper.rb
163
+ - lib/resource_kit/testing.rb
164
+ - lib/resource_kit/testing/action_handler_matchers.rb
165
+ - lib/resource_kit/testing/have_action_matchers.rb
149
166
  - lib/resource_kit/version.rb
150
167
  - resource_kit.gemspec
168
+ - spec/integration/resource_actions_spec.rb
151
169
  - spec/lib/resource_kit/action_invoker_spec.rb
152
170
  - spec/lib/resource_kit/action_spec.rb
153
171
  - spec/lib/resource_kit/endpoint_resolver_spec.rb
@@ -155,6 +173,8 @@ files:
155
173
  - spec/lib/resource_kit/resource_collection_spec.rb
156
174
  - spec/lib/resource_kit/resource_spec.rb
157
175
  - spec/lib/resource_kit/status_code_mapper_spec.rb
176
+ - spec/lib/resource_kit/testing/action_handler_matchers_spec.rb
177
+ - spec/lib/resource_kit/testing/have_action_matchers_spec.rb
158
178
  - spec/spec_helper.rb
159
179
  homepage: https://github.com/digitaloceancloud/resource_kit
160
180
  licenses:
@@ -166,12 +186,12 @@ require_paths:
166
186
  - lib
167
187
  required_ruby_version: !ruby/object:Gem::Requirement
168
188
  requirements:
169
- - - '>='
189
+ - - ">="
170
190
  - !ruby/object:Gem::Version
171
- version: '0'
191
+ version: '2.0'
172
192
  required_rubygems_version: !ruby/object:Gem::Requirement
173
193
  requirements:
174
- - - '>='
194
+ - - ">="
175
195
  - !ruby/object:Gem::Version
176
196
  version: '0'
177
197
  requirements: []
@@ -182,6 +202,7 @@ specification_version: 4
182
202
  summary: Resource Kit provides tools to aid in making API Clients. Such as URL resolving,
183
203
  Request / Response layer, and more.
184
204
  test_files:
205
+ - spec/integration/resource_actions_spec.rb
185
206
  - spec/lib/resource_kit/action_invoker_spec.rb
186
207
  - spec/lib/resource_kit/action_spec.rb
187
208
  - spec/lib/resource_kit/endpoint_resolver_spec.rb
@@ -189,4 +210,6 @@ test_files:
189
210
  - spec/lib/resource_kit/resource_collection_spec.rb
190
211
  - spec/lib/resource_kit/resource_spec.rb
191
212
  - spec/lib/resource_kit/status_code_mapper_spec.rb
213
+ - spec/lib/resource_kit/testing/action_handler_matchers_spec.rb
214
+ - spec/lib/resource_kit/testing/have_action_matchers_spec.rb
192
215
  - spec/spec_helper.rb