resource_kit 0.0.5 → 0.1.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: 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