jsonapi-resources 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/README.md +11 -10
- data/lib/jsonapi/active_record_operations_processor.rb +1 -1
- data/lib/jsonapi/exceptions.rb +3 -2
- data/lib/jsonapi/operation.rb +14 -14
- data/lib/jsonapi/operations_processor.rb +2 -4
- data/lib/jsonapi/request.rb +1 -3
- data/lib/jsonapi/resource.rb +15 -3
- data/lib/jsonapi/resources/version.rb +1 -1
- data/test/controllers/controller_test.rb +96 -11
- data/test/fixtures/active_record.rb +58 -37
- data/test/fixtures/categories.yml +35 -0
- data/test/fixtures/craters.yml +9 -0
- data/test/fixtures/moons.yml +6 -0
- data/test/fixtures/planet_types.yml +19 -0
- data/test/fixtures/planets.yml +47 -0
- data/test/test_helper.rb +10 -0
- data/test/unit/resource/resource_test.rb +11 -0
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1860b6f54c5c18361eee885c58d9a44ca799223
|
4
|
+
data.tar.gz: 75520fb96150d1185d444c474f9b803e1efada5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f335fd832a84998b16b4c10fb349c87fba4bfbc5e97166e3c7875c6d07ec46a6fe8453aba8d2297d12ab1a9c22ee28355695e009384a7bb3f2f41fd2ade7d8db
|
7
|
+
data.tar.gz: bf31f588b77dfa2c95d9ddcd6461ef984aa329c2c17641082662156b01a36c69aa6f51fcb2741f84886f0ccdee5c9d2f6108966cfd842db8bcae5509ebb44dd1
|
data/README.md
CHANGED
@@ -102,16 +102,16 @@ class AuthorResource < JSONAPI::Resource
|
|
102
102
|
|
103
103
|
def fetchable_fields
|
104
104
|
if (context.current_user.guest)
|
105
|
-
super
|
105
|
+
super - [:email]
|
106
106
|
else
|
107
|
-
super
|
107
|
+
super
|
108
108
|
end
|
109
109
|
end
|
110
110
|
end
|
111
111
|
```
|
112
112
|
|
113
|
-
Context flows through from the controller and can be used to control the attributes based on the
|
114
|
-
value).
|
113
|
+
Context flows through from the controller to the resource and can be used to control the attributes based on the
|
114
|
+
current user (or other value).
|
115
115
|
|
116
116
|
##### Creatable and Updatable Attributes
|
117
117
|
|
@@ -262,7 +262,7 @@ end
|
|
262
262
|
|
263
263
|
The relationship methods (`relationship`, `has_one`, and `has_many`) support the following options:
|
264
264
|
|
265
|
-
* `class_name` - a string specifying the underlying class for the related resource
|
265
|
+
* `class_name` - a string specifying the underlying class for the related resource. Defaults to the `class_name` property on the underlying model.
|
266
266
|
* `foreign_key` - the method on the resource used to fetch the related resource. Defaults to `<resource_name>_id` for has_one and `<resource_name>_ids` for has_many relationships.
|
267
267
|
* `acts_as_set` - allows the entire set of related records to be replaced in one operation. Defaults to false if not set.
|
268
268
|
* `polymorphic` - set to true to identify relationships that are polymorphic.
|
@@ -270,7 +270,7 @@ The relationship methods (`relationship`, `has_one`, and `has_many`) support the
|
|
270
270
|
|
271
271
|
`to_one` relationships support the additional option:
|
272
272
|
* `foreign_key_on` - defaults to `:self`. To indicate that the foreign key is on the related resource specify `:related`.
|
273
|
-
|
273
|
+
|
274
274
|
Examples:
|
275
275
|
|
276
276
|
```ruby
|
@@ -338,7 +338,7 @@ class ContactResource < JSONAPI::Resource
|
|
338
338
|
end
|
339
339
|
```
|
340
340
|
|
341
|
-
Then a request could pass in a filter for example `http://example.com/contacts?filter[name_last]=Smith` and the system
|
341
|
+
Then a request could pass in a filter for example `http://example.com/contacts?filter[name_last]=Smith` and the system
|
342
342
|
will find all people where the last name exactly matches Smith.
|
343
343
|
|
344
344
|
##### Default Filters
|
@@ -1276,12 +1276,13 @@ JSONAPI.configure do |config|
|
|
1276
1276
|
# processing. If you want to use Rails' `rescue_from` macro to
|
1277
1277
|
# catch this error and render a 403 status code, you should add
|
1278
1278
|
# the `Pundit::NotAuthorizedError` to the `exception_class_whitelist`.
|
1279
|
+
# Subclasses of the whitelisted classes will also be whitelisted.
|
1279
1280
|
config.exception_class_whitelist = []
|
1280
|
-
|
1281
|
+
|
1281
1282
|
# Resource Linkage
|
1282
1283
|
# Controls the serialization of resource linkage for non compound documents
|
1283
|
-
# NOTE:
|
1284
|
-
config.
|
1284
|
+
# NOTE: always_include_to_many_linkage_data is not currently implemented
|
1285
|
+
config.always_include_to_one_linkage_data = false
|
1285
1286
|
end
|
1286
1287
|
```
|
1287
1288
|
|
@@ -29,7 +29,7 @@ class ActiveRecordOperationsProcessor < JSONAPI::OperationsProcessor
|
|
29
29
|
raise e
|
30
30
|
|
31
31
|
rescue => e
|
32
|
-
if JSONAPI.configuration.exception_class_whitelist.
|
32
|
+
if JSONAPI.configuration.exception_class_whitelist.any? { |k| e.class.ancestors.include?(k) }
|
33
33
|
raise e
|
34
34
|
else
|
35
35
|
internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
|
data/lib/jsonapi/exceptions.rb
CHANGED
@@ -323,10 +323,11 @@ module JSONAPI
|
|
323
323
|
end
|
324
324
|
|
325
325
|
def pointer(attr_or_relationship_name)
|
326
|
+
formatted_attr_or_relationship_name = format_key(attr_or_relationship_name)
|
326
327
|
if resource_relationships.include?(attr_or_relationship_name)
|
327
|
-
"/data/relationships/#{
|
328
|
+
"/data/relationships/#{formatted_attr_or_relationship_name}"
|
328
329
|
else
|
329
|
-
"/data/attributes/#{
|
330
|
+
"/data/attributes/#{formatted_attr_or_relationship_name}"
|
330
331
|
end
|
331
332
|
end
|
332
333
|
end
|
data/lib/jsonapi/operation.rb
CHANGED
@@ -17,12 +17,12 @@ module JSONAPI
|
|
17
17
|
attr_reader :filters, :include_directives, :sort_criteria, :paginator
|
18
18
|
|
19
19
|
def initialize(resource_klass, options = {})
|
20
|
+
super(resource_klass, options)
|
20
21
|
@filters = options[:filters]
|
21
22
|
@include_directives = options[:include_directives]
|
22
23
|
@sort_criteria = options.fetch(:sort_criteria, [])
|
23
24
|
@paginator = options[:paginator]
|
24
25
|
@transactional = false
|
25
|
-
super(resource_klass, options)
|
26
26
|
end
|
27
27
|
|
28
28
|
def record_count
|
@@ -70,10 +70,10 @@ module JSONAPI
|
|
70
70
|
attr_reader :id, :include_directives
|
71
71
|
|
72
72
|
def initialize(resource_klass, options = {})
|
73
|
+
super(resource_klass, options)
|
73
74
|
@id = options.fetch(:id)
|
74
75
|
@include_directives = options[:include_directives]
|
75
76
|
@transactional = false
|
76
|
-
super(resource_klass, options)
|
77
77
|
end
|
78
78
|
|
79
79
|
def apply
|
@@ -94,10 +94,10 @@ module JSONAPI
|
|
94
94
|
attr_reader :parent_key, :relationship_type
|
95
95
|
|
96
96
|
def initialize(resource_klass, options = {})
|
97
|
+
super(resource_klass, options)
|
97
98
|
@parent_key = options.fetch(:parent_key)
|
98
99
|
@relationship_type = options.fetch(:relationship_type)
|
99
100
|
@transactional = false
|
100
|
-
super(resource_klass, options)
|
101
101
|
end
|
102
102
|
|
103
103
|
def apply
|
@@ -116,11 +116,11 @@ module JSONAPI
|
|
116
116
|
attr_reader :source_klass, :source_id, :relationship_type
|
117
117
|
|
118
118
|
def initialize(resource_klass, options = {})
|
119
|
+
super(resource_klass, options)
|
119
120
|
@source_klass = options.fetch(:source_klass)
|
120
121
|
@source_id = options.fetch(:source_id)
|
121
122
|
@relationship_type = options.fetch(:relationship_type)
|
122
123
|
@transactional = false
|
123
|
-
super(resource_klass, options)
|
124
124
|
end
|
125
125
|
|
126
126
|
def apply
|
@@ -139,6 +139,7 @@ module JSONAPI
|
|
139
139
|
attr_reader :source_klass, :source_id, :relationship_type, :filters, :sort_criteria, :paginator
|
140
140
|
|
141
141
|
def initialize(resource_klass, options = {})
|
142
|
+
super(resource_klass, options)
|
142
143
|
@source_klass = options.fetch(:source_klass)
|
143
144
|
@source_id = options.fetch(:source_id)
|
144
145
|
@relationship_type = options.fetch(:relationship_type)
|
@@ -146,7 +147,6 @@ module JSONAPI
|
|
146
147
|
@sort_criteria = options[:sort_criteria]
|
147
148
|
@paginator = options[:paginator]
|
148
149
|
@transactional = false
|
149
|
-
super(resource_klass, options)
|
150
150
|
end
|
151
151
|
|
152
152
|
def apply
|
@@ -168,8 +168,8 @@ module JSONAPI
|
|
168
168
|
attr_reader :data
|
169
169
|
|
170
170
|
def initialize(resource_klass, options = {})
|
171
|
-
@data = options.fetch(:data)
|
172
171
|
super(resource_klass, options)
|
172
|
+
@data = options.fetch(:data)
|
173
173
|
end
|
174
174
|
|
175
175
|
def apply
|
@@ -186,8 +186,8 @@ module JSONAPI
|
|
186
186
|
class RemoveResourceOperation < Operation
|
187
187
|
attr_reader :resource_id
|
188
188
|
def initialize(resource_klass, options = {})
|
189
|
-
@resource_id = options.fetch(:resource_id)
|
190
189
|
super(resource_klass, options)
|
190
|
+
@resource_id = options.fetch(:resource_id)
|
191
191
|
end
|
192
192
|
|
193
193
|
def apply
|
@@ -205,9 +205,9 @@ module JSONAPI
|
|
205
205
|
attr_reader :data, :resource_id
|
206
206
|
|
207
207
|
def initialize(resource_klass, options = {})
|
208
|
+
super(resource_klass, options)
|
208
209
|
@resource_id = options.fetch(:resource_id)
|
209
210
|
@data = options.fetch(:data)
|
210
|
-
super(resource_klass, options)
|
211
211
|
end
|
212
212
|
|
213
213
|
def apply
|
@@ -222,10 +222,10 @@ module JSONAPI
|
|
222
222
|
attr_reader :resource_id, :relationship_type, :key_value
|
223
223
|
|
224
224
|
def initialize(resource_klass, options = {})
|
225
|
+
super(resource_klass, options)
|
225
226
|
@resource_id = options.fetch(:resource_id)
|
226
227
|
@key_value = options.fetch(:key_value)
|
227
228
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
228
|
-
super(resource_klass, options)
|
229
229
|
end
|
230
230
|
|
231
231
|
def apply
|
@@ -240,11 +240,11 @@ module JSONAPI
|
|
240
240
|
attr_reader :resource_id, :relationship_type, :key_value, :key_type
|
241
241
|
|
242
242
|
def initialize(resource_klass, options = {})
|
243
|
+
super(resource_klass, options)
|
243
244
|
@resource_id = options.fetch(:resource_id)
|
244
245
|
@key_value = options.fetch(:key_value)
|
245
246
|
@key_type = options.fetch(:key_type)
|
246
247
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
247
|
-
super(resource_klass, options)
|
248
248
|
end
|
249
249
|
|
250
250
|
def apply
|
@@ -259,10 +259,10 @@ module JSONAPI
|
|
259
259
|
attr_reader :resource_id, :relationship_type, :data
|
260
260
|
|
261
261
|
def initialize(resource_klass, options)
|
262
|
+
super(resource_klass, options)
|
262
263
|
@resource_id = options.fetch(:resource_id)
|
263
264
|
@data = options.fetch(:data)
|
264
265
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
265
|
-
super(resource_klass, options)
|
266
266
|
end
|
267
267
|
|
268
268
|
def apply
|
@@ -277,10 +277,10 @@ module JSONAPI
|
|
277
277
|
attr_reader :resource_id, :relationship_type, :data
|
278
278
|
|
279
279
|
def initialize(resource_klass, options)
|
280
|
+
super(resource_klass, options)
|
280
281
|
@resource_id = options.fetch(:resource_id)
|
281
282
|
@data = options.fetch(:data)
|
282
283
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
283
|
-
super(resource_klass, options)
|
284
284
|
end
|
285
285
|
|
286
286
|
def apply
|
@@ -295,10 +295,10 @@ module JSONAPI
|
|
295
295
|
attr_reader :resource_id, :relationship_type, :associated_key
|
296
296
|
|
297
297
|
def initialize(resource_klass, options)
|
298
|
+
super(resource_klass, options)
|
298
299
|
@resource_id = options.fetch(:resource_id)
|
299
300
|
@associated_key = options.fetch(:associated_key)
|
300
301
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
301
|
-
super(resource_klass, options)
|
302
302
|
end
|
303
303
|
|
304
304
|
def apply
|
@@ -313,9 +313,9 @@ module JSONAPI
|
|
313
313
|
attr_reader :resource_id, :relationship_type
|
314
314
|
|
315
315
|
def initialize(resource_klass, options)
|
316
|
+
super(resource_klass, options)
|
316
317
|
@resource_id = options.fetch(:resource_id)
|
317
318
|
@relationship_type = options.fetch(:relationship_type).to_sym
|
318
|
-
super(resource_klass, options)
|
319
319
|
end
|
320
320
|
|
321
321
|
def apply
|
@@ -33,10 +33,8 @@ module JSONAPI
|
|
33
33
|
# Use transactions if more than one operation and if one of the operations can be transactional
|
34
34
|
# Even if transactional transactions won't be used unless the derived OperationsProcessor supports them.
|
35
35
|
@transactional = false
|
36
|
-
|
37
|
-
@
|
38
|
-
@transactional |= operation.transactional
|
39
|
-
end
|
36
|
+
@operations.each do |operation|
|
37
|
+
@transactional |= operation.transactional
|
40
38
|
end
|
41
39
|
|
42
40
|
run_callbacks :operations do
|
data/lib/jsonapi/request.rb
CHANGED
@@ -604,7 +604,7 @@ module JSONAPI
|
|
604
604
|
end
|
605
605
|
|
606
606
|
def parse_remove_operation(params)
|
607
|
-
keys = parse_key_array(params.
|
607
|
+
keys = parse_key_array(params.require(:id))
|
608
608
|
|
609
609
|
keys.each do |key|
|
610
610
|
@operations.push JSONAPI::RemoveResourceOperation.new(
|
@@ -613,8 +613,6 @@ module JSONAPI
|
|
613
613
|
resource_id: key
|
614
614
|
)
|
615
615
|
end
|
616
|
-
rescue ActionController::UnpermittedParameters => e
|
617
|
-
@errors.concat(JSONAPI::Exceptions::ParametersNotAllowed.new(e.params).errors)
|
618
616
|
rescue JSONAPI::Exceptions::Error => e
|
619
617
|
@errors.concat(e.errors)
|
620
618
|
end
|
data/lib/jsonapi/resource.rb
CHANGED
@@ -596,7 +596,7 @@ module JSONAPI
|
|
596
596
|
end
|
597
597
|
|
598
598
|
def _as_parent_key
|
599
|
-
@_as_parent_key ||= "#{_type.to_s.singularize}
|
599
|
+
@_as_parent_key ||= "#{_type.to_s.singularize}_id"
|
600
600
|
end
|
601
601
|
|
602
602
|
def _allowed_filters
|
@@ -621,7 +621,10 @@ module JSONAPI
|
|
621
621
|
end
|
622
622
|
|
623
623
|
def _model_class
|
624
|
-
@model
|
624
|
+
return @model if @model
|
625
|
+
@model = _model_name.to_s.safe_constantize
|
626
|
+
fail NameError, "model could not be found for #{self.name}" if @model.nil?
|
627
|
+
@model
|
625
628
|
end
|
626
629
|
|
627
630
|
def _allowed_filter?(filter)
|
@@ -670,6 +673,15 @@ module JSONAPI
|
|
670
673
|
|
671
674
|
attrs.each do |attr|
|
672
675
|
check_reserved_relationship_name(attr)
|
676
|
+
|
677
|
+
# Initialize from an ActiveRecord model's properties
|
678
|
+
if _model_class < ActiveRecord::Base
|
679
|
+
model_association = _model_class.reflect_on_association(attr)
|
680
|
+
if model_association
|
681
|
+
options[:class_name] ||= model_association.class_name
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
673
685
|
@_relationships[attr] = relationship = klass.new(attr, options)
|
674
686
|
|
675
687
|
associated_records_method_name = case relationship
|
@@ -741,7 +753,7 @@ module JSONAPI
|
|
741
753
|
|
742
754
|
sort_criteria = options.fetch(:sort_criteria, {})
|
743
755
|
unless sort_criteria.nil? || sort_criteria.empty?
|
744
|
-
order_options =
|
756
|
+
order_options = relationship.resource_klass.construct_order_options(sort_criteria)
|
745
757
|
records = resource_klass.apply_sort(records, order_options)
|
746
758
|
end
|
747
759
|
|
@@ -1427,13 +1427,6 @@ class PostsControllerTest < ActionController::TestCase
|
|
1427
1427
|
assert_equal initial_count, Post.count
|
1428
1428
|
end
|
1429
1429
|
|
1430
|
-
def test_delete_extra_param
|
1431
|
-
initial_count = Post.count
|
1432
|
-
delete :destroy, {id: '4', asdfg: 'aaaa'}
|
1433
|
-
assert_response :bad_request
|
1434
|
-
assert_equal initial_count, Post.count
|
1435
|
-
end
|
1436
|
-
|
1437
1430
|
def test_show_to_one_relationship
|
1438
1431
|
get :show_relationship, {post_id: '1', relationship: 'author'}
|
1439
1432
|
assert_response :success
|
@@ -2159,11 +2152,8 @@ class Api::V1::PostsControllerTest < ActionController::TestCase
|
|
2159
2152
|
end
|
2160
2153
|
|
2161
2154
|
class FactsControllerTest < ActionController::TestCase
|
2162
|
-
def setup
|
2163
|
-
JSONAPI.configuration.json_key_format = :camelized_key
|
2164
|
-
end
|
2165
|
-
|
2166
2155
|
def test_type_formatting
|
2156
|
+
JSONAPI.configuration.json_key_format = :camelized_key
|
2167
2157
|
get :show, {id: '1'}
|
2168
2158
|
assert_response :success
|
2169
2159
|
assert json_response['data'].is_a?(Hash)
|
@@ -2177,6 +2167,40 @@ class FactsControllerTest < ActionController::TestCase
|
|
2177
2167
|
assert_equal 'abc', json_response['data']['attributes']['photo']
|
2178
2168
|
assert_equal false, json_response['data']['attributes']['cool']
|
2179
2169
|
end
|
2170
|
+
|
2171
|
+
def test_create_with_invalid_data
|
2172
|
+
JSONAPI.configuration.json_key_format = :dasherized_key
|
2173
|
+
set_content_type_header!
|
2174
|
+
post :create,
|
2175
|
+
{
|
2176
|
+
data: {
|
2177
|
+
type: 'facts',
|
2178
|
+
attributes: {
|
2179
|
+
bio: '',
|
2180
|
+
:"quality-rating" => '',
|
2181
|
+
:"spouse-name" => '',
|
2182
|
+
salary: 100000,
|
2183
|
+
:"date-time-joined" => '',
|
2184
|
+
birthday: '',
|
2185
|
+
bedtime: '',
|
2186
|
+
photo: 'abc',
|
2187
|
+
cool: false
|
2188
|
+
},
|
2189
|
+
relationships: {
|
2190
|
+
}
|
2191
|
+
}
|
2192
|
+
}
|
2193
|
+
|
2194
|
+
assert_response :unprocessable_entity
|
2195
|
+
|
2196
|
+
assert_equal "/data/attributes/spouse-name", json_response['errors'][0]['source']['pointer']
|
2197
|
+
assert_equal "can't be blank", json_response['errors'][0]['detail']
|
2198
|
+
assert_equal "spouse-name - can't be blank", json_response['errors'][0]['title']
|
2199
|
+
|
2200
|
+
assert_equal "/data/attributes/bio", json_response['errors'][1]['source']['pointer']
|
2201
|
+
assert_equal "can't be blank", json_response['errors'][1]['detail']
|
2202
|
+
assert_equal "bio - can't be blank", json_response['errors'][1]['title']
|
2203
|
+
end
|
2180
2204
|
end
|
2181
2205
|
|
2182
2206
|
class Api::V2::BooksControllerTest < ActionController::TestCase
|
@@ -2634,3 +2658,64 @@ class Api::V1::PlanetsControllerTest < ActionController::TestCase
|
|
2634
2658
|
assert_match /Save failed or was cancelled/, json_response['errors'][0]['detail']
|
2635
2659
|
end
|
2636
2660
|
end
|
2661
|
+
|
2662
|
+
class Api::V1::MoonsControllerTest < ActionController::TestCase
|
2663
|
+
def test_get_related_resource
|
2664
|
+
get :get_related_resource, {crater_id: 'S56D', relationship: 'moon', source: "api/v1/craters"}
|
2665
|
+
assert_response :success
|
2666
|
+
assert_hash_equals json_response,
|
2667
|
+
{
|
2668
|
+
data: {
|
2669
|
+
id: "1",
|
2670
|
+
type: "moons",
|
2671
|
+
links: {self: "http://test.host/moons/1"},
|
2672
|
+
attributes: {name: "Titan", description: "Best known of the Saturn moons."},
|
2673
|
+
relationships: {
|
2674
|
+
planet: {links: {self: "http://test.host/moons/1/relationships/planet", related: "http://test.host/moons/1/planet"}},
|
2675
|
+
craters: {links: {self: "http://test.host/moons/1/relationships/craters", related: "http://test.host/moons/1/craters"}}}
|
2676
|
+
}
|
2677
|
+
}
|
2678
|
+
|
2679
|
+
end
|
2680
|
+
end
|
2681
|
+
|
2682
|
+
class Api::V1::CratersControllerTest < ActionController::TestCase
|
2683
|
+
def test_show_single
|
2684
|
+
get :show, {id: 'S56D'}
|
2685
|
+
assert_response :success
|
2686
|
+
assert json_response['data'].is_a?(Hash)
|
2687
|
+
assert_equal 'S56D', json_response['data']['attributes']['code']
|
2688
|
+
assert_equal 'Very large crater', json_response['data']['attributes']['description']
|
2689
|
+
assert_nil json_response['included']
|
2690
|
+
end
|
2691
|
+
|
2692
|
+
def test_get_related_resources
|
2693
|
+
get :get_related_resources, {moon_id: '1', relationship: 'craters', source: "api/v1/moons"}
|
2694
|
+
assert_response :success
|
2695
|
+
assert_hash_equals json_response,
|
2696
|
+
{
|
2697
|
+
data: [
|
2698
|
+
{id:"A4D3",
|
2699
|
+
type:"craters",
|
2700
|
+
links:{self: "http://test.host/craters/A4D3"},
|
2701
|
+
attributes:{code: "A4D3", description: "Small crater"},
|
2702
|
+
relationships:{moon: {links: {self: "http://test.host/craters/A4D3/relationships/moon", related: "http://test.host/craters/A4D3/moon"}}}
|
2703
|
+
},
|
2704
|
+
{id: "S56D",
|
2705
|
+
type: "craters",
|
2706
|
+
links:{self: "http://test.host/craters/S56D"},
|
2707
|
+
attributes:{code: "S56D", description: "Very large crater"},
|
2708
|
+
relationships:{moon: {links: {self: "http://test.host/craters/S56D/relationships/moon", related: "http://test.host/craters/S56D/moon"}}}
|
2709
|
+
}
|
2710
|
+
]
|
2711
|
+
}
|
2712
|
+
end
|
2713
|
+
|
2714
|
+
def test_show_relationship
|
2715
|
+
get :show_relationship, {crater_id: 'S56D', relationship: 'moon'}
|
2716
|
+
|
2717
|
+
assert_response :success
|
2718
|
+
assert_equal "moons", json_response['data']['type']
|
2719
|
+
assert_equal "1", json_response['data']['id']
|
2720
|
+
end
|
2721
|
+
end
|
@@ -92,6 +92,12 @@ ActiveRecord::Schema.define do
|
|
92
92
|
t.integer :planet_id
|
93
93
|
end
|
94
94
|
|
95
|
+
create_table :craters, id: false, force: true do |t|
|
96
|
+
t.string :code
|
97
|
+
t.string :description
|
98
|
+
t.integer :moon_id
|
99
|
+
end
|
100
|
+
|
95
101
|
create_table :preferences, force: true do |t|
|
96
102
|
t.integer :person_id
|
97
103
|
t.boolean :advanced_mode, default: false
|
@@ -248,6 +254,21 @@ end
|
|
248
254
|
class Section < ActiveRecord::Base
|
249
255
|
end
|
250
256
|
|
257
|
+
class HairCut < ActiveRecord::Base
|
258
|
+
end
|
259
|
+
|
260
|
+
class Property < ActiveRecord::Base
|
261
|
+
end
|
262
|
+
|
263
|
+
class Customer < ActiveRecord::Base
|
264
|
+
end
|
265
|
+
|
266
|
+
class BadlyNamedAttributes < ActiveRecord::Base
|
267
|
+
end
|
268
|
+
|
269
|
+
class Cat < ActiveRecord::Base
|
270
|
+
end
|
271
|
+
|
251
272
|
class IsoCurrency < ActiveRecord::Base
|
252
273
|
self.primary_key = :code
|
253
274
|
# has_many :expense_entries, foreign_key: 'currency_code'
|
@@ -281,6 +302,14 @@ end
|
|
281
302
|
|
282
303
|
class Moon < ActiveRecord::Base
|
283
304
|
belongs_to :planet
|
305
|
+
|
306
|
+
has_many :craters
|
307
|
+
end
|
308
|
+
|
309
|
+
class Crater < ActiveRecord::Base
|
310
|
+
self.primary_key = :code
|
311
|
+
|
312
|
+
belongs_to :moon
|
284
313
|
end
|
285
314
|
|
286
315
|
class Preferences < ActiveRecord::Base
|
@@ -289,6 +318,7 @@ class Preferences < ActiveRecord::Base
|
|
289
318
|
end
|
290
319
|
|
291
320
|
class Fact < ActiveRecord::Base
|
321
|
+
validates :spouse_name, :bio, presence: true
|
292
322
|
end
|
293
323
|
|
294
324
|
class Like < ActiveRecord::Base
|
@@ -360,7 +390,7 @@ class BreedData
|
|
360
390
|
end
|
361
391
|
end
|
362
392
|
|
363
|
-
class
|
393
|
+
class Customer < ActiveRecord::Base
|
364
394
|
has_many :purchase_orders
|
365
395
|
end
|
366
396
|
|
@@ -407,13 +437,6 @@ class Product < ActiveRecord::Base
|
|
407
437
|
has_one :picture, as: :imageable
|
408
438
|
end
|
409
439
|
|
410
|
-
### PORO Data - don't do this in a production app
|
411
|
-
$breed_data = BreedData.new
|
412
|
-
$breed_data.add(Breed.new(0, 'persian'))
|
413
|
-
$breed_data.add(Breed.new(1, 'siamese'))
|
414
|
-
$breed_data.add(Breed.new(2, 'sphinx'))
|
415
|
-
$breed_data.add(Breed.new(3, 'to_delete'))
|
416
|
-
|
417
440
|
### OperationsProcessor
|
418
441
|
class CountingActiveRecordOperationsProcessor < ActiveRecordOperationsProcessor
|
419
442
|
after_find_operation do
|
@@ -429,7 +452,7 @@ class ErrorRaisingOperationsProcessor < ActiveRecordOperationsProcessor
|
|
429
452
|
def process_operation(operation)
|
430
453
|
mock_operation = Minitest::Mock.new
|
431
454
|
mock_operation.expect(:apply, true) do
|
432
|
-
raise PostsController::
|
455
|
+
raise PostsController::SubSpecialError
|
433
456
|
end
|
434
457
|
super(mock_operation)
|
435
458
|
end
|
@@ -445,6 +468,7 @@ end
|
|
445
468
|
class PostsController < ActionController::Base
|
446
469
|
include JSONAPI::ActsAsResourceController
|
447
470
|
class SpecialError < StandardError; end
|
471
|
+
class SubSpecialError < PostsController::SpecialError; end
|
448
472
|
|
449
473
|
# This is used to test that classes that are whitelisted are reraised by
|
450
474
|
# the operations processor.
|
@@ -523,6 +547,9 @@ module Api
|
|
523
547
|
class MoonsController < JSONAPI::ResourceController
|
524
548
|
end
|
525
549
|
|
550
|
+
class CratersController < JSONAPI::ResourceController
|
551
|
+
end
|
552
|
+
|
526
553
|
class LikesController < JSONAPI::ResourceController
|
527
554
|
end
|
528
555
|
end
|
@@ -860,13 +887,25 @@ class MoonResource < JSONAPI::Resource
|
|
860
887
|
attribute :description
|
861
888
|
|
862
889
|
has_one :planet
|
890
|
+
has_many :craters
|
891
|
+
end
|
892
|
+
|
893
|
+
class CraterResource < JSONAPI::Resource
|
894
|
+
attribute :code
|
895
|
+
attribute :description
|
896
|
+
|
897
|
+
has_one :moon
|
898
|
+
|
899
|
+
def self.verify_key(key, context = nil)
|
900
|
+
key && String(key)
|
901
|
+
end
|
863
902
|
end
|
864
903
|
|
865
904
|
class PreferencesResource < JSONAPI::Resource
|
866
905
|
attribute :advanced_mode
|
867
906
|
|
868
|
-
has_one :author, foreign_key: :person_id
|
869
|
-
has_many :friends
|
907
|
+
has_one :author, foreign_key: :person_id
|
908
|
+
has_many :friends
|
870
909
|
|
871
910
|
def self.find_by_key(key, options = {})
|
872
911
|
new(Preferences.first)
|
@@ -930,7 +969,7 @@ module Api
|
|
930
969
|
attribute :body
|
931
970
|
attribute :subject
|
932
971
|
|
933
|
-
has_one :writer, foreign_key: 'author_id'
|
972
|
+
has_one :writer, foreign_key: 'author_id', class_name: 'Writer'
|
934
973
|
has_one :section
|
935
974
|
has_many :comments, acts_as_set: false
|
936
975
|
|
@@ -951,6 +990,7 @@ module Api
|
|
951
990
|
PlanetResource = PlanetResource.dup
|
952
991
|
PlanetTypeResource = PlanetTypeResource.dup
|
953
992
|
MoonResource = MoonResource.dup
|
993
|
+
CraterResource = CraterResource.dup
|
954
994
|
PreferencesResource = PreferencesResource.dup
|
955
995
|
EmployeeResource = EmployeeResource.dup
|
956
996
|
FriendResource = FriendResource.dup
|
@@ -1214,28 +1254,9 @@ class BadlyNamedAttributesResource < JSONAPI::Resource
|
|
1214
1254
|
end
|
1215
1255
|
warn 'end testing Name Collisions'
|
1216
1256
|
|
1217
|
-
### PORO
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
saturn = Planet.create(name: 'Satern',
|
1225
|
-
description: 'Saturn is the sixth planet from the Sun and the second largest planet in the Solar System, after Jupiter.',
|
1226
|
-
planet_type_id: planetoid.id)
|
1227
|
-
titan = Moon.create(name:'Titan', description: 'Best known of the Saturn moons.', planet_id: saturn.id)
|
1228
|
-
makemake = Planet.create(name: 'Makemake', description: 'A small planetoid in the Kuiperbelt.', planet_type_id: planetoid.id)
|
1229
|
-
uranus = Planet.create(name: 'Uranus', description: 'Insert adolescent jokes here.', planet_type_id: gas_giant.id)
|
1230
|
-
jupiter = Planet.create(name: 'Jupiter', description: 'A gas giant.', planet_type_id: gas_giant.id)
|
1231
|
-
betax = Planet.create(name: 'Beta X', description: 'Newly discovered Planet X', planet_type_id: unknown.id)
|
1232
|
-
betay = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Y', planet_type_id: unknown.id)
|
1233
|
-
betaz = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Z', planet_type_id: unknown.id)
|
1234
|
-
betaw = Planet.create(name: 'Beta W', description: 'Newly discovered Planet W')
|
1235
|
-
Category.create(name: 'Category A', status: 'active')
|
1236
|
-
Category.create(name: 'Category B', status: 'active')
|
1237
|
-
Category.create(name: 'Category C', status: 'active')
|
1238
|
-
Category.create(name: 'Category D', status: 'inactive')
|
1239
|
-
Category.create(name: 'Category E', status: 'inactive')
|
1240
|
-
Category.create(name: 'Category F', status: 'inactive')
|
1241
|
-
Category.create(name: 'Category G', status: 'inactive')
|
1257
|
+
### PORO Data - don't do this in a production app
|
1258
|
+
$breed_data = BreedData.new
|
1259
|
+
$breed_data.add(Breed.new(0, 'persian'))
|
1260
|
+
$breed_data.add(Breed.new(1, 'siamese'))
|
1261
|
+
$breed_data.add(Breed.new(2, 'sphinx'))
|
1262
|
+
$breed_data.add(Breed.new(3, 'to_delete'))
|
@@ -0,0 +1,35 @@
|
|
1
|
+
category_a:
|
2
|
+
id: 1
|
3
|
+
name: Category A
|
4
|
+
status: active
|
5
|
+
|
6
|
+
category_b:
|
7
|
+
id: 2
|
8
|
+
name: Category B
|
9
|
+
status: active
|
10
|
+
|
11
|
+
category_c:
|
12
|
+
id: 3
|
13
|
+
name: Category C
|
14
|
+
status: active
|
15
|
+
|
16
|
+
category_d:
|
17
|
+
id: 4
|
18
|
+
name: Category D
|
19
|
+
status: inactive
|
20
|
+
|
21
|
+
category_e:
|
22
|
+
id: 5
|
23
|
+
name: Category E
|
24
|
+
status: inactive
|
25
|
+
|
26
|
+
category_f:
|
27
|
+
id: 6
|
28
|
+
name: Category F
|
29
|
+
status: inactive
|
30
|
+
|
31
|
+
category_g:
|
32
|
+
id: 7
|
33
|
+
name: Category G
|
34
|
+
status: inactive
|
35
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
saturn:
|
2
|
+
id: 1
|
3
|
+
name: Satern
|
4
|
+
description: Saturn is the sixth planet from the Sun and the second largest planet in the Solar System, after Jupiter.
|
5
|
+
planet_type_id: 2
|
6
|
+
|
7
|
+
makemake:
|
8
|
+
id: 2
|
9
|
+
name: Makemake
|
10
|
+
description: A small planetoid in the Kuiperbelt.
|
11
|
+
planet_type_id: 2
|
12
|
+
|
13
|
+
uranus:
|
14
|
+
id: 3
|
15
|
+
name: Uranus
|
16
|
+
description: Insert adolescent jokes here.
|
17
|
+
planet_type_id: 1
|
18
|
+
|
19
|
+
jupiter:
|
20
|
+
id: 4
|
21
|
+
name: Jupiter
|
22
|
+
description: A gas giant.
|
23
|
+
planet_type_id: 1
|
24
|
+
|
25
|
+
betax:
|
26
|
+
id: 5
|
27
|
+
name: Beta X
|
28
|
+
description: Newly discovered Planet X
|
29
|
+
planet_type_id: 5
|
30
|
+
|
31
|
+
betay:
|
32
|
+
id: 6
|
33
|
+
name: Beta X
|
34
|
+
description: Newly discovered Planet Y
|
35
|
+
planet_type_id: 5
|
36
|
+
|
37
|
+
betaz:
|
38
|
+
id: 7
|
39
|
+
name: Beta X
|
40
|
+
description: Newly discovered Planet Z
|
41
|
+
planet_type_id: 5
|
42
|
+
|
43
|
+
betaw:
|
44
|
+
id: 8
|
45
|
+
name: Beta W
|
46
|
+
description: Newly discovered Planet W
|
47
|
+
planet_type_id:
|
data/test/test_helper.rb
CHANGED
@@ -110,6 +110,7 @@ TestApp.routes.draw do
|
|
110
110
|
jsonapi_resources :planets
|
111
111
|
jsonapi_resources :planet_types
|
112
112
|
jsonapi_resources :moons
|
113
|
+
jsonapi_resources :craters
|
113
114
|
jsonapi_resources :preferences
|
114
115
|
jsonapi_resources :facts
|
115
116
|
jsonapi_resources :categories
|
@@ -130,6 +131,7 @@ TestApp.routes.draw do
|
|
130
131
|
jsonapi_resources :planets
|
131
132
|
jsonapi_resources :planet_types
|
132
133
|
jsonapi_resources :moons
|
134
|
+
jsonapi_resources :craters
|
133
135
|
jsonapi_resources :preferences
|
134
136
|
jsonapi_resources :likes
|
135
137
|
end
|
@@ -224,6 +226,14 @@ class Minitest::Test
|
|
224
226
|
include Helpers::Assertions
|
225
227
|
include Helpers::ValueMatchers
|
226
228
|
include Helpers::FunctionalHelpers
|
229
|
+
include ActiveRecord::TestFixtures
|
230
|
+
|
231
|
+
def run_in_transaction?
|
232
|
+
true
|
233
|
+
end
|
234
|
+
|
235
|
+
self.fixture_path = "#{Rails.root}/fixtures"
|
236
|
+
fixtures :all
|
227
237
|
end
|
228
238
|
|
229
239
|
class ActiveSupport::TestCase
|
@@ -8,6 +8,9 @@ class ArticleResource < JSONAPI::Resource
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
class NoMatchResource < JSONAPI::Resource
|
12
|
+
end
|
13
|
+
|
11
14
|
class CatResource < JSONAPI::Resource
|
12
15
|
attribute :id
|
13
16
|
attribute :name
|
@@ -52,6 +55,14 @@ class ResourceTest < ActiveSupport::TestCase
|
|
52
55
|
assert_equal(PostResource._model_class, Post)
|
53
56
|
end
|
54
57
|
|
58
|
+
def test_nil_model_class
|
59
|
+
error = assert_raises(NameError) { NoMatchResource._model_class }
|
60
|
+
assert_equal(
|
61
|
+
error.message,
|
62
|
+
"model could not be found for NoMatchResource"
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
55
66
|
def test_model_alternate
|
56
67
|
assert_equal(ArticleResource._model_class, Post)
|
57
68
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Gebhardt
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-07-
|
12
|
+
date: 2015-07-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -170,8 +170,10 @@ files:
|
|
170
170
|
- test/fixtures/author_details.yml
|
171
171
|
- test/fixtures/book_comments.yml
|
172
172
|
- test/fixtures/books.yml
|
173
|
+
- test/fixtures/categories.yml
|
173
174
|
- test/fixtures/comments.yml
|
174
175
|
- test/fixtures/comments_tags.yml
|
176
|
+
- test/fixtures/craters.yml
|
175
177
|
- test/fixtures/customers.yml
|
176
178
|
- test/fixtures/documents.yml
|
177
179
|
- test/fixtures/expense_entries.yml
|
@@ -179,10 +181,13 @@ files:
|
|
179
181
|
- test/fixtures/hair_cuts.yml
|
180
182
|
- test/fixtures/iso_currencies.yml
|
181
183
|
- test/fixtures/line_items.yml
|
184
|
+
- test/fixtures/moons.yml
|
182
185
|
- test/fixtures/numeros_telefone.yml
|
183
186
|
- test/fixtures/order_flags.yml
|
184
187
|
- test/fixtures/people.yml
|
185
188
|
- test/fixtures/pictures.yml
|
189
|
+
- test/fixtures/planet_types.yml
|
190
|
+
- test/fixtures/planets.yml
|
186
191
|
- test/fixtures/posts.yml
|
187
192
|
- test/fixtures/posts_tags.yml
|
188
193
|
- test/fixtures/preferences.yml
|
@@ -239,8 +244,10 @@ test_files:
|
|
239
244
|
- test/fixtures/author_details.yml
|
240
245
|
- test/fixtures/book_comments.yml
|
241
246
|
- test/fixtures/books.yml
|
247
|
+
- test/fixtures/categories.yml
|
242
248
|
- test/fixtures/comments.yml
|
243
249
|
- test/fixtures/comments_tags.yml
|
250
|
+
- test/fixtures/craters.yml
|
244
251
|
- test/fixtures/customers.yml
|
245
252
|
- test/fixtures/documents.yml
|
246
253
|
- test/fixtures/expense_entries.yml
|
@@ -248,10 +255,13 @@ test_files:
|
|
248
255
|
- test/fixtures/hair_cuts.yml
|
249
256
|
- test/fixtures/iso_currencies.yml
|
250
257
|
- test/fixtures/line_items.yml
|
258
|
+
- test/fixtures/moons.yml
|
251
259
|
- test/fixtures/numeros_telefone.yml
|
252
260
|
- test/fixtures/order_flags.yml
|
253
261
|
- test/fixtures/people.yml
|
254
262
|
- test/fixtures/pictures.yml
|
263
|
+
- test/fixtures/planet_types.yml
|
264
|
+
- test/fixtures/planets.yml
|
255
265
|
- test/fixtures/posts.yml
|
256
266
|
- test/fixtures/posts_tags.yml
|
257
267
|
- test/fixtures/preferences.yml
|