api-resource 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/api-resource/resource.rb +16 -13
- data/lib/api-resource/version.rb +1 -1
- data/spec/resource_spec.rb +65 -48
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fb25fa5e1a25eec2eca37948e5c8638d2ac17b2
|
4
|
+
data.tar.gz: 112ad65c7f8b3d611ac2e8e1a5781adc82736efa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4b7f563692b808b8120f3e88d5ca01b100601b354a869b9265fcdd84cf2b7661d8f2292b9950fb21bff0f06b9b16d804978e54e7966e80886ec629ae42f7edb
|
7
|
+
data.tar.gz: 7ebc0f1b6768180cac7fa13465e56d7acbfa20f70dc1a1d7bbc0ec6d94ea26403ba5063fd7c1e7de261f28da1a1f4b9cba1917cdb654b2cf9489151965672652
|
@@ -6,6 +6,7 @@ require 'active_model/model'
|
|
6
6
|
require 'active_model/serializers/json'
|
7
7
|
require 'active_support/core_ext/hash/indifferent_access'
|
8
8
|
require 'active_support/core_ext/object/to_param'
|
9
|
+
require 'active_support/core_ext/module/remove_method'
|
9
10
|
|
10
11
|
module ApiResource
|
11
12
|
|
@@ -40,7 +41,7 @@ module ApiResource
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def self.resource_path
|
43
|
-
@resource_path || name.to_s.tableize
|
44
|
+
@resource_path || name.to_s.tableize.split('/').last
|
44
45
|
end
|
45
46
|
|
46
47
|
def self.resource_path=(path)
|
@@ -148,20 +149,21 @@ module ApiResource
|
|
148
149
|
# define method for associated embedded resource(s)
|
149
150
|
if value.is_a?(Hash)
|
150
151
|
meta, data = value['meta'], value['data'] || value
|
151
|
-
elsif value.is_a?(Array)
|
152
|
-
meta, data = nil, value
|
153
152
|
else
|
154
|
-
|
153
|
+
meta, data = nil, value
|
155
154
|
end
|
155
|
+
|
156
156
|
child_class = self.class.load_class((meta && meta['type']) || key)
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
157
|
+
child_value = if child_class && data.is_a?(Hash)
|
158
|
+
child_class.new(data)
|
159
|
+
elsif child_class && data.is_a?(Array)
|
160
|
+
ResourceCollection.new(data, meta, child_class)
|
161
|
+
else
|
162
|
+
data
|
163
|
+
end
|
164
|
+
|
165
|
+
self.class.remove_possible_method(key)
|
166
|
+
define_singleton_method(key) { child_value }
|
165
167
|
end
|
166
168
|
end
|
167
169
|
end
|
@@ -177,7 +179,7 @@ module ApiResource
|
|
177
179
|
|
178
180
|
def self.with_parent_resource(parent_resource_id, parent_resource_name)
|
179
181
|
parent_resource_name = parent_resource_name.pluralize
|
180
|
-
child_resource_path
|
182
|
+
child_resource_path = resource_path
|
181
183
|
Class.new(self) do
|
182
184
|
self.resource_path = build_url(parent_resource_name, parent_resource_id, child_resource_path)
|
183
185
|
end
|
@@ -189,6 +191,7 @@ module ApiResource
|
|
189
191
|
end
|
190
192
|
|
191
193
|
def self.load_class(tableized_class)
|
194
|
+
tableized_class = name.to_s.tableize.split('/')[0...-1].push(tableized_class).join('/')
|
192
195
|
klass = tableized_class && tableized_class.to_s.singularize.classify.constantize rescue nil
|
193
196
|
klass if klass && klass < ApiResource::Resource
|
194
197
|
end
|
data/lib/api-resource/version.rb
CHANGED
data/spec/resource_spec.rb
CHANGED
@@ -1,19 +1,22 @@
|
|
1
1
|
RSpec.describe ApiResource::Resource do
|
2
|
+
module BlogApi
|
3
|
+
class Resource < ApiResource::Resource
|
4
|
+
self.base_url = 'http://api.example.com'
|
5
|
+
end
|
2
6
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
class Category < BlogResource
|
8
|
-
attr_accessor :id, :name
|
9
|
-
end
|
7
|
+
class Category < Resource
|
8
|
+
attr_accessor :id, :name
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
class Blog < Resource
|
12
|
+
attr_accessor :id, :title
|
13
|
+
def do_stuff_method
|
14
|
+
end
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
class Tag < Resource
|
18
|
+
attr_accessor :id, :name
|
19
|
+
end
|
17
20
|
end
|
18
21
|
|
19
22
|
def check_returned_array(type, expected, actual)
|
@@ -24,11 +27,11 @@ RSpec.describe ApiResource::Resource do
|
|
24
27
|
|
25
28
|
def check_returned_object(type, expected, actual, *except)
|
26
29
|
expect(actual).to be_a(type)
|
27
|
-
expect(actual.attributes).to match((expected[:data] || expected).except(*except))
|
30
|
+
expect(actual.try(:attributes) || actual).to match((expected[:data] || expected).except(*except))
|
28
31
|
end
|
29
32
|
|
30
33
|
def req(verb, path, resource, status=200, params=nil)
|
31
|
-
stub_request(verb, "#{
|
34
|
+
stub_request(verb, "#{BlogApi::Resource.base_url}#{path}").
|
32
35
|
with(headers: { 'Accept' => 'application/json' }, body: params).
|
33
36
|
to_return(status: status, body: resource.is_a?(String) || resource.blank? ? resource : resource.to_json)
|
34
37
|
end
|
@@ -40,18 +43,18 @@ RSpec.describe ApiResource::Resource do
|
|
40
43
|
end
|
41
44
|
|
42
45
|
context '::all_pages' do
|
43
|
-
it '
|
46
|
+
it 'retrieves all pages' do
|
44
47
|
expected_values = (1..223).map { |i| { id: i, title: "Hello #{i}" } }.each_slice(100).to_a
|
45
48
|
expected_values.each_with_index do |objs, i|
|
46
49
|
req(:get, "/blogs?page=#{i+1}&per_page=100", { data: objs, meta: { total_count: 223 } })
|
47
50
|
end
|
48
51
|
|
49
52
|
i = 1
|
50
|
-
Blog.all_pages(per_page: 100) do |elts, page, per_page, total_count|
|
53
|
+
BlogApi::Blog.all_pages(per_page: 100) do |elts, page, per_page, total_count|
|
51
54
|
expect(total_count).to eq(223)
|
52
55
|
expect(page).to eq(i)
|
53
56
|
expect(per_page).to eq(100)
|
54
|
-
check_returned_array(Blog, expected_values[i-1], elts)
|
57
|
+
check_returned_array(BlogApi::Blog, expected_values[i-1], elts)
|
55
58
|
i += 1
|
56
59
|
false
|
57
60
|
end
|
@@ -63,8 +66,8 @@ RSpec.describe ApiResource::Resource do
|
|
63
66
|
def check_found_resource(expected_value)
|
64
67
|
resource_id = 3
|
65
68
|
req(:get, "/blogs/#{resource_id}", expected_value)
|
66
|
-
result = Blog.find(resource_id)
|
67
|
-
check_returned_object(Blog, expected_value, result)
|
69
|
+
result = BlogApi::Blog.find(resource_id)
|
70
|
+
check_returned_object(BlogApi::Blog, expected_value, result)
|
68
71
|
end
|
69
72
|
|
70
73
|
it 'creates resource with data rooted json' do
|
@@ -79,8 +82,8 @@ RSpec.describe ApiResource::Resource do
|
|
79
82
|
|
80
83
|
it 'fetches all resources' do
|
81
84
|
req(:get, '/blogs', @expected_resources)
|
82
|
-
result = Blog.all
|
83
|
-
check_returned_array(Blog, @expected_resources, result)
|
85
|
+
result = BlogApi::Blog.all
|
86
|
+
check_returned_array(BlogApi::Blog, @expected_resources, result)
|
84
87
|
end
|
85
88
|
end
|
86
89
|
|
@@ -88,24 +91,24 @@ RSpec.describe ApiResource::Resource do
|
|
88
91
|
|
89
92
|
it 'fetches resourced filtered by query string with GET' do
|
90
93
|
req(:get, '/blogs?title=hello+world&tags%5B%5D=hello&tags%5B%5D=first&tags%5B%5D=greeting', @expected_resources)
|
91
|
-
result = Blog.where(title: 'hello world', tags: ['hello', 'first', 'greeting'])
|
92
|
-
check_returned_array(Blog, @expected_resources, result)
|
94
|
+
result = BlogApi::Blog.where(title: 'hello world', tags: ['hello', 'first', 'greeting'])
|
95
|
+
check_returned_array(BlogApi::Blog, @expected_resources, result)
|
93
96
|
end
|
94
97
|
|
95
98
|
it 'fetches resources filtered by query string with POST' do
|
96
|
-
stub_request(:post, "#{Blog.base_url}/blogs").
|
99
|
+
stub_request(:post, "#{BlogApi::Blog.base_url}/blogs").
|
97
100
|
with(headers: { 'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded' },
|
98
101
|
body: 'title=hello%20world&tags[]=hello&tags[]=first&tags[]=greeting').
|
99
102
|
to_return(status: 200, body: @expected_resources.to_json)
|
100
103
|
|
101
|
-
result = Blog.where({ title: 'hello world', tags: ['hello', 'first', 'greeting'] }, :post)
|
102
|
-
check_returned_array(Blog, @expected_resources, result)
|
104
|
+
result = BlogApi::Blog.where({ title: 'hello world', tags: ['hello', 'first', 'greeting'] }, :post)
|
105
|
+
check_returned_array(BlogApi::Blog, @expected_resources, result)
|
103
106
|
end
|
104
107
|
|
105
108
|
it 'not add query string for empty array parameter values on GET' do
|
106
109
|
req(:get, '/blogs', @expected_resources)
|
107
|
-
result = Blog.where({ tags: [] })
|
108
|
-
check_returned_array(Blog, @expected_resources, result)
|
110
|
+
result = BlogApi::Blog.where({ tags: [] })
|
111
|
+
check_returned_array(BlogApi::Blog, @expected_resources, result)
|
109
112
|
end
|
110
113
|
end
|
111
114
|
|
@@ -114,8 +117,8 @@ RSpec.describe ApiResource::Resource do
|
|
114
117
|
it 'fetches resource nested with parent resource' do
|
115
118
|
resource_id = 3
|
116
119
|
req(:get, "/tags/#{resource_id}/blogs", @expected_resources)
|
117
|
-
result = Blog.by_tag(resource_id).all
|
118
|
-
check_returned_array(Blog, @expected_resources, result)
|
120
|
+
result = BlogApi::Blog.by_tag(resource_id).all
|
121
|
+
check_returned_array(BlogApi::Blog, @expected_resources, result)
|
119
122
|
end
|
120
123
|
end
|
121
124
|
|
@@ -130,10 +133,10 @@ RSpec.describe ApiResource::Resource do
|
|
130
133
|
resource_values = @blog.merge(data: { relation_name => relation_values })
|
131
134
|
resource_id = 3
|
132
135
|
req(:get, "/blogs/#{resource_id}", resource_values)
|
133
|
-
blog = Blog.find(resource_id)
|
134
|
-
check_returned_object(Blog, resource_values, blog, relation_name)
|
136
|
+
blog = BlogApi::Blog.find(resource_id)
|
137
|
+
check_returned_object(BlogApi::Blog, resource_values, blog, relation_name)
|
135
138
|
expect(blog).to respond_to(relation_name)
|
136
|
-
check_returned_array(Tag, relation_values, blog.send(relation_name))
|
139
|
+
check_returned_array(BlogApi::Tag, relation_values, blog.send(relation_name))
|
137
140
|
yield blog if block_given?
|
138
141
|
end
|
139
142
|
|
@@ -155,25 +158,38 @@ RSpec.describe ApiResource::Resource do
|
|
155
158
|
end
|
156
159
|
end
|
157
160
|
context 'has_one' do
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
161
|
+
let(:raw_data) { { 'foo' => 'bar', 'baz' => 3 } }
|
162
|
+
|
163
|
+
def check_has_one(relation_name, relation_values, relation_type=BlogApi::Category)
|
164
|
+
@blog[:data].merge!(relation_name => relation_values)
|
165
|
+
resource_id = 3
|
166
|
+
stub = req(:get, "/blogs/#{resource_id}", @blog)
|
167
|
+
blog = BlogApi::Blog.find(resource_id)
|
168
|
+
check_returned_object(BlogApi::Blog, @blog, blog, relation_name)
|
164
169
|
expect(blog).to respond_to(relation_name)
|
165
|
-
check_returned_object(
|
170
|
+
check_returned_object(relation_type, relation_values, blog.send(relation_name))
|
171
|
+
expect(stub).to have_been_requested
|
166
172
|
end
|
167
173
|
|
168
174
|
it 'gets data rooted inlined json' do
|
169
175
|
check_has_one(:category, data: @category_data)
|
170
176
|
end
|
177
|
+
|
171
178
|
it 'gets un-rooted inlined json' do
|
172
179
|
check_has_one(:category, @category_data)
|
173
180
|
end
|
181
|
+
|
174
182
|
it 'override type from meta' do
|
175
183
|
check_has_one(:main_category, data: { id: 33, name: 'Hobbies' }, meta: { type: :category })
|
176
184
|
end
|
185
|
+
|
186
|
+
it 'creates method for raw types' do
|
187
|
+
check_has_one(:raw_attr, raw_data, Hash)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'replaces method with raw types' do
|
191
|
+
check_has_one(:do_stuff_method, raw_data, Hash)
|
192
|
+
end
|
177
193
|
end
|
178
194
|
end
|
179
195
|
|
@@ -187,19 +203,19 @@ RSpec.describe ApiResource::Resource do
|
|
187
203
|
|
188
204
|
context '#save!' do
|
189
205
|
it 'POSTs data when id is nil' do
|
190
|
-
blog = Blog.new(title: 'hello')
|
206
|
+
blog = BlogApi::Blog.new(title: 'hello')
|
191
207
|
expect(blog.id).to be_nil
|
192
208
|
expected_value = { data: { id: 32, title: 'hello' } }
|
193
209
|
|
194
210
|
stub = req(:post, '/blogs', expected_value, 201, { title: 'hello' })
|
195
211
|
returned = blog.save!
|
196
212
|
expect(returned).to eq(blog)
|
197
|
-
check_returned_object(Blog, expected_value, blog)
|
213
|
+
check_returned_object(BlogApi::Blog, expected_value, blog)
|
198
214
|
expect(stub).to have_been_requested
|
199
215
|
end
|
200
216
|
it 'PUTs data when id is not nil' do
|
201
217
|
blog_id = 525
|
202
|
-
blog = Blog.new(id: blog_id, title: 'hello')
|
218
|
+
blog = BlogApi::Blog.new(id: blog_id, title: 'hello')
|
203
219
|
|
204
220
|
stub = req(:put, '/blogs', nil, 204, id: blog_id.to_s, title: 'hello')
|
205
221
|
returned = blog.save!
|
@@ -211,14 +227,14 @@ RSpec.describe ApiResource::Resource do
|
|
211
227
|
|
212
228
|
context '#destroy!' do
|
213
229
|
it 'DELETEs data when id is not nil' do
|
214
|
-
blog = Blog.new(id: 323, title: 'hello')
|
230
|
+
blog = BlogApi::Blog.new(id: 323, title: 'hello')
|
215
231
|
stub = req(:delete, '/blogs', '', 204)
|
216
232
|
returned = blog.destroy!
|
217
233
|
expect(returned).to eq(blog)
|
218
234
|
expect(stub).to have_been_requested
|
219
235
|
end
|
220
236
|
it 'not DELETE data when id is nil' do
|
221
|
-
blog = Blog.new(id: nil, title: 'hello')
|
237
|
+
blog = BlogApi::Blog.new(id: nil, title: 'hello')
|
222
238
|
stub = req(:delete, '/blogs', '', 204)
|
223
239
|
returned = blog.destroy!
|
224
240
|
expect(returned).to eq(blog)
|
@@ -228,13 +244,13 @@ RSpec.describe ApiResource::Resource do
|
|
228
244
|
|
229
245
|
context '#submit!' do
|
230
246
|
|
231
|
-
class SignUp <
|
247
|
+
class SignUp < BlogApi::Resource
|
232
248
|
self.resource_path = 'sign_up'
|
233
249
|
attr_accessor :email, :password, :password_confirmation, :name
|
234
250
|
validates :name, presence: true
|
235
251
|
end
|
236
252
|
|
237
|
-
class User <
|
253
|
+
class User < BlogApi::Resource
|
238
254
|
attr_accessor :id, :email, :name
|
239
255
|
end
|
240
256
|
|
@@ -268,8 +284,9 @@ RSpec.describe ApiResource::Resource do
|
|
268
284
|
expect(stub).to_not have_been_requested
|
269
285
|
end
|
270
286
|
end
|
287
|
+
|
271
288
|
context '::filter_submit_params' do
|
272
|
-
class Purchase <
|
289
|
+
class Purchase < BlogApi::Resource
|
273
290
|
filter_submitted_attributes do |attrs|
|
274
291
|
address_attrs = [:name, :street, :zip_code]
|
275
292
|
attrs.except(*address_attrs).merge(address: attrs.slice(*address_attrs))
|