jsonapi-resources 0.0.4 → 0.0.5

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.
@@ -1,3 +1,4 @@
1
+ require 'jsonapi/configuration'
1
2
  require 'jsonapi/resource_for'
2
3
  require 'jsonapi/association'
3
4
  require 'action_dispatch/routing/mapper'
@@ -24,9 +25,15 @@ module JSONAPI
24
25
 
25
26
  def create_has_many_link(association_type, association_key_value, context)
26
27
  association = self.class._associations[association_type]
27
- related_resource = self.class.resource_for(association.serialize_type_name).find_by_key(association_key_value, context)
28
+ related_resource = self.class.resource_for(association.type).find_by_key(association_key_value, context)
28
29
 
29
- @object.send(association.serialize_type_name) << related_resource.object
30
+ # ToDo: Add option to skip relations that already exist instead of returning an error?
31
+ relation = @object.send(association.type).where(association.primary_key => association_key_value).first
32
+ if relation.nil?
33
+ @object.send(association.type) << related_resource.object
34
+ else
35
+ raise JSONAPI::Exceptions::HasManyRelationExists.new(association_key_value)
36
+ end
30
37
  end
31
38
 
32
39
  def replace_has_many_links(association_type, association_key_values, context)
@@ -35,6 +42,18 @@ module JSONAPI
35
42
  @object.send("#{association.key}=", association_key_values)
36
43
  end
37
44
 
45
+ def create_has_one_link(association_type, association_key_value, context)
46
+ association = self.class._associations[association_type]
47
+
48
+ # ToDo: Add option to skip relations that already exist instead of returning an error?
49
+ relation = @object.send("#{association.key}")
50
+ if relation.nil?
51
+ @object.send("#{association.key}=", association_key_value)
52
+ else
53
+ raise JSONAPI::Exceptions::HasOneRelationExists.new
54
+ end
55
+ end
56
+
38
57
  def replace_has_one_link(association_type, association_key_value, context)
39
58
  association = self.class._associations[association_type]
40
59
 
@@ -44,7 +63,7 @@ module JSONAPI
44
63
  def remove_has_many_link(association_type, key, context)
45
64
  association = self.class._associations[association_type]
46
65
 
47
- @object.send(association.serialize_type_name).delete(key)
66
+ @object.send(association.type).delete(key)
48
67
  end
49
68
 
50
69
  def remove_has_one_link(association_type, context)
@@ -95,7 +114,7 @@ module JSONAPI
95
114
 
96
115
  class << self
97
116
  def inherited(base)
98
- base._attributes = (_attributes || Set.new).dup
117
+ base._attributes = (_attributes || {}).dup
99
118
  base._associations = (_associations || {}).dup
100
119
  base._allowed_filters = (_allowed_filters || Set.new).dup
101
120
 
@@ -119,14 +138,13 @@ module JSONAPI
119
138
 
120
139
  # Methods used in defining a resource class
121
140
  def attributes(*attrs)
122
- @_attributes.merge attrs
123
141
  attrs.each do |attr|
124
142
  attribute(attr)
125
143
  end
126
144
  end
127
145
 
128
- def attribute(attr)
129
- @_attributes.add attr
146
+ def attribute(attr, options = {})
147
+ @_attributes[attr] = options
130
148
  define_method attr do
131
149
  @object.send(attr)
132
150
  end unless method_defined?(attr)
@@ -136,6 +154,10 @@ module JSONAPI
136
154
  end unless method_defined?("#{attr}=")
137
155
  end
138
156
 
157
+ def default_attribute_options
158
+ {format: :default}
159
+ end
160
+
139
161
  def has_one(*attrs)
140
162
  _associate(Association::HasOne, *attrs)
141
163
  end
@@ -161,13 +183,17 @@ module JSONAPI
161
183
  end
162
184
 
163
185
  # Override in your resource to filter the updateable keys
164
- def updateable(keys, context = nil)
165
- keys
186
+ def updateable_fields(context)
187
+ fields
166
188
  end
167
189
 
168
190
  # Override in your resource to filter the createable keys
169
- def createable(keys, context = nil)
170
- keys
191
+ def createable_fields(context)
192
+ fields
193
+ end
194
+
195
+ def fields
196
+ _updateable_associations | _attributes.keys
171
197
  end
172
198
 
