resource_set 1.0.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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +12 -0
  5. data/CHANGELOG.md +11 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +165 -0
  9. data/Rakefile +5 -0
  10. data/examples/digitalocean_droplets.rb +60 -0
  11. data/examples/httpbin_client.rb +15 -0
  12. data/lib/resource_set/action.rb +66 -0
  13. data/lib/resource_set/action_invoker.rb +58 -0
  14. data/lib/resource_set/endpoint_resolver.rb +46 -0
  15. data/lib/resource_set/inheritable_attribute.rb +20 -0
  16. data/lib/resource_set/method_factory.rb +20 -0
  17. data/lib/resource_set/resource.rb +40 -0
  18. data/lib/resource_set/resource_collection.rb +55 -0
  19. data/lib/resource_set/status_code_mapper.rb +59 -0
  20. data/lib/resource_set/testing/action_handler_matchers.rb +42 -0
  21. data/lib/resource_set/testing/have_action_matchers.rb +85 -0
  22. data/lib/resource_set/testing.rb +20 -0
  23. data/lib/resource_set/version.rb +3 -0
  24. data/lib/resource_set.rb +17 -0
  25. data/resource_set.gemspec +30 -0
  26. data/spec/integration/resource_actions_spec.rb +41 -0
  27. data/spec/lib/resource_set/action_invoker_spec.rb +167 -0
  28. data/spec/lib/resource_set/action_spec.rb +87 -0
  29. data/spec/lib/resource_set/endpoint_resolver_spec.rb +60 -0
  30. data/spec/lib/resource_set/inheritable_attribute_spec.rb +54 -0
  31. data/spec/lib/resource_set/method_factory_spec.rb +50 -0
  32. data/spec/lib/resource_set/resource_collection_spec.rb +67 -0
  33. data/spec/lib/resource_set/resource_spec.rb +66 -0
  34. data/spec/lib/resource_set/status_code_mapper_spec.rb +9 -0
  35. data/spec/lib/resource_set/testing/action_handler_matchers_spec.rb +68 -0
  36. data/spec/lib/resource_set/testing/have_action_matchers_spec.rb +157 -0
  37. data/spec/spec_helper.rb +8 -0
  38. metadata +202 -0
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ResourceSet::ResourceCollection do
4
+ subject(:collection) { ResourceSet::ResourceCollection.new }
5
+
6
+ describe '#default_handler' do
7
+ it 'adds the passed black to a hash of handlers on the resource collection' do
8
+ handler_block = Proc.new { |b| 'whut whut' }
9
+ collection.default_handler(:ok, :no_content, &handler_block)
10
+
11
+ expect(collection.default_handlers[200]).to eq(handler_block)
12
+ expect(collection.default_handlers[204]).to eq(handler_block)
13
+ end
14
+
15
+ it 'provides a top-level default handler if no status code is provided' do
16
+ handler_block = Proc.new { true }
17
+ collection.default_handler(&handler_block)
18
+
19
+ expect(collection.default_handlers[:any]).to eq(handler_block)
20
+ end
21
+ end
22
+
23
+ describe '#action' do
24
+ it 'yields an action to the block' do
25
+ expect { |b| collection.action(:all, &b) }.to yield_with_args(instance_of(ResourceSet::Action))
26
+ end
27
+
28
+ it 'adds the action to the collection' do
29
+ action = collection.action :all
30
+ expect(collection).to include(action)
31
+ end
32
+
33
+ it "accepts a second argument of VERB /resource" do
34
+ action = collection.action :all, 'GET /v2/droplets'
35
+ expect(action.verb).to eq :get
36
+ expect(action.path).to eq '/v2/droplets'
37
+ expect(action.name).to eq :all
38
+ end
39
+
40
+ context 'when default handlers have been specified on the collection' do
41
+ let(:handler) { Proc.new { |response| 'sure' } }
42
+ let(:default_handler) { Proc.new { 'Something unexpected happened!' } }
43
+
44
+ before do
45
+ collection.default_handler(:ok, &handler)
46
+ collection.default_handler(&default_handler)
47
+ end
48
+
49
+ it 'prepends the default handlers to the test' do
50
+ action = collection.action(:all)
51
+ expect(action.handlers[200]).to eq(handler)
52
+ expect(action.handlers[:any]).to eq(default_handler)
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '#find_action' do
58
+ it 'returns the action with the name passed' do
59
+ collection.action(:all)
60
+
61
+ retrieved_action = collection.find_action(:all)
62
+
63
+ expect(retrieved_action).to be_kind_of(ResourceSet::Action)
64
+ expect(retrieved_action.name).to eq(:all)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ResourceSet::Resource do
4
+ describe '.resources' do
5
+ subject(:resource) { Class.new(described_class) }
6
+
7
+ it 'returns a resource collection' do
8
+ expect(resource.resources).to be_kind_of(ResourceSet::ResourceCollection)
9
+ end
10
+
11
+ it 'yields a resource collection' do
12
+ expect { |b| resource.resources(&b) }.to yield_with_args(instance_of(ResourceSet::ResourceCollection))
13
+ end
14
+
15
+ context 'action methods' do
16
+ class DropletResource < described_class
17
+ resources do
18
+ action :find
19
+ action :all
20
+ end
21
+ end
22
+
23
+ subject(:droplet_resource) { DropletResource.new }
24
+
25
+ it "defines the action method" do
26
+ expect(droplet_resource).to respond_to(:find)
27
+ expect(droplet_resource).to respond_to(:all)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#initialize' do
33
+ it 'initializes with a connection' do
34
+ faraday = Faraday.new(url: 'http://lol.com')
35
+ instance = ResourceSet::Resource.new(connection: faraday)
36
+
37
+ expect(instance.connection).to be(faraday)
38
+ end
39
+
40
+ it 'initializes with an optional scope object' do
41
+ connection = double('conn')
42
+ scope = double('scope')
43
+
44
+ instance = ResourceSet::Resource.new(connection: connection, scope: scope)
45
+
46
+ expect(instance.connection).to be(connection)
47
+ expect(instance.scope).to be(scope)
48
+ end
49
+ end
50
+
51
+ describe '#action' do
52
+ it 'returns the action for the name passed' do
53
+ faraday = Faraday.new(url: 'http://lol.com')
54
+
55
+ class DummyResource < described_class
56
+ resources do
57
+ action :find, 'GET /hello'
58
+ end
59
+ end
60
+
61
+ instance = DummyResource.new(connection: faraday)
62
+
63
+ expect(instance.action(:find)).to be_kind_of(ResourceSet::Action)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ResourceSet::StatusCodeMapper do
4
+ describe '.code_for' do
5
+ it 'returns the status code for a symbol' do
6
+ expect(described_class.code_for(:ok)).to eq(200)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ RSpec.describe ResourceSet::Testing::ActionHandlerMatchers do
5
+ let(:resource_class) { Class.new(ResourceSet::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
+
41
+ describe '#failure_message' do
42
+ context "when the matchers doesnt handle the same status code" do
43
+ it 'returns "expected the #{status_code} status code to be handled by the class."' do
44
+ resource_class.resources.action :all, 'GET /all' do
45
+ handler(200) { |response| JSON.load(response.body) }
46
+ end
47
+
48
+ matcher.with(status: 201, body: '{"Hello": "World"}')
49
+ matcher.matches?(resource_class) { }
50
+
51
+ expect(matcher.failure_message).to eq('expected the 201 status code to be handled by the class.')
52
+ end
53
+ end
54
+
55
+ context "when the matchers doesnt handle the same status code" do
56
+ it 'returns "expected the #{status_code} status code to be handled by the class."' do
57
+ resource_class.resources.action :show, 'GET /all' do
58
+ handler(200) { |response| JSON.load(response.body) }
59
+ end
60
+
61
+ matcher.with(status: 200, body: '{"Hello": "World"}')
62
+ matcher.matches?(resource_class) { }
63
+
64
+ expect(matcher.failure_message).to eq('expected :all to be handled by the class.')
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ResourceSet::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(ResourceSet::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
+
109
+ describe '#failure_message' do
110
+ let(:resource_class) {
111
+ Class.new(ResourceSet::Resource) { |resource|
112
+ resource.resources {
113
+ action :all, 'POST valid_path' do
114
+ handler(201)
115
+ end
116
+ }
117
+ }
118
+ }
119
+
120
+ context 'when the matcher doesnt find the action' do
121
+ it 'returns "expected class to have action #{action}".' do
122
+ matcher = described_class.new(:all)
123
+ expect(matcher.failure_message).to eq('expected class to have action all.')
124
+ end
125
+ end
126
+
127
+ context 'when the matcher doesnt find the path' do
128
+ it 'returns "expected #{expected} path, got #{gotten_value} instead".' do
129
+ matcher = described_class.new(:all)
130
+ matcher.at_path('invalid_path')
131
+ matcher.matches?(resource_class)
132
+
133
+ expect(matcher.failure_message).to eq('expected invalid_path path, got valid_path instead.')
134
+ end
135
+ end
136
+
137
+ context 'when the matcher doesnt find the status code' do
138
+ it 'returns "expected #{expected} status_code, got #{gotten_value} instead".' do
139
+ matcher = described_class.new(:all)
140
+ matcher.that_handles(:ok)
141
+ matcher.matches?(resource_class)
142
+
143
+ expect(matcher.failure_message).to eq('expected 200 status_code, got [201] instead.')
144
+ end
145
+ end
146
+
147
+ context 'when the matcher doesnt find the verb code' do
148
+ it 'returns "expected #{expected} verb, got #{gotten_value} instead".' do
149
+ matcher = described_class.new(:all)
150
+ matcher.with_verb(:get)
151
+ matcher.matches?(resource_class)
152
+
153
+ expect(matcher.failure_message).to eq('expected get verb, got post instead.')
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,8 @@
1
+ require 'resource_set'
2
+ require 'resource_set/testing'
3
+ require 'pry'
4
+ require 'faraday'
5
+
6
+ RSpec.configure do |config|
7
+ config.order = :random
8
+ end
metadata ADDED
@@ -0,0 +1,202 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resource_set
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert Ross
8
+ - Ivan Vanderbyl
9
+ - Kyrylo Silin
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2017-09-07 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: addressable
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 2.3.6
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: 3.0.0
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 2.3.6
35
+ - !ruby/object:Gem::Dependency
36
+ name: rake
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '12.0'
42
+ type: :development
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '12.0'
49
+ - !ruby/object:Gem::Dependency
50
+ name: faraday
51
+ requirement: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: rspec
65
+ requirement: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.6'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '3.6'
77
+ - !ruby/object:Gem::Dependency
78
+ name: webmock
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '3.0'
84
+ type: :development
85
+ prerelease: false
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '3.0'
91
+ - !ruby/object:Gem::Dependency
92
+ name: cartograph
93
+ requirement: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.0'
98
+ type: :development
99
+ prerelease: false
100
+ version_requirements: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '1.0'
105
+ - !ruby/object:Gem::Dependency
106
+ name: pry
107
+ requirement: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ description: ''
120
+ email:
121
+ - engineering@digitalocean.com
122
+ - rross@digitalocean.com
123
+ - ivan@digitalocean.com
124
+ - silin@kyrylo.org
125
+ executables: []
126
+ extensions: []
127
+ extra_rdoc_files: []
128
+ files:
129
+ - ".gitignore"
130
+ - ".rspec"
131
+ - ".travis.yml"
132
+ - CHANGELOG.md
133
+ - Gemfile
134
+ - LICENSE.txt
135
+ - README.md
136
+ - Rakefile
137
+ - examples/digitalocean_droplets.rb
138
+ - examples/httpbin_client.rb
139
+ - lib/resource_set.rb
140
+ - lib/resource_set/action.rb
141
+ - lib/resource_set/action_invoker.rb
142
+ - lib/resource_set/endpoint_resolver.rb
143
+ - lib/resource_set/inheritable_attribute.rb
144
+ - lib/resource_set/method_factory.rb
145
+ - lib/resource_set/resource.rb
146
+ - lib/resource_set/resource_collection.rb
147
+ - lib/resource_set/status_code_mapper.rb
148
+ - lib/resource_set/testing.rb
149
+ - lib/resource_set/testing/action_handler_matchers.rb
150
+ - lib/resource_set/testing/have_action_matchers.rb
151
+ - lib/resource_set/version.rb
152
+ - resource_set.gemspec
153
+ - spec/integration/resource_actions_spec.rb
154
+ - spec/lib/resource_set/action_invoker_spec.rb
155
+ - spec/lib/resource_set/action_spec.rb
156
+ - spec/lib/resource_set/endpoint_resolver_spec.rb
157
+ - spec/lib/resource_set/inheritable_attribute_spec.rb
158
+ - spec/lib/resource_set/method_factory_spec.rb
159
+ - spec/lib/resource_set/resource_collection_spec.rb
160
+ - spec/lib/resource_set/resource_spec.rb
161
+ - spec/lib/resource_set/status_code_mapper_spec.rb
162
+ - spec/lib/resource_set/testing/action_handler_matchers_spec.rb
163
+ - spec/lib/resource_set/testing/have_action_matchers_spec.rb
164
+ - spec/spec_helper.rb
165
+ homepage: https://github.com/kyrylo/resource_set
166
+ licenses:
167
+ - MIT
168
+ metadata: {}
169
+ post_install_message:
170
+ rdoc_options: []
171
+ require_paths:
172
+ - lib
173
+ required_ruby_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '2.0'
178
+ required_rubygems_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ requirements: []
184
+ rubyforge_project:
185
+ rubygems_version: 2.6.8
186
+ signing_key:
187
+ specification_version: 4
188
+ summary: Resource Set provides tools to aid in making API Clients. Such as URL resolving,
189
+ Request / Response layer, and more.
190
+ test_files:
191
+ - spec/integration/resource_actions_spec.rb
192
+ - spec/lib/resource_set/action_invoker_spec.rb
193
+ - spec/lib/resource_set/action_spec.rb
194
+ - spec/lib/resource_set/endpoint_resolver_spec.rb
195
+ - spec/lib/resource_set/inheritable_attribute_spec.rb
196
+ - spec/lib/resource_set/method_factory_spec.rb
197
+ - spec/lib/resource_set/resource_collection_spec.rb
198
+ - spec/lib/resource_set/resource_spec.rb
199
+ - spec/lib/resource_set/status_code_mapper_spec.rb
200
+ - spec/lib/resource_set/testing/action_handler_matchers_spec.rb
201
+ - spec/lib/resource_set/testing/have_action_matchers_spec.rb
202
+ - spec/spec_helper.rb