rest_framework 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.