173
199
  # Override this method if you have more complex requirements than this basic find method provides
@@ -178,7 +204,7 @@ module JSONAPI
178
204
  filters.each do |filter, value|
179
205
  if _associations.include?(filter)
180
206
  if _associations[filter].is_a?(JSONAPI::Association::HasMany)
181
- includes.push(filter.to_sym)
207
+ includes.push(filter)
182
208
  where_filters["#{filter}.#{_associations[filter].primary_key}"] = value
183
209
  else
184
210
  where_filters["#{_associations[filter].key}"] = value
@@ -204,53 +230,6 @@ module JSONAPI
204
230
  self.new(obj)
205
231
  end
206
232
 
207
- def verify_create_params(object_params, context = nil)
208
- verify_params(object_params, createable(_updateable_associations | _attributes.to_a), context)
209
- end
210
-
211
- def verify_update_params(object_params, context = nil)
212
- verify_params(object_params, updateable(_updateable_associations | _attributes.to_a), context)
213
- end
214
-
215
- def verify_params(object_params, allowed_params, context)
216
- # push links into top level param list with attributes in order to check for invalid params
217
- if object_params[:links]
218
- object_params[:links].each do |link, value|
219
- object_params[link] = value
220
- end
221
- object_params.delete(:links)
222
- end
223
- verify_permitted_params(object_params, allowed_params)
224
-
225
- checked_attributes = {}
226
- checked_has_one_associations = {}
227
- checked_has_many_associations = {}
228
-
229
- object_params.each do |key, value|
230
- param = key.to_sym
231
-
232
- association = _associations[param]
233
-
234
- if association.is_a?(JSONAPI::Association::HasOne)
235
- checked_has_one_associations[param.to_sym] = resource_for(association.serialize_type_name).verify_key(value, context)
236
- elsif association.is_a?(JSONAPI::Association::HasMany)
237
- keys = []
238
- value.each do |value|
239
- keys.push(resource_for(association.serialize_type_name).verify_key(value, context))
240
- end
241
- checked_has_many_associations[param.to_sym] = keys
242
- else
243
- checked_attributes[param] = value
244
- end
245
- end
246
-
247
- return {
248
- attributes: checked_attributes,
249
- has_one: checked_has_one_associations,
250
- has_many: checked_has_many_associations
251
- }
252
- end
253
-
254
233
  def verify_filters(filters, context = nil)
255
234
  verified_filters = {}
256
235
  filters.each do |filter, raw_value|
@@ -261,7 +240,7 @@ module JSONAPI
261
240
  end
262
241
 
263
242
  def is_filter_association?(filter)
264
- filter == _serialize_as || _associations.include?(filter)
243
+ filter == _type || _associations.include?(filter)
265
244
  end
266
245
 
267
246
  def verify_filter(filter, raw, context = nil)
@@ -291,6 +270,10 @@ module JSONAPI
291
270
  end
292
271
 
293
272
  # quasi private class methods
273
+ def _attribute_options(attr)
274
+ default_attribute_options.merge(@_attributes[attr])
275
+ end
276
+
294
277
  def _updateable_associations
295
278
  associations = []
296
279
 
@@ -303,11 +286,12 @@ module JSONAPI
303
286
  end
304
287
 
305
288
  def _has_association?(type)
306
- @_associations.has_key?(type)
289
+ type = type.to_s
290
+ @_associations.has_key?(type.singularize.to_sym) || @_associations.has_key?(type.pluralize.to_sym)
307
291
  end
308
292
 
309
293
  def _association(type)
310
- type = type.to_sym unless type.is_a?(Symbol)
294
+ type = type.to_sym
311
295
  @_associations[type]
312
296
  end
313
297
 
@@ -315,16 +299,12 @@ module JSONAPI
315
299
  @_model_name ||= self.name.demodulize.sub(/Resource$/, '')
316
300
  end
317
301
 
318
- def _serialize_as
319
- @_serialize_as ||= self._type
320
- end
321
-
322
302
  def _key
323
303
  @_key ||= :id
324
304
  end
325
305
 
326
306
  def _as_parent_key
327
- @_as_parent_key ||= "#{_serialize_as.to_s.singularize}_#{_key}"
307
+ @_as_parent_key ||= "#{_type.to_s.singularize}_#{_key}"
328
308
  end
