rest_framework 0.10.0 → 0.11.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.
@@ -24,14 +24,13 @@ module RESTFramework::Mixins::BaseModelControllerMixin
24
24
  fields: nil,
25
25
  field_config: nil,
26
26
 
27
- # Options for what should be included/excluded from default fields.
28
- exclude_associations: false,
29
- }
30
- RRF_BASE_MODEL_INSTANCE_CONFIG = {
31
27
  # Attributes for finding records.
32
28
  find_by_fields: nil,
33
29
  find_by_query_param: "find_by",
34
30
 
31
+ # Options for what should be included/excluded from default fields.
32
+ exclude_associations: false,
33
+
35
34
  # Options for handling request body parameters.
36
35
  allowed_parameters: nil,
37
36
  filter_pk_from_request_body: true,
@@ -125,113 +124,72 @@ module RESTFramework::Mixins::BaseModelControllerMixin
125
124
  return input_fields
126
125
  end
127
126
 
128
- # Get a field's config, including defaults.
129
- def field_config_for(f)
130
- f = f.to_sym
131
- @_field_config_for ||= {}
132
- return @_field_config_for[f] if @_field_config_for[f]
133
-
134
- config = self.field_config&.dig(f) || {}
135
-
136
- # Default sub-fields if field is an association.
137
- if ref = self.get_model.reflections[f.to_s]
138
- if ref.polymorphic?
139
- columns = {}
140
- else
141
- model = ref.klass
142
- columns = model.columns_hash
143
- end
144
- config[:sub_fields] ||= RESTFramework::Utils.sub_fields_for(ref)
145
- config[:sub_fields] = config[:sub_fields].map(&:to_s)
146
-
147
- # Serialize very basic metadata about sub-fields.
148
- config[:sub_fields_metadata] = config[:sub_fields].map { |sf|
149
- v = {}
127
+ # Get a full field configuration, including defaults and inferred values.
128
+ def field_configuration
129
+ return @field_configuration if @field_configuration
150
130
 
151
- if columns[sf]
152
- v[:kind] = "column"
153
- end
154
-
155
- next [sf, v]
156
- }.to_h.compact.presence
157
- end
158
-
159
- return @_field_config_for[f] = config.compact
160
- end
161
-
162
- # Get metadata about the resource's fields.
163
- def fields_metadata
164
- return @_fields_metadata if @_fields_metadata
165
-
166
- # Get metadata sources.
131
+ field_config = self.field_config&.with_indifferent_access || {}
167
132
  model = self.get_model
168
- fields = self.get_fields.map(&:to_s)
169
133
  columns = model.columns_hash
170
134
  column_defaults = model.column_defaults
171
135
  reflections = model.reflections
172
136
  attributes = model._default_attributes
173
137
  readonly_attributes = model.readonly_attributes
174
- exclude_body_fields = self.exclude_body_fields.map(&:to_s)
138
+ exclude_body_fields = self.exclude_body_fields&.map(&:to_s)
175
139
  rich_text_association_names = model.reflect_on_all_associations(:has_one)
176
140
  .collect(&:name)
177
141
  .select { |n| n.to_s.start_with?("rich_text_") }
178
142
  attachment_reflections = model.attachment_reflections
179
143
 
180
- return @_fields_metadata = fields.map { |f|
181
- # Initialize metadata to make the order consistent.
182
- metadata = {
183
- type: nil,
184
- kind: nil,
185
- label: self.label_for(f),
186
- primary_key: nil,
187
- required: nil,
188
- read_only: nil,
189
- }
144
+ return @field_configuration = self.get_fields.map { |f|
145
+ cfg = field_config[f]&.dup || {}
146
+ cfg[:label] ||= self.label_for(f)
190
147
 
191
- # Determine `primary_key` based on model.
148
+ # Annotate primary key.
192
149
  if model.primary_key == f
193
- metadata[:primary_key] = true
150
+ cfg[:primary_key] = true
151
+
152
+ unless cfg.key?(:readonly)
153
+ cfg[:readonly] = true
154
+ end
194
155
  end
195
156
 
196
- # Determine if the field is a read-only attribute.
197
- if metadata[:primary_key] || f.in?(readonly_attributes) || f.in?(exclude_body_fields)
198
- metadata[:read_only] = true
157
+ # Annotate readonly attributes.
158
+ if f.in?(readonly_attributes) || f.in?(exclude_body_fields)
159
+ cfg[:readonly] = true
199
160
  end
200
161
 
201
- # Determine `type`, `required`, `label`, and `kind` based on schema.
162
+ # Annotate column data.
202
163
  if column = columns[f]
203
- metadata[:kind] = "column"
204
- metadata[:type] = column.type
205
- metadata[:required] = true unless column.null
164
+ cfg[:kind] = "column"
165
+ cfg[:type] ||= column.type
166
+ cfg[:required] = true unless column.null
206
167
  end
207
168
 
208
- # Determine `default` based on schema; we use `column_defaults` rather than `columns_hash`
209
- # because these are casted to the proper type.
210
- column_default = column_defaults[f]
211
- unless column_default.nil?
212
- metadata[:default] = column_default
169
+ # Add default values from the model's schema.
170
+ if column_default = column_defaults[f] && !cfg[:default].nil?
171
+ cfg[:default] ||= column_default
213
172
  end
214
173
 
215
- # Extract details from the model's attributes hash.
216
- if attributes.key?(f) && attribute = attributes[f]
217
- unless metadata.key?(:default)
218
- default = attribute.value_before_type_cast
219
- metadata[:default] = default unless default.nil?
174
+ # Add metadata from the model's attributes hash.
175
+ if attribute = attributes[f]
176
+ if cfg[:default].nil? && default = attribute.value_before_type_cast
177
+ cfg[:default] = default
220
178
  end
221
- metadata[:kind] ||= "attribute"
179
+ cfg[:kind] ||= "attribute"
222
180
 
223
181
  # Get any type information from the attribute.
224
182
  if type = attribute.type
225
- metadata[:type] ||= type.type
183
+ cfg[:type] ||= type.type if type.type
226
184
 
227
185
  # Get enum variants.
228
186
  if type.is_a?(ActiveRecord::Enum::EnumType)
229
- metadata[:enum_variants] = type.send(:mapping)
187
+ cfg[:enum_variants] = type.send(:mapping)
230
188
 
231
- # Custom integration with `translate_enum`.
189
+ # TranslateEnum Integration:
232
190
  translate_method = "translated_#{f.pluralize}"
233
191
  if model.respond_to?(translate_method)
234
- metadata[:enum_translations] = model.send(translate_method)
192
+ cfg[:enum_translations] = model.send(translate_method)
235
193
  end
236
194
  end
237
195
  end
@@ -239,53 +197,65 @@ module RESTFramework::Mixins::BaseModelControllerMixin
239
197
 
240
198
  # Get association metadata.
241
199
  if ref = reflections[f]
242
- metadata[:kind] = "association"
200
+ cfg[:kind] = "association"
201
+
202
+ # Determine sub-fields for associations.
203
+ if ref.polymorphic?
204
+ ref_columns = {}
205
+ else
206
+ ref_columns = ref.klass.columns_hash
207
+ end
208
+ cfg[:sub_fields] ||= RESTFramework::Utils.sub_fields_for(ref)
209
+ cfg[:sub_fields] = cfg[:sub_fields].map(&:to_s)
210
+
211
+ # Very basic metadata about sub-fields.
212
+ cfg[:sub_fields_metadata] = cfg[:sub_fields].map { |sf|
213
+ v = {}
214
+
215
+ if ref_columns[sf]
216
+ v[:kind] = "column"
217
+ else
218
+ v[:kind] = "method"
219
+ end
220
+
221
+ next [sf, v]
222
+ }.to_h.compact.presence
243
223
 
244
224
  # Determine if we render id/ids fields. Unfortunately, `has_one` does not provide this
245
225
  # interface.
246
- if self.permit_id_assignment && id_field = RESTFramework::Utils.get_id_field(f, ref)
247
- metadata[:id_field] = id_field
226
+ if self.permit_id_assignment && id_field = RESTFramework::Utils.id_field_for(f, ref)
227
+ cfg[:id_field] = id_field
248
228
  end
249
229
 
250
230
  # Determine if we render nested attributes options.
251
231
  if self.permit_nested_attributes_assignment && (
252
232
  nested_opts = model.nested_attributes_options[f.to_sym].presence
253
233
  )
254
- metadata[:nested_attributes_options] = {field: "#{f}_attributes", **nested_opts}
234
+ cfg[:nested_attributes_options] = {field: "#{f}_attributes", **nested_opts}
255
235
  end
256
236
 
257
237
  begin
258
- pk = ref.active_record_primary_key
238
+ cfg[:association_pk] = ref.active_record_primary_key
259
239
  rescue ActiveRecord::UnknownPrimaryKey
260
240
  end
261
- metadata[:association] = {
262
- macro: ref.macro,
263
- collection: ref.collection?,
264
- class_name: ref.class_name,
265
- foreign_key: ref.foreign_key,
266
- primary_key: pk,
267
- polymorphic: ref.polymorphic?,
268
- table_name: ref.polymorphic? ? nil : ref.table_name,
269
- options: ref.options.as_json.presence,
270
- }.compact
241
+
242
+ cfg[:reflection] = ref
271
243
  end
272
244
 
273
245
  # Determine if this is an ActionText "rich text".
274
246
  if :"rich_text_#{f}".in?(rich_text_association_names)
275
- metadata[:kind] = "rich_text"
247
+ cfg[:kind] = "rich_text"
276
248
  end
277
249
 
278
250
  # Determine if this is an ActiveStorage attachment.
279
251
  if ref = attachment_reflections[f]
280
- metadata[:kind] = "attachment"
281
- metadata[:attachment] = {
282
- macro: ref.macro,
283
- }
252
+ cfg[:kind] = "attachment"
253
+ cfg[:attachment_type] = ref.macro
284
254
  end
285
255
 
286
256
  # Determine if this is just a method.
287
- if !metadata[:kind] && model.method_defined?(f)
288
- metadata[:kind] = "method"
257
+ if !cfg[:kind] && model.method_defined?(f)
258
+ cfg[:kind] = "method"
289
259
  end
290
260
 
291
261
  # Collect validator options into a hash on their type, while also updating `required` based
@@ -299,7 +269,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
299
269
  next if IGNORE_VALIDATORS_WITH_KEYS.any? { |k| options.key?(k) }
300
270
 
301
271
  # Update `required` if we find a presence validator.
302
- metadata[:required] = true if kind == :presence
272
+ cfg[:required] = true if kind == :presence
303
273
 
304
274
  # Resolve procs (and lambdas), and symbols for certain arguments.
305
275
  if options[:in].is_a?(Proc)
@@ -308,27 +278,65 @@ module RESTFramework::Mixins::BaseModelControllerMixin
308
278
  options = options.merge(in: model.send(options[:in]))
309
279
  end
310
280
 
311
- metadata[:validators] ||= {}
312
- metadata[:validators][kind] ||= []
313
- metadata[:validators][kind] << options
281
+ cfg[:validators] ||= {}
282
+ cfg[:validators][kind] ||= []
283
+ cfg[:validators][kind] << options
314
284
  end
315
285
 
316
- # Serialize any field config.
317
- metadata[:config] = self.field_config_for(f).presence
318
-
319
- next [f, metadata.compact]
320
- }.to_h
286
+ next [f, cfg]
287
+ }.to_h.with_indifferent_access
321
288
  end
322
289
 
323
- # Get a hash of metadata to be rendered in the `OPTIONS` response.
324
- def options_metadata
325
- return super.merge(
326
- {
327
- primary_key: self.get_model.primary_key,
328
- fields: self.fields_metadata,
329
- callbacks: self._process_action_callbacks.as_json,
330
- },
331
- )
290
+ def openapi_schema
291
+ return @openapi_schema if @openapi_schema
292
+
293
+ field_configuration = self.field_configuration
294
+ @openapi_schema = {
295
+ required: field_configuration.select { |_, cfg| cfg[:required] }.keys,
296
+ type: "object",
297
+ properties: field_configuration.map { |f, cfg|
298
+ v = {title: cfg[:label]}
299
+
300
+ if cfg[:kind] == "association"
301
+ v[:type] = cfg[:reflection].collection? ? "array" : "object"
302
+ elsif cfg[:kind] == "rich_text"
303
+ v[:type] = "string"
304
+ v[:"x-rrf-rich_text"] = true
305
+ elsif cfg[:kind] == "attachment"
306
+ v[:type] = "string"
307
+ v[:"x-rrf-attachment"] = cfg[:attachment_type]
308
+ else
309
+ v[:type] = cfg[:type]
310
+ end
311
+
312
+ v[:readOnly] = true if cfg[:readonly]
313
+ v[:default] = cfg[:default] if cfg.key?(:default)
314
+
315
+ if enum_variants = cfg[:enum_variants]
316
+ v[:enum] = enum_variants.keys
317
+ v[:"x-rrf-enum_variants"] = enum_variants
318
+ end
319
+
320
+ if validators = cfg[:validators]
321
+ v[:"x-rrf-validators"] = validators
322
+ end
323
+
324
+ v[:"x-rrf-kind"] = cfg[:kind] if cfg[:kind]
325
+
326
+ if cfg[:reflection]
327
+ v[:"x-rrf-reflection"] = cfg[:reflection]
328
+ v[:"x-rrf-association_pk"] = cfg[:association_pk]
329
+ v[:"x-rrf-sub_fields"] = cfg[:sub_fields]
330
+ v[:"x-rrf-sub_fields_metadata"] = cfg[:sub_fields_metadata]
331
+ v[:"x-rrf-id_field"] = cfg[:id_field]
332
+ v[:"x-rrf-nested_attributes_options"] = cfg[:nested_attributes_options]
333
+ end
334
+
335
+ next [f, v]
336
+ }.to_h,
337
+ }
338
+
339
+ return @openapi_schema
332
340
  end
333
341
 
334
342
  def setup_delegation
@@ -341,9 +349,9 @@ module RESTFramework::Mixins::BaseModelControllerMixin
341
349
  model = self.class.get_model
342
350
 
343
351
  if model.method(action).parameters.last&.first == :keyrest
344
- return api_response(model.send(action, **params))
352
+ return render_api(model.send(action, **params))
345
353
  else
346
- return api_response(model.send(action))
354
+ return render_api(model.send(action))
347
355
  end
348
356
  end
349
357
  end
@@ -357,9 +365,9 @@ module RESTFramework::Mixins::BaseModelControllerMixin
357
365
  record = self.get_record
358
366
 
359
367
  if record.method(action).parameters.last&.first == :keyrest
360
- return api_response(record.send(action, **params))
368
+ return render_api(record.send(action, **params))
361
369
  else
362
- return api_response(record.send(action))
370
+ return render_api(record.send(action))
363
371
  end
364
372
  end
365
373
  end
@@ -373,7 +381,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
373
381
  # self.setup_channel
374
382
 
375
383
  if RESTFramework.config.freeze_config
376
- (self::RRF_BASE_MODEL_CONFIG.keys + self::RRF_BASE_MODEL_INSTANCE_CONFIG.keys).each { |k|
384
+ self::RRF_BASE_MODEL_CONFIG.keys.each { |k|
377
385
  v = self.send(k)
378
386
  v.freeze if v.is_a?(Hash) || v.is_a?(Array)
379
387
  }
@@ -395,27 +403,51 @@ module RESTFramework::Mixins::BaseModelControllerMixin
395
403
 
396
404
  base.class_attribute(a, default: default, instance_accessor: false)
397
405
  end
398
- RRF_BASE_MODEL_INSTANCE_CONFIG.each do |a, default|
399
- next if base.respond_to?(a)
400
-
401
- base.class_attribute(a, default: default)
402
- end
403
406
  end
404
407
 
405
- # Get a list of fields for this controller.
406
408
  def get_fields
407
409
  return self.class.get_fields(input_fields: self.class.fields)
408
410
  end
409
411
 
410
- def options_metadata
411
- return self.class.options_metadata
412
+ def openapi_metadata
413
+ data = super
414
+ routes = self.route_groups.values[0]
415
+ schema_name = routes[0][:controller].camelize.gsub("::", ".")
416
+
417
+ # Insert schema into metadata.
418
+ data[:components] ||= {}
419
+ data[:components][:schemas] ||= {}
420
+ data[:components][:schemas][schema_name] = self.class.openapi_schema
421
+
422
+ # Reference schema for specific actions with a `requestBody`.
423
+ data[:paths].each do |_path, actions|
424
+ actions.each do |_method, action|
425
+ next unless action.is_a?(Hash)
426
+
427
+ injectables = [action.dig(:requestBody, :content), *action[:responses].values.map { |r|
428
+ r[:content]
429
+ }].compact
430
+ injectables.each do |i|
431
+ i.each do |_, v|
432
+ v[:schema] = {"$ref" => "#/components/schemas/#{schema_name}"}
433
+ end
434
+ end
435
+ end
436
+ end
437
+
438
+ return data.merge(
439
+ {
440
+ "x-rrf-primary_key" => self.class.get_model.primary_key,
441
+ "x-rrf-callbacks" => self._process_action_callbacks.as_json,
442
+ },
443
+ )
412
444
  end
413
445
 
414
- # Get a list of parameters allowed for the current action.
446
+ # Get a hash of strong parameters for the current action.
415
447
  def get_allowed_parameters
416
448
  return @_get_allowed_parameters if defined?(@_get_allowed_parameters)
417
449
 
418
- @_get_allowed_parameters = self.allowed_parameters
450
+ @_get_allowed_parameters = self.class.allowed_parameters
419
451
  return @_get_allowed_parameters if @_get_allowed_parameters
420
452
 
421
453
  # Assemble strong parameters.
@@ -424,6 +456,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
424
456
  reflections = self.class.get_model.reflections
425
457
  @_get_allowed_parameters = self.get_fields.map { |f|
426
458
  f = f.to_s
459
+ config = self.class.field_configuration[f]
427
460
 
428
461
  # ActionText Integration:
429
462
  if self.class.enable_action_text && reflections.key?("rich_test_#{f}")
@@ -442,28 +475,30 @@ module RESTFramework::Mixins::BaseModelControllerMixin
442
475
  next nil
443
476
  end
444
477
 
445
- # Return field if it's not an association.
446
- next f unless ref = reflections[f]
478
+ if config[:reflection]
479
+ # Add `_id`/`_ids` variations for associations.
480
+ if id_field = config[:id_field]
481
+ if id_field.ends_with?("_ids")
482
+ hash_variations[id_field] = []
483
+ else
484
+ variations << id_field
485
+ end
486
+ end
447
487
 
448
- # Add `_id`/`_ids` variations for associations.
449
- if self.permit_id_assignment && id_field = RESTFramework::Utils.get_id_field(f, ref)
450
- if id_field.ends_with?("_ids")
451
- hash_variations[id_field] = []
452
- else
453
- variations << id_field
488
+ # Add `_attributes` variations for associations.
489
+ # TODO: Consider adjusting this based on `nested_attributes_options`.
490
+ if self.class.permit_nested_attributes_assignment
491
+ hash_variations["#{f}_attributes"] = (
492
+ config[:sub_fields] + ["_destroy"]
493
+ )
454
494
  end
455
- end
456
495
 
457
- # Add `_attributes` variations for associations.
458
- if self.permit_nested_attributes_assignment
459
- hash_variations["#{f}_attributes"] = (
460
- self.class.field_config_for(f)[:sub_fields] + ["_destroy"]
461
- )
496
+ # Associations are not allowed to be submitted in their bare form (if they are submitted
497
+ # that way, they will be translated to either id/ids or nested attributes assignment).
498
+ next nil
462
499
  end
463
500
 
464
- # Associations are not allowed to be submitted in their bare form (if they are submitted that
465
- # way, they will be translated to either ID assignment or nested attributes assignment).
466
- next nil
501
+ next f
467
502
  }.compact
468
503
  @_get_allowed_parameters += variations
469
504
  @_get_allowed_parameters << hash_variations
@@ -471,22 +506,11 @@ module RESTFramework::Mixins::BaseModelControllerMixin
471
506
  return @_get_allowed_parameters
472
507
  end
473
508
 
474
- def serializer_class
509
+ def get_serializer_class
475
510
  return super || RESTFramework::NativeSerializer
476
511
  end
477
512
 
478
- def apply_filters(data)
479
- # TODO: Compatibility; remove in 1.0.
480
- if filtered_data = self.try(:get_filtered_data, data)
481
- return filtered_data
482
- end
483
-
484
- return self.filter_backends&.reduce(data) { |d, filter|
485
- filter.new(controller: self).filter_data(d)
486
- } || data
487
- end
488
-
489
- # Use strong parameters to filter the request body using the configured allowed parameters.
513
+ # Use strong parameters to filter the request body.
490
514
  def get_body_params(bulk_mode: nil)
491
515
  data = self.request.request_parameters
492
516
  pk = self.class.get_model&.primary_key
@@ -502,7 +526,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
502
526
  # Assume nested attributes assignment.
503
527
  attributes_key = "#{name}_attributes"
504
528
  data[attributes_key] = data.delete(name) unless data[attributes_key]
505
- elsif id_field = RESTFramework::Utils.get_id_field(name, ref)
529
+ elsif id_field = RESTFramework::Utils.id_field_for(name, ref)
506
530
  # Assume id/ids assignment.
507
531
  data[id_field] = data.delete(name) unless data[id_field]
508
532
  end
@@ -571,12 +595,12 @@ module RESTFramework::Mixins::BaseModelControllerMixin
571
595
  end
572
596
 
573
597
  # Filter primary key, if configured.
574
- if self.filter_pk_from_request_body && bulk_mode != :update
598
+ if self.class.filter_pk_from_request_body && bulk_mode != :update
575
599
  body_params.delete(pk)
576
600
  end
577
601
 
578
602
  # Filter fields in `exclude_body_fields`.
579
- (self.exclude_body_fields || []).each { |f| body_params.delete(f) }
603
+ (self.class.exclude_body_fields || []).each { |f| body_params.delete(f) }
580
604
 
581
605
  return body_params
582
606
  end
@@ -595,13 +619,16 @@ module RESTFramework::Mixins::BaseModelControllerMixin
595
619
  return nil
596
620
  end
597
621
 
598
- # Get the records this controller has access to *after* any filtering is applied.
622
+ # Filter the recordset and return records this request has access to.
599
623
  def get_records
600
- return @records ||= self.apply_filters(self.get_recordset)
624
+ data = self.get_recordset
625
+
626
+ return @records ||= self.class.filter_backends&.reduce(data) { |d, filter|
627
+ filter.new(controller: self).filter_data(d)
628
+ } || data
601
629
  end
602
630
 
603
- # Get a single record by primary key or another column, if allowed. The return value is memoized
604
- # and exposed to the view as the `@record` instance variable.
631
+ # Get a single record by primary key or another column, if allowed.
605
632
  def get_record
606
633
  return @record if @record
607
634
 
@@ -609,9 +636,9 @@ module RESTFramework::Mixins::BaseModelControllerMixin
609
636
  is_pk = true
610
637
 
611
638
  # Find by another column if it's permitted.
612
- if find_by_param = self.find_by_query_param.presence
639
+ if find_by_param = self.class.find_by_query_param.presence
613
640
  if find_by = params[find_by_param].presence
614
- find_by_fields = self.find_by_fields&.map(&:to_s)
641
+ find_by_fields = self.class.find_by_fields&.map(&:to_s)
615
642
 
616
643
  if !find_by_fields || find_by.in?(find_by_fields)
617
644
  is_pk = false unless find_by_key == find_by
@@ -621,7 +648,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
621
648
  end
622
649
 
623
650
  # Get the recordset, filtering if configured.
624
- collection = if self.filter_recordset_before_find
651
+ collection = if self.class.filter_recordset_before_find
625
652
  self.get_records
626
653
  else
627
654
  self.get_recordset
@@ -637,7 +664,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
637
664
 
638
665
  # Determine what collection to call `create` on.
639
666
  def get_create_from
640
- if self.create_from_recordset
667
+ if self.class.create_from_recordset
641
668
  # Create with any properties inherited from the recordset. We exclude any `select` clauses
642
669
  # in case model callbacks need to call `count` on this collection, which typically raises a
643
670
  # SQL `SyntaxError`.
@@ -654,19 +681,17 @@ module RESTFramework::Mixins::BaseModelControllerMixin
654
681
  # This is kinda slow, so perhaps we should eventually integrate `errors` serialization into
655
682
  # the serializer directly. This would fail for active model serializers, but maybe we don't
656
683
  # care?
657
- s = RESTFramework::Utils.wrap_ams(self.serializer_class)
658
- serialized_records = records.map do |record|
684
+ s = RESTFramework::Utils.wrap_ams(self.get_serializer_class)
685
+ return records.map do |record|
659
686
  s.new(record, controller: self).serialize.merge!({errors: record.errors.presence}.compact)
660
687
  end
661
-
662
- return serialized_records
663
688
  end
664
689
  end
665
690
 
666
691
  # Mixin for listing records.
667
692
  module RESTFramework::Mixins::ListModelMixin
668
693
  def index
669
- return api_response(self.get_index_records)
694
+ return render_api(self.get_index_records)
670
695
  end
671
696
 
672
697
  # Get records with both filtering and pagination applied.
@@ -674,13 +699,13 @@ module RESTFramework::Mixins::ListModelMixin
674
699
  records = self.get_records
675
700
 
676
701
  # Handle pagination, if enabled.
677
- if self.paginator_class
678
- # If there is no `max_page_size`, `page_size_query_param` is not `nil`, and the page size is
679
- # set to "0", then skip pagination.
680
- unless !self.max_page_size &&
681
- self.page_size_query_param &&
682
- params[self.page_size_query_param] == "0"
683
- paginator = self.paginator_class.new(data: records, controller: self)
702
+ if paginator_class = self.class.paginator_class
703
+ # Paginate if there is a `max_page_size`, or if there is no `page_size_query_param`, or if the
704
+ # page size is not set to "0".
705
+ max_page_size = self.class.max_page_size
706
+ page_size_query_param = self.class.page_size_query_param
707
+ if max_page_size || !page_size_query_param || params[page_size_query_param] != "0"
708
+ paginator = paginator_class.new(data: records, controller: self)
684
709
  page = paginator.get_page
685
710
  serialized_page = self.serialize(page)
686
711
  return paginator.get_paginated_response(serialized_page)
@@ -694,14 +719,14 @@ end
694
719
  # Mixin for showing records.
695
720
  module RESTFramework::Mixins::ShowModelMixin
696
721
  def show
697
- return api_response(self.get_record)
722
+ return render_api(self.get_record)
698
723
  end
699
724
  end
700
725
 
701
726
  # Mixin for creating records.
702
727
  module RESTFramework::Mixins::CreateModelMixin
703
728
  def create
704
- return api_response(self.create!, status: :created)
729
+ return render_api(self.create!, status: :created)
705
730
  end
706
731
 
707
732
  # Perform the `create!` call and return the created record.
@@ -713,7 +738,7 @@ end
713
738
  # Mixin for updating records.
714
739
  module RESTFramework::Mixins::UpdateModelMixin
715
740
  def update
716
- return api_response(self.update!)
741
+ return render_api(self.update!)
717
742
  end
718
743
 
719
744
  # Perform the `update!` call and return the updated record.
@@ -728,7 +753,7 @@ end
728
753
  module RESTFramework::Mixins::DestroyModelMixin
729
754
  def destroy
730
755
  self.destroy!
731
- return api_response("")
756
+ return render_api("")
732
757
  end
733
758
 
734
759
  # Perform the `destroy!` call and return the destroyed (and frozen) record.