api-resource 0.5.0 → 0.5.1
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 +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))
|