329
309
 
330
310
  def _allowed_filters
@@ -351,11 +331,7 @@ module JSONAPI
351
331
  end
352
332
 
353
333
  def _allowed_filter?(filter)
354
- _allowed_filters.include?(filter.to_sym)
355
- end
356
-
357
- def _validate_field(field)
358
- _attributes.include?(field) || _associations.key?(field)
334
+ _allowed_filters.include?(filter)
359
335
  end
360
336
 
361
337
  private
@@ -374,7 +350,7 @@ module JSONAPI
374
350
  end unless method_defined?(key)
375
351
 
376
352
  define_method "_#{attr}_object" do
377
- type_name = self.class._associations[attr].serialize_type_name
353
+ type_name = self.class._associations[attr].type
378
354
  resource_class = self.class.resource_for(type_name)
379
355
  if resource_class
380
356
  associated_object = @object.send attr
@@ -389,7 +365,7 @@ module JSONAPI
389
365
  end unless method_defined?(key)
390
366
 
391
367
  define_method "_#{attr}_objects" do
392
- type_name = self.class._associations[attr].serialize_type_name
368
+ type_name = self.class._associations[attr].type
393
369
  resource_class = self.class.resource_for(type_name)
394
370
  resources = []
395
371
  if resource_class
@@ -403,15 +379,6 @@ module JSONAPI
403
379
  end
404
380
  end
405
381
  end
406
-
407
- def verify_permitted_params(params, allowed_param_set)
408
- params_not_allowed = []
409
- params.keys.each do |key|
410
- param = key.to_sym
411
- params_not_allowed.push(param) unless allowed_param_set.include?(param)
412
- end
413
- raise JSONAPI::Exceptions::ParametersNotAllowed.new(params_not_allowed) if params_not_allowed.length > 0
414
- end
415
382
  end
416
383
  end
417
- end
384
+ end
@@ -15,7 +15,10 @@ module JSONAPI
15
15
 
16
16
  before_filter {
17
17
  begin
18
- @request = JSONAPI::Request.new(context, params)
18
+ @request = JSONAPI::Request.new(params, {
19
+ context: context,
20
+ key_formatter: key_formatter
21
+ })
19
22
  render_errors(@request.errors) unless @request.errors.empty?
20
23
  rescue => e
21
24
  handle_exceptions(e)
@@ -23,11 +26,13 @@ module JSONAPI
23
26
  }
24
27
 
25
28
  def index
26
- render json: JSONAPI::ResourceSerializer.new.serialize(
29
+ render json: JSONAPI::ResourceSerializer.new.serialize_to_hash(
27
30
  resource_klass.find(resource_klass.verify_filters(@request.filters, context), context),
28
- @request.includes,
29
- @request.fields,
30
- context)
31
+ include: @request.include,
32
+ fields: @request.fields,
33
+ context: context,
34
+ attribute_formatters: attribute_formatters,
35
+ key_formatter: key_formatter)
31
36
  rescue => e
32
37
  handle_exceptions(e)
33
38
  end
@@ -35,16 +40,22 @@ module JSONAPI
35
40
  def show
36
41
  keys = parse_key_array(params[resource_klass._key])
37
42
 
38
- resources = []
39
- keys.each do |key|
40
- resources.push(resource_klass.find_by_key(key, context))
43
+ if keys.length > 1
44
+ resources = []
45
+ keys.each do |key|
46
+ resources.push(resource_klass.find_by_key(key, context))
47
+ end
48
+ else
49
+ resources = resource_klass.find_by_key(keys[0], context)
41
50
  end
42
51
 
43
- render json: JSONAPI::ResourceSerializer.new.serialize(
52
+ render json: JSONAPI::ResourceSerializer.new.serialize_to_hash(
44
53
  resources,
45
- @request.includes,
46
- @request.fields,
47
- context)
54
+ include: @request.include,
55
+ fields: @request.fields,
56
+ context: context,
57
+ attribute_formatters: attribute_formatters,
58
+ key_formatter: key_formatter)
48
59
  rescue => e
49
60
  handle_exceptions(e)
50
61
  end
@@ -70,6 +81,10 @@ module JSONAPI
70
81
  process_request_operations
71
82
  end
72
83
 
84
+ def update_association
85
+ process_request_operations
86
+ end
87
+
73
88
  def update
74
89
  process_request_operations
75
90
  end
@@ -115,6 +130,20 @@ module JSONAPI
115
130
  {}
116
131
  end
117
132
 
133
+ # Control by setting in an initializer:
134
+ # JSONAPI.configuration.json_key_format = :camelized_key
135
+ #
136
+ # Override if you want to set a per controller key format.
137
+ # Must return a class derived from KeyFormatter.
138
+ def key_formatter
139
+ JSONAPI.configuration.key_formatter
140
+ end
141
+
142
+ # override to setup custom attribute_formatters
143
+ def attribute_formatters
144
+ {}
145
+ end
146
+
118
147
  def render_errors(errors, status = :bad_request)
119
148
  render(json: {errors: errors}, status: errors.count == 1 ? errors[0].status : status)
120
149
  end
@@ -138,18 +167,17 @@ module JSONAPI
138
167
  if errors.count > 0
139
168
  render :status => errors.count == 1 ? errors[0].status : :bad_request, json: {errors: errors}
140
169
  else
141
- # if patch
142
- render :status => results[0].code, json: JSONAPI::ResourceSerializer.new.serialize(resources,
143
- @request.includes,
144
- @request.fields,
145
- context)
146
- # else
147
- # result_hash = {}
148
- # resources.each do |resource|
149
- # result_hash.merge!(JSONAPI::ResourceSerializer.new.serialize(resource, @request.includes, @request.fields, context))
150
- # end
151
- # render :status => results.count == 1 ? results[0].code : :ok, json: result_hash
152
- # end
170
+ if results.length > 0 && resources.length > 0
171
+ render :status => results[0].code,
172
+ json: JSONAPI::ResourceSerializer.new.serialize_to_hash(resources.length > 1 ? resources : resources[0],
173
+ include: @request.include,
174
+ fields: @request.fields,
175
+ context: context,
176
+ attribute_formatters: attribute_formatters,
177
+ key_formatter: key_formatter)
178
+ else
179
+ render :status => results[0].code, json: nil
180
+ end
153
181
  end
154
182
  rescue => e
155
183
  handle_exceptions(e)
@@ -166,4 +194,4 @@ module JSONAPI
166
194
  end
167
195
  end
168
196
  end
169
- end
197
+ end
@@ -7,12 +7,10 @@ module JSONAPI
7
7
  module ClassMethods
8
8
  if RUBY_VERSION >= '2.0'
9
9
  def resource_for(type)
10
- begin
11
- resource_name = JSONAPI::Resource._resource_name_from_type(type)
12
- Object.const_get resource_name if resource_name
13
- rescue NameError
14
- nil
15
- end
10
+ resource_name = JSONAPI::Resource._resource_name_from_type(type)
11
+ Object.const_get resource_name if resource_name
12
+ rescue NameError
13
+ nil
16
14
  end
17
15
  else
18
16
  def resource_for(type)
@@ -1,7 +1,7 @@
1
1
  module JSONAPI
2
2
  class ResourceSerializer
3
3
 
4
- # Serializes a single resource, or an array of resources
4
+ # Converts a single resource, or an array of resources to a hash, conforming to the JSONAPI structure
5
5
  # include:
6
6
  # Purpose: determines which objects will be side loaded with the source objects in a linked section
7
7
  # Example: ['comments','author','comments.tags','author.posts']
@@ -9,45 +9,56 @@ module JSONAPI
9
9
  # Purpose: determines which fields are serialized for a resource type. This encompasses both attributes and
10
10
  # association ids in the links section for a resource. Fields are global for a resource type.
11
11
  # Example: { people: [:id, :email, :comments], posts: [:id, :title, :author], comments: [:id, :body, :post]}
12
- def serialize(source, include = [], fields = {}, context = nil)
13
- @fields = fields
14
- @context = context
12
+ def serialize_to_hash(source, options = {})
13
+ is_resource_collection = source.respond_to?(:to_ary)
14
+ return {} if source.nil? || (is_resource_collection && source.size == 0)
15
+
16
+ @fields = options.fetch(:fields, {})
17
+ include = options.fetch(:include, [])
18
+ @context = options.fetch(:context, nil)
19
+ @key_formatter = options.fetch(:key_formatter, JSONAPI.configuration.key_formatter)
20
+
15
21
  @linked_objects = {}
16
22
 
17
23
  requested_associations = parse_includes(include)
18
24
 
19
- if source.respond_to?(:to_ary)
20
- return {} if source.size == 0
21
- @primary_class_name = source[0].class._serialize_as
25
+ if is_resource_collection
26
+ @primary_class_name = source[0].class._type
22
27
  else
23
- @primary_class_name = source.class._serialize_as
28
+ @primary_class_name = source.class._type
24
29
  end
25
30
 
26
31
  process_primary(source, requested_associations)
27
32
 
28
33
  primary_class_name = @primary_class_name.to_sym
29
- primary_hash = {primary_class_name => []}
30
34
 
31
35
  linked_hash = {}
36
+ primary_objects = []
32
37
  @linked_objects.each do |class_name, objects|
33
38
  class_name = class_name.to_sym
34
39
 
35
- linked = []
40
+ linked_objects = []
36
41
  objects.each_value do |object|
37
42
  if object[:primary]
38
- primary_hash[primary_class_name].push(object[:object_hash])
43
+ primary_objects.push(object[:object_hash])
39
44
  else
40
- linked.push(object[:object_hash])
45
+ linked_objects.push(object[:object_hash])
41
46
  end
42
47
  end
43
- linked_hash[class_name] = linked unless linked.empty?
48
+ linked_hash[format_key(class_name)] = linked_objects unless linked_objects.empty?
44
49
  end
45
50
 
46
- if linked_hash.size > 0
47
- primary_hash.merge!({linked: linked_hash})
51
+ if is_resource_collection
52
+ primary_hash = {format_key(primary_class_name) => primary_objects}
53
+ else
54
+ primary_hash = {format_key(primary_class_name) => primary_objects[0]}
48
55
  end
49
56
 
50
- return primary_hash
57
+ if linked_hash.size > 0
58
+ primary_hash.merge({linked: linked_hash})
59
+ else
60
+ primary_hash
61
+ end
51
62
  end
52
63
 
53
64
  private
@@ -57,7 +68,7 @@ module JSONAPI
57
68
  def parse_includes(includes)
58
69
  requested_associations = {}
59
70
  includes.each do |include|
60
- include = include.to_s if include.is_a? Symbol
71
+ include = include.to_s.underscore
61
72
 
62
73
  pos = include.index('.')
63
74
  if pos
@@ -112,14 +123,17 @@ module JSONAPI
112
123
  end
113
124
 
114
125
  def attribute_hash(source)
115
- requested = requested_fields(source.class._serialize_as)
116
- fields = source.class._attributes.to_a
126
+ requested = requested_fields(source.class._type)
127
+ fields = source.class._attributes.keys.to_a
117
128
  unless requested.nil?
118
129
  fields = requested & fields
119
130
  end
120
131
 
121
132
  source.fetchable(fields, @context).each_with_object({}) do |name, hash|
122
- hash[name] = source.send(name)
133
+ hash[format_key(name)] = format_value(source.send(name),
134
+ source.class._attribute_options(name)[:format],
135
+ source,
136
+ @context)
123
137
  end
124
138
  end
125
139
 
@@ -127,7 +141,7 @@ module JSONAPI
127
141
  # class's fetchable method
128
142
  def links_hash(source, requested_associations)
129
143
  associations = source.class._associations
130
- requested = requested_fields(source.class._serialize_as)
144
+ requested = requested_fields(source.class._type)
131
145
  fields = associations.keys
132
146
  unless requested.nil?
133
147
  fields = requested & fields
@@ -141,7 +155,7 @@ module JSONAPI
141
155
  key = association.key
142
156
 
143
157
  if field_set.include?(name)
144
- hash[name] = source.send(key)
158
+ hash[format_key(name)] = source.send(key)
145
159
  end
146
160
 
147
161
  ia = requested_associations.is_a?(Hash) ? requested_associations[name] : nil
@@ -149,7 +163,7 @@ module JSONAPI
149
163
  include_linked_object = ia && ia[:include]
150
164
  include_linked_children = ia && ia[:include_children]
151
165
 
152
- type = association.serialize_type_name
166
+ type = association.type
153
167
 
154
168
  # If the object has been serialized once it will be in the related objects list,
155
169
  # but it's possible all children won't have been captured. So we must still go
@@ -157,13 +171,14 @@ module JSONAPI
157
171
  if include_linked_object || include_linked_children
158
172
  if association.is_a?(JSONAPI::Association::HasOne)
159
173
  object = source.send("_#{name}_object")
160
-
161
- id = object.send(association.primary_key)
162
- associations_only = already_serialized?(type, id)
163
- if include_linked_object && !associations_only
164
- add_linked_object(type, id, object_hash(object, ia[:include_related]))
165
- elsif include_linked_children || associations_only
166
- links_hash(object, ia[:include_related])
174
+ if object
175
+ id = object.send(association.primary_key)
176
+ associations_only = already_serialized?(type, id)
177
+ if include_linked_object && !associations_only
178
+ add_linked_object(type, id, object_hash(object, ia[:include_related]))
179
+ elsif include_linked_children || associations_only
180
+ links_hash(object, ia[:include_related])
181
+ end
167
182
  end
168
183
  elsif association.is_a?(JSONAPI::Association::HasMany)
169
184
  objects = source.send("_#{name}_objects")
@@ -183,16 +198,20 @@ module JSONAPI
183
198
  end
184
199
 
185
200
  def already_serialized?(type, id)
201
+ type = format_key(type)
186
202
  return @linked_objects.key?(type) && @linked_objects[type].key?(id)
187
203
  end
188
204
 
189
205
  # Sets that an object should be included in the primary document of the response.
190
206
  def set_primary(type, id)
207
+ type = format_key(type)
191
208
  @linked_objects[type][id][:primary] = true
192
209
  end
193
210
 
194
211
  # Collects the hashes for all objects processed by the serializer
195
212
  def add_linked_object(type, id, object_hash, primary = false)
213
+ type = format_key(type)
214
+
196
215
  unless @linked_objects.key?(type)
197
216
  @linked_objects[type] = {}
198
217
  end
@@ -205,5 +224,14 @@ module JSONAPI
205
224
  @linked_objects[type].store(id, {primary: primary, object_hash: object_hash})
206
225
  end
207
226
  end
227
+
228
+ def format_key(key)
229
+ @key_formatter.format(key)
230
+ end
231
+
232
+ def format_value(value, format, source, context)
233
+ value_formatter = JSONAPI::ValueFormatter.value_formatter_for(format)
234
+ value_formatter.format(value, source, context)
235
+ end
208
236
  end
209
- end
237
+ end
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Resources
3
- VERSION = "0.0.4"
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
@@ -47,7 +47,7 @@ module ActionDispatch
47
47
  end
48
48
 
49
49
  def links_methods(options)
50
- default_methods = [:show, :create, :destroy]
50
+ default_methods = [:show, :create, :destroy, :update]
51
51
  if only = options[:only]
52
52
  Array(only).map(&:to_sym)
53
53
  elsif except = options[:except]
@@ -73,6 +73,10 @@ module ActionDispatch
73
73
  match "links/#{link_type}", controller: res._type.to_s, action: 'create_association', association: link_type.to_s, via: [:post]
74
74
  end
75
75
 
76
+ if methods.include?(:update)
77
+ match "links/#{link_type}", controller: res._type.to_s, action: 'update_association', association: link_type.to_s, via: [:put]
78
+ end
79
+
76
80
  if methods.include?(:destroy)
77
81
  match "links/#{link_type}", controller: res._type.to_s, action: 'destroy_association', association: link_type.to_s, via: [:delete]
78
82
  end
@@ -94,6 +98,10 @@ module ActionDispatch
94
98
  match "links/#{link_type}", controller: res._type.to_s, action: 'create_association', association: link_type.to_s, via: [:post]
95
99
  end
96
100
 
101
+ if methods.include?(:update) && res._association(link_type).acts_as_set
102
+ match "links/#{link_type}", controller: res._type.to_s, action: 'update_association', association: link_type.to_s, via: [:put]
103
+ end
104
+
97
105
  if methods.include?(:destroy)
98
106
  match "links/#{link_type}/:keys", controller: res._type.to_s, action: 'destroy_association', association: link_type.to_s, via: [:delete]
99
107
  end
@@ -1,2 +1,4 @@
1
1
  require 'jsonapi/resource'
2
2
  require 'jsonapi/resources/version'
3
+ require 'jsonapi/configuration'
4
+ require 'jsonapi/formatter'