rest_framework 1.0.0.rc1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -2
- data/VERSION +1 -1
- data/app/views/rest_framework/_head.html.erb +3 -1
- data/app/views/rest_framework/_heading.html.erb +1 -0
- data/app/views/rest_framework/head/_extra.html.erb +0 -0
- data/app/views/rest_framework/heading/_actions.html.erb +2 -1
- data/app/views/rest_framework/heading/_extra.html.erb +0 -0
- data/app/views/rest_framework/heading/actions/_extra.html.erb +0 -0
- data/app/views/rest_framework/routes_and_forms/_html_form.html.erb +2 -2
- data/lib/rest_framework/errors/nil_passed_to_render_api_error.rb +1 -1
- data/lib/rest_framework/errors/unknown_model_error.rb +1 -1
- data/lib/rest_framework/filters/ordering_filter.rb +3 -3
- data/lib/rest_framework/filters/query_filter.rb +14 -14
- data/lib/rest_framework/filters/ransack_filter.rb +1 -1
- data/lib/rest_framework/filters/search_filter.rb +3 -3
- data/lib/rest_framework/generators/controller_generator.rb +2 -2
- data/lib/rest_framework/mixins/base_controller_mixin.rb +34 -33
- data/lib/rest_framework/mixins/bulk_model_controller_mixin.rb +8 -15
- data/lib/rest_framework/mixins/model_controller_mixin.rb +91 -65
- data/lib/rest_framework/paginators/page_number_paginator.rb +6 -6
- data/lib/rest_framework/routers.rb +10 -16
- data/lib/rest_framework/serializers/active_model_serializer_adapter_factory.rb +4 -2
- data/lib/rest_framework/serializers/base_serializer.rb +7 -5
- data/lib/rest_framework/serializers/native_serializer.rb +82 -129
- data/lib/rest_framework/utils.rb +27 -26
- data/lib/rest_framework/version.rb +1 -1
- data/lib/rest_framework.rb +24 -17
- data/vendor/assets/javascripts/rest_framework/external.min.js +4 -4
- data/vendor/assets/stylesheets/rest_framework/external.min.css +3 -3
- metadata +8 -5
|
@@ -5,13 +5,13 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
5
5
|
return value unless BASE64_REGEX.match?(value)
|
|
6
6
|
|
|
7
7
|
_, content_type, payload = value.match(BASE64_REGEX).to_a
|
|
8
|
-
|
|
8
|
+
{
|
|
9
9
|
io: StringIO.new(Base64.decode64(payload)),
|
|
10
10
|
content_type: content_type,
|
|
11
11
|
filename: "file_#{field}#{Rack::Mime::MIME_TYPES.invert[content_type]}",
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
ACTIVESTORAGE_KEYS = [:io, :content_type, :filename, :identify, :key]
|
|
14
|
+
ACTIVESTORAGE_KEYS = [ :io, :content_type, :filename, :identify, :key ]
|
|
15
15
|
|
|
16
16
|
include RESTFramework::BaseControllerMixin
|
|
17
17
|
|
|
@@ -23,27 +23,30 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
23
23
|
# Attributes for configuring record fields.
|
|
24
24
|
fields: nil,
|
|
25
25
|
field_config: nil,
|
|
26
|
+
read_only_fields: RESTFramework.config.read_only_fields,
|
|
27
|
+
write_only_fields: RESTFramework.config.write_only_fields,
|
|
28
|
+
hidden_fields: nil,
|
|
26
29
|
|
|
27
30
|
# Attributes for finding records.
|
|
28
31
|
find_by_fields: nil,
|
|
29
|
-
find_by_query_param: "find_by",
|
|
32
|
+
find_by_query_param: "find_by".freeze,
|
|
30
33
|
|
|
31
34
|
# Options for what should be included/excluded from default fields.
|
|
32
35
|
exclude_associations: false,
|
|
33
36
|
|
|
34
37
|
# Options for handling request body parameters.
|
|
35
38
|
allowed_parameters: nil,
|
|
36
|
-
filter_pk_from_request_body: true,
|
|
37
|
-
exclude_body_fields: RESTFramework.config.exclude_body_fields,
|
|
38
39
|
|
|
39
40
|
# Attributes for the default native serializer.
|
|
40
41
|
native_serializer_config: nil,
|
|
41
42
|
native_serializer_singular_config: nil,
|
|
42
43
|
native_serializer_plural_config: nil,
|
|
43
|
-
native_serializer_only_query_param: "only",
|
|
44
|
-
native_serializer_except_query_param: "except",
|
|
44
|
+
native_serializer_only_query_param: "only".freeze,
|
|
45
|
+
native_serializer_except_query_param: "except".freeze,
|
|
46
|
+
native_serializer_include_query_param: "include".freeze,
|
|
47
|
+
native_serializer_exclude_query_param: "exclude".freeze,
|
|
45
48
|
native_serializer_associations_limit: nil,
|
|
46
|
-
native_serializer_associations_limit_query_param: "associations_limit",
|
|
49
|
+
native_serializer_associations_limit_query_param: "associations_limit".freeze,
|
|
47
50
|
native_serializer_include_associations_count: false,
|
|
48
51
|
|
|
49
52
|
# Attributes for filtering, ordering, and searching.
|
|
@@ -51,19 +54,19 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
51
54
|
RESTFramework::QueryFilter,
|
|
52
55
|
RESTFramework::OrderingFilter,
|
|
53
56
|
RESTFramework::SearchFilter,
|
|
54
|
-
],
|
|
57
|
+
].freeze,
|
|
55
58
|
filter_recordset_before_find: true,
|
|
56
59
|
filter_fields: nil,
|
|
57
60
|
ordering_fields: nil,
|
|
58
|
-
ordering_query_param: "ordering",
|
|
61
|
+
ordering_query_param: "ordering".freeze,
|
|
59
62
|
ordering_no_reorder: false,
|
|
60
63
|
search_fields: nil,
|
|
61
|
-
search_query_param: "search",
|
|
64
|
+
search_query_param: "search".freeze,
|
|
62
65
|
search_ilike: false,
|
|
63
66
|
ransack_options: nil,
|
|
64
|
-
ransack_query_param: "q",
|
|
67
|
+
ransack_query_param: "q".freeze,
|
|
65
68
|
ransack_distinct: true,
|
|
66
|
-
ransack_distinct_query_param: "distinct",
|
|
69
|
+
ransack_distinct_query_param: "distinct".freeze,
|
|
67
70
|
|
|
68
71
|
# Options for association assignment.
|
|
69
72
|
permit_id_assignment: true,
|
|
@@ -74,7 +77,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
module ClassMethods
|
|
77
|
-
IGNORE_VALIDATORS_WITH_KEYS = [:if, :unless].freeze
|
|
80
|
+
IGNORE_VALIDATORS_WITH_KEYS = [ :if, :unless ].freeze
|
|
78
81
|
|
|
79
82
|
def get_model
|
|
80
83
|
return @model if @model
|
|
@@ -91,7 +94,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
91
94
|
|
|
92
95
|
# Override to include ActiveRecord i18n-translated column names.
|
|
93
96
|
def label_for(s)
|
|
94
|
-
|
|
97
|
+
self.get_model.human_attribute_name(s, default: super)
|
|
95
98
|
end
|
|
96
99
|
|
|
97
100
|
# Get the available fields. Fallback to this controller's model columns, or an empty array. This
|
|
@@ -121,7 +124,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
121
124
|
input_fields = input_fields.map(&:to_s)
|
|
122
125
|
end
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
input_fields
|
|
125
128
|
end
|
|
126
129
|
|
|
127
130
|
# Get a full field configuration, including defaults and inferred values.
|
|
@@ -135,13 +138,15 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
135
138
|
reflections = model.reflections
|
|
136
139
|
attributes = model._default_attributes
|
|
137
140
|
readonly_attributes = model.readonly_attributes
|
|
138
|
-
|
|
141
|
+
read_only_fields = self.read_only_fields&.map(&:to_s)&.to_set || Set[]
|
|
142
|
+
write_only_fields = self.write_only_fields&.map(&:to_s)&.to_set || Set[]
|
|
143
|
+
hidden_fields = self.hidden_fields&.map(&:to_s)&.to_set || Set[]
|
|
139
144
|
rich_text_association_names = model.reflect_on_all_associations(:has_one)
|
|
140
145
|
.collect(&:name)
|
|
141
146
|
.select { |n| n.to_s.start_with?("rich_text_") }
|
|
142
147
|
attachment_reflections = model.attachment_reflections
|
|
143
148
|
|
|
144
|
-
|
|
149
|
+
@field_configuration = self.get_fields.map { |f|
|
|
145
150
|
cfg = field_config[f]&.dup || {}
|
|
146
151
|
cfg[:label] ||= self.label_for(f)
|
|
147
152
|
|
|
@@ -149,14 +154,29 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
149
154
|
if model.primary_key == f
|
|
150
155
|
cfg[:primary_key] = true
|
|
151
156
|
|
|
152
|
-
unless cfg.key?(:
|
|
153
|
-
cfg[:
|
|
157
|
+
unless cfg.key?(:read_only)
|
|
158
|
+
cfg[:read_only] = true
|
|
154
159
|
end
|
|
155
160
|
end
|
|
156
161
|
|
|
157
|
-
# Annotate
|
|
158
|
-
if f.in?(readonly_attributes) || f.in?(
|
|
159
|
-
|
|
162
|
+
# Annotate field mutability and display properties.
|
|
163
|
+
cfg[:read_only] = true if f.in?(readonly_attributes) || f.in?(read_only_fields)
|
|
164
|
+
cfg[:write_only] = true if f.in?(write_only_fields)
|
|
165
|
+
cfg[:hidden] = true if f.in?(hidden_fields)
|
|
166
|
+
|
|
167
|
+
# Raise warnings on some bad combinations of properties.
|
|
168
|
+
if cfg[:write_only]
|
|
169
|
+
if cfg[:read_only]
|
|
170
|
+
Rails.logger.warn("RRF: `#{f}` write_only conflicts with read_only.")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if cfg[:hidden]
|
|
174
|
+
Rails.logger.warn("RRF: `#{f}` write_only implies hidden.")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
if cfg[:hidden_from_index]
|
|
178
|
+
Rails.logger.warn("RRF: `#{f}` write_only implies hidden_from_index.")
|
|
179
|
+
end
|
|
160
180
|
end
|
|
161
181
|
|
|
162
182
|
# Annotate column data.
|
|
@@ -167,12 +187,12 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
167
187
|
end
|
|
168
188
|
|
|
169
189
|
# Add default values from the model's schema.
|
|
170
|
-
if column_default = column_defaults[f]
|
|
171
|
-
cfg[:default]
|
|
190
|
+
if cfg[:default].nil? && (column_default = column_defaults[f])
|
|
191
|
+
cfg[:default] = column_default
|
|
172
192
|
end
|
|
173
193
|
|
|
174
194
|
# Add metadata from the model's attributes hash.
|
|
175
|
-
if attribute = attributes[f]
|
|
195
|
+
if attributes.key?(f) && attribute = attributes[f]
|
|
176
196
|
if cfg[:default].nil? && default = attribute.value_before_type_cast
|
|
177
197
|
cfg[:default] = default
|
|
178
198
|
end
|
|
@@ -218,7 +238,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
218
238
|
v[:kind] = "method"
|
|
219
239
|
end
|
|
220
240
|
|
|
221
|
-
next [sf, v]
|
|
241
|
+
next [ sf, v ]
|
|
222
242
|
}.to_h.compact.presence
|
|
223
243
|
|
|
224
244
|
# Determine if we render id/ids fields. Unfortunately, `has_one` does not provide this
|
|
@@ -231,7 +251,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
231
251
|
if self.permit_nested_attributes_assignment && (
|
|
232
252
|
nested_opts = model.nested_attributes_options[f.to_sym].presence
|
|
233
253
|
)
|
|
234
|
-
cfg[:nested_attributes_options] = {field: "#{f}_attributes", **nested_opts}
|
|
254
|
+
cfg[:nested_attributes_options] = { field: "#{f}_attributes", **nested_opts }
|
|
235
255
|
end
|
|
236
256
|
|
|
237
257
|
begin
|
|
@@ -256,6 +276,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
256
276
|
# Determine if this is just a method.
|
|
257
277
|
if !cfg[:kind] && model.method_defined?(f)
|
|
258
278
|
cfg[:kind] = "method"
|
|
279
|
+
cfg[:read_only] = true if cfg[:read_only].nil?
|
|
259
280
|
end
|
|
260
281
|
|
|
261
282
|
# Collect validator options into a hash on their type, while also updating `required` based
|
|
@@ -283,8 +304,8 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
283
304
|
cfg[:validators][kind] << options
|
|
284
305
|
end
|
|
285
306
|
|
|
286
|
-
next [f, cfg]
|
|
287
|
-
}.to_h.with_indifferent_access
|
|
307
|
+
next [ f, cfg ]
|
|
308
|
+
}.to_h.compact.with_indifferent_access
|
|
288
309
|
end
|
|
289
310
|
|
|
290
311
|
def openapi_schema
|
|
@@ -295,7 +316,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
295
316
|
required: field_configuration.select { |_, cfg| cfg[:required] }.keys,
|
|
296
317
|
type: "object",
|
|
297
318
|
properties: field_configuration.map { |f, cfg|
|
|
298
|
-
v = {title: cfg[:label]}
|
|
319
|
+
v = { title: cfg[:label] }
|
|
299
320
|
|
|
300
321
|
if cfg[:kind] == "association"
|
|
301
322
|
v[:type] = cfg[:reflection].collection? ? "array" : "object"
|
|
@@ -309,7 +330,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
309
330
|
v[:type] = cfg[:type]
|
|
310
331
|
end
|
|
311
332
|
|
|
312
|
-
v[:readOnly] = true if cfg[:
|
|
333
|
+
v[:readOnly] = true if cfg[:read_only]
|
|
313
334
|
v[:default] = cfg[:default] if cfg.key?(:default)
|
|
314
335
|
|
|
315
336
|
if enum_variants = cfg[:enum_variants]
|
|
@@ -324,7 +345,14 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
324
345
|
v[:"x-rrf-kind"] = cfg[:kind] if cfg[:kind]
|
|
325
346
|
|
|
326
347
|
if cfg[:reflection]
|
|
327
|
-
v[:"x-rrf-reflection"] =
|
|
348
|
+
v[:"x-rrf-reflection"] = {
|
|
349
|
+
class_name: cfg[:reflection].class_name,
|
|
350
|
+
foreign_key: cfg[:reflection].foreign_key,
|
|
351
|
+
association_foreign_key: cfg[:reflection].association_foreign_key,
|
|
352
|
+
association_primary_key: cfg[:reflection].association_primary_key,
|
|
353
|
+
inverse_of: cfg[:reflection].inverse_of&.name,
|
|
354
|
+
join_table: cfg[:reflection].join_table,
|
|
355
|
+
}.compact
|
|
328
356
|
v[:"x-rrf-association_pk"] = cfg[:association_pk]
|
|
329
357
|
v[:"x-rrf-sub_fields"] = cfg[:sub_fields]
|
|
330
358
|
v[:"x-rrf-sub_fields_metadata"] = cfg[:sub_fields_metadata]
|
|
@@ -332,15 +360,15 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
332
360
|
v[:"x-rrf-nested_attributes_options"] = cfg[:nested_attributes_options]
|
|
333
361
|
end
|
|
334
362
|
|
|
335
|
-
next [f, v]
|
|
363
|
+
next [ f, v ]
|
|
336
364
|
}.to_h,
|
|
337
365
|
}
|
|
338
366
|
|
|
339
|
-
|
|
367
|
+
@openapi_schema
|
|
340
368
|
end
|
|
341
369
|
|
|
342
370
|
def openapi_schema_name
|
|
343
|
-
|
|
371
|
+
@openapi_schema_name ||= self.name.chomp("Controller").gsub("::", ".")
|
|
344
372
|
end
|
|
345
373
|
|
|
346
374
|
def openapi_paths(_routes, tag)
|
|
@@ -358,7 +386,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
358
386
|
if !extra_action && method != "options" # rubocop:disable Style/Next
|
|
359
387
|
# Add schema to request body content types.
|
|
360
388
|
action.dig(:requestBody, :content)&.each do |_t, v|
|
|
361
|
-
v[:schema] = {"$ref" => "#/components/schemas/#{schema_name}"}
|
|
389
|
+
v[:schema] = { "$ref" => "#/components/schemas/#{schema_name}" }
|
|
362
390
|
end
|
|
363
391
|
|
|
364
392
|
# Add schema to successful response body content types.
|
|
@@ -368,7 +396,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
368
396
|
response[:content]&.each do |t, v|
|
|
369
397
|
next if t == "text/html"
|
|
370
398
|
|
|
371
|
-
v[:schema] = {"$ref" => "#/components/schemas/#{schema_name}"}
|
|
399
|
+
v[:schema] = { "$ref" => "#/components/schemas/#{schema_name}" }
|
|
372
400
|
end
|
|
373
401
|
end
|
|
374
402
|
|
|
@@ -385,7 +413,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
385
413
|
end
|
|
386
414
|
end
|
|
387
415
|
|
|
388
|
-
|
|
416
|
+
paths
|
|
389
417
|
end
|
|
390
418
|
|
|
391
419
|
def openapi_document(request, route_group_name, _routes)
|
|
@@ -396,7 +424,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
396
424
|
document[:components][:schemas] ||= {}
|
|
397
425
|
document[:components][:schemas][self.openapi_schema_name] = self.openapi_schema
|
|
398
426
|
|
|
399
|
-
|
|
427
|
+
document.merge(
|
|
400
428
|
{
|
|
401
429
|
"x-rrf-primary_key" => self.get_model.primary_key,
|
|
402
430
|
"x-rrf-callbacks" => self._process_action_callbacks.as_json,
|
|
@@ -471,7 +499,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
471
499
|
end
|
|
472
500
|
|
|
473
501
|
def get_fields
|
|
474
|
-
|
|
502
|
+
self.class.get_fields(input_fields: self.class.fields)
|
|
475
503
|
end
|
|
476
504
|
|
|
477
505
|
# Get a hash of strong parameters for the current action.
|
|
@@ -490,7 +518,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
490
518
|
config = self.class.field_configuration[f]
|
|
491
519
|
|
|
492
520
|
# ActionText Integration:
|
|
493
|
-
if self.class.enable_action_text && reflections.key?("
|
|
521
|
+
if self.class.enable_action_text && reflections.key?("rich_text_#{f}")
|
|
494
522
|
next f
|
|
495
523
|
end
|
|
496
524
|
|
|
@@ -520,7 +548,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
520
548
|
# TODO: Consider adjusting this based on `nested_attributes_options`.
|
|
521
549
|
if self.class.permit_nested_attributes_assignment
|
|
522
550
|
hash_variations["#{f}_attributes"] = (
|
|
523
|
-
config[:sub_fields] + ["_destroy"]
|
|
551
|
+
config[:sub_fields] + [ "_destroy" ]
|
|
524
552
|
)
|
|
525
553
|
end
|
|
526
554
|
|
|
@@ -534,11 +562,11 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
534
562
|
@_get_allowed_parameters += variations
|
|
535
563
|
@_get_allowed_parameters << hash_variations
|
|
536
564
|
|
|
537
|
-
|
|
565
|
+
@_get_allowed_parameters
|
|
538
566
|
end
|
|
539
567
|
|
|
540
568
|
def get_serializer_class
|
|
541
|
-
|
|
569
|
+
super || RESTFramework::NativeSerializer
|
|
542
570
|
end
|
|
543
571
|
|
|
544
572
|
# Use strong parameters to filter the request body.
|
|
@@ -610,8 +638,8 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
610
638
|
body_params = if allowed_params == true
|
|
611
639
|
ActionController::Parameters.new(data).permit!
|
|
612
640
|
elsif bulk_mode
|
|
613
|
-
pk = bulk_mode == :update ? [pk] : []
|
|
614
|
-
ActionController::Parameters.new(data).permit({_json: allowed_params + pk})
|
|
641
|
+
pk = bulk_mode == :update ? [ pk ] : []
|
|
642
|
+
ActionController::Parameters.new(data).permit({ _json: allowed_params + pk })
|
|
615
643
|
else
|
|
616
644
|
ActionController::Parameters.new(data).permit(*allowed_params)
|
|
617
645
|
end
|
|
@@ -625,15 +653,13 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
625
653
|
body_params[k].unshift(*v)
|
|
626
654
|
end
|
|
627
655
|
|
|
628
|
-
# Filter
|
|
629
|
-
|
|
630
|
-
|
|
656
|
+
# Filter read-only fields.
|
|
657
|
+
body_params.delete_if do |f, _|
|
|
658
|
+
cfg = self.class.field_configuration[f]
|
|
659
|
+
cfg && cfg[:read_only]
|
|
631
660
|
end
|
|
632
661
|
|
|
633
|
-
|
|
634
|
-
(self.class.exclude_body_fields || []).each { |f| body_params.delete(f) }
|
|
635
|
-
|
|
636
|
-
return body_params
|
|
662
|
+
body_params
|
|
637
663
|
end
|
|
638
664
|
alias_method :get_create_params, :get_body_params
|
|
639
665
|
alias_method :get_update_params, :get_body_params
|
|
@@ -647,14 +673,14 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
647
673
|
return model.all
|
|
648
674
|
end
|
|
649
675
|
|
|
650
|
-
|
|
676
|
+
nil
|
|
651
677
|
end
|
|
652
678
|
|
|
653
679
|
# Filter the recordset and return records this request has access to.
|
|
654
680
|
def get_records
|
|
655
681
|
data = self.get_recordset
|
|
656
682
|
|
|
657
|
-
|
|
683
|
+
@records ||= self.class.filter_backends&.reduce(data) { |d, filter|
|
|
658
684
|
filter.new(controller: self).filter_data(d)
|
|
659
685
|
} || data
|
|
660
686
|
end
|
|
@@ -687,14 +713,14 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
687
713
|
|
|
688
714
|
# Return the record. Route key is always `:id` by Rails' convention.
|
|
689
715
|
if is_pk
|
|
690
|
-
|
|
716
|
+
@record = collection.find(request.path_parameters[:id])
|
|
691
717
|
else
|
|
692
|
-
|
|
718
|
+
@record = collection.find_by!(find_by_key => request.path_parameters[:id])
|
|
693
719
|
end
|
|
694
720
|
end
|
|
695
721
|
|
|
696
722
|
# Determine what collection to call `create` on.
|
|
697
|
-
def
|
|
723
|
+
def create_from
|
|
698
724
|
if self.class.create_from_recordset
|
|
699
725
|
# Create with any properties inherited from the recordset. We exclude any `select` clauses
|
|
700
726
|
# in case model callbacks need to call `count` on this collection, which typically raises a
|
|
@@ -713,8 +739,8 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
713
739
|
# the serializer directly. This would fail for active model serializers, but maybe we don't
|
|
714
740
|
# care?
|
|
715
741
|
s = RESTFramework::Utils.wrap_ams(self.get_serializer_class)
|
|
716
|
-
|
|
717
|
-
s.new(record, controller: self).serialize.merge!({errors: record.errors.presence}.compact)
|
|
742
|
+
records.map do |record|
|
|
743
|
+
s.new(record, controller: self).serialize.merge!({ errors: record.errors.presence }.compact)
|
|
718
744
|
end
|
|
719
745
|
end
|
|
720
746
|
end
|
|
@@ -743,7 +769,7 @@ module RESTFramework::Mixins::ListModelMixin
|
|
|
743
769
|
end
|
|
744
770
|
end
|
|
745
771
|
|
|
746
|
-
|
|
772
|
+
records
|
|
747
773
|
end
|
|
748
774
|
end
|
|
749
775
|
|
|
@@ -762,7 +788,7 @@ module RESTFramework::Mixins::CreateModelMixin
|
|
|
762
788
|
|
|
763
789
|
# Perform the `create!` call and return the created record.
|
|
764
790
|
def create!
|
|
765
|
-
|
|
791
|
+
self.create_from.create!(self.get_create_params)
|
|
766
792
|
end
|
|
767
793
|
end
|
|
768
794
|
|
|
@@ -776,7 +802,7 @@ module RESTFramework::Mixins::UpdateModelMixin
|
|
|
776
802
|
def update!
|
|
777
803
|
record = self.get_record
|
|
778
804
|
record.update!(self.get_update_params)
|
|
779
|
-
|
|
805
|
+
record
|
|
780
806
|
end
|
|
781
807
|
end
|
|
782
808
|
|
|
@@ -789,7 +815,7 @@ module RESTFramework::Mixins::DestroyModelMixin
|
|
|
789
815
|
|
|
790
816
|
# Perform the `destroy!` call and return the destroyed (and frozen) record.
|
|
791
817
|
def destroy!
|
|
792
|
-
|
|
818
|
+
self.get_record.destroy!
|
|
793
819
|
end
|
|
794
820
|
end
|
|
795
821
|
|
|
@@ -34,11 +34,11 @@ class RESTFramework::Paginators::PageNumberPaginator < RESTFramework::Paginators
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Ensure we return at least 1.
|
|
37
|
-
|
|
37
|
+
[ page_size, 1 ].max
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Get the page and return it so the caller can serialize it.
|
|
41
|
-
def get_page(page_number=nil)
|
|
41
|
+
def get_page(page_number = nil)
|
|
42
42
|
# If page number isn't provided, infer from the params or use 1 as a fallback value.
|
|
43
43
|
unless page_number
|
|
44
44
|
page_number = @controller&.params&.[](@controller.class.page_query_param&.to_sym)
|
|
@@ -55,7 +55,7 @@ class RESTFramework::Paginators::PageNumberPaginator < RESTFramework::Paginators
|
|
|
55
55
|
|
|
56
56
|
# Get the data page and return it so the caller can serialize the data in the proper format.
|
|
57
57
|
page_index = @page_number - 1
|
|
58
|
-
|
|
58
|
+
@data.limit(@page_size).offset(page_index * @page_size)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# Wrap the serialized page with appropriate metadata.
|
|
@@ -63,13 +63,13 @@ class RESTFramework::Paginators::PageNumberPaginator < RESTFramework::Paginators
|
|
|
63
63
|
page_query_param = @controller.class.page_query_param
|
|
64
64
|
base_params = @controller.params.to_unsafe_h
|
|
65
65
|
next_url = if @page_number < @total_pages
|
|
66
|
-
@controller.url_for({**base_params, page_query_param => @page_number + 1})
|
|
66
|
+
@controller.url_for({ **base_params, page_query_param => @page_number + 1 })
|
|
67
67
|
end
|
|
68
68
|
previous_url = if @page_number > 1
|
|
69
|
-
@controller.url_for({**base_params, page_query_param => @page_number - 1})
|
|
69
|
+
@controller.url_for({ **base_params, page_query_param => @page_number - 1 })
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
{
|
|
73
73
|
count: @count,
|
|
74
74
|
page: @page_number,
|
|
75
75
|
page_size: @page_size,
|
|
@@ -43,10 +43,10 @@ module ActionDispatch::Routing
|
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
controller
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
#
|
|
49
|
+
# Internal interface for routing extra actions.
|
|
50
50
|
def _route_extra_actions(actions, &block)
|
|
51
51
|
parsed_actions = RESTFramework::Utils.parse_extra_actions(actions)
|
|
52
52
|
|
|
@@ -84,13 +84,7 @@ module ActionDispatch::Routing
|
|
|
84
84
|
unscoped = kwargs.delete(:unscoped)
|
|
85
85
|
|
|
86
86
|
# Determine plural/singular resource.
|
|
87
|
-
|
|
88
|
-
force_plural = kwargs.delete(:force_plural)
|
|
89
|
-
if force_singular
|
|
90
|
-
singular = true
|
|
91
|
-
elsif force_plural
|
|
92
|
-
singular = false
|
|
93
|
-
elsif !controller_class.singleton_controller.nil?
|
|
87
|
+
if !controller_class.singleton_controller.nil?
|
|
94
88
|
singular = controller_class.singleton_controller
|
|
95
89
|
else
|
|
96
90
|
singular = default_singular
|
|
@@ -98,7 +92,7 @@ module ActionDispatch::Routing
|
|
|
98
92
|
resource_method = singular ? :resource : :resources
|
|
99
93
|
|
|
100
94
|
# Call either `resource` or `resources`, passing appropriate modifiers.
|
|
101
|
-
skip = RESTFramework::Utils.get_skipped_builtin_actions(controller_class)
|
|
95
|
+
skip = RESTFramework::Utils.get_skipped_builtin_actions(controller_class, singular)
|
|
102
96
|
public_send(resource_method, name, except: skip, **kwargs) do
|
|
103
97
|
if controller_class.respond_to?(:extra_member_actions)
|
|
104
98
|
member do
|
|
@@ -114,7 +108,7 @@ module ActionDispatch::Routing
|
|
|
114
108
|
RESTFramework::RRF_BUILTIN_ACTIONS.each do |action, methods|
|
|
115
109
|
next unless controller_class.method_defined?(action)
|
|
116
110
|
|
|
117
|
-
[methods].flatten.each do |m|
|
|
111
|
+
[ methods ].flatten.each do |m|
|
|
118
112
|
public_send(m, "", action: action) if self.respond_to?(m)
|
|
119
113
|
end
|
|
120
114
|
end
|
|
@@ -123,7 +117,7 @@ module ActionDispatch::Routing
|
|
|
123
117
|
RESTFramework::RRF_BUILTIN_BULK_ACTIONS.each do |action, methods|
|
|
124
118
|
next unless controller_class.method_defined?(action)
|
|
125
119
|
|
|
126
|
-
[methods].flatten.each do |m|
|
|
120
|
+
[ methods ].flatten.each do |m|
|
|
127
121
|
public_send(m, "", action: action) if self.respond_to?(m)
|
|
128
122
|
end
|
|
129
123
|
end
|
|
@@ -154,7 +148,7 @@ module ActionDispatch::Routing
|
|
|
154
148
|
end
|
|
155
149
|
|
|
156
150
|
# Route a controller without the default resourceful paths.
|
|
157
|
-
def rest_route(name=nil, **kwargs, &block)
|
|
151
|
+
def rest_route(name = nil, **kwargs, &block)
|
|
158
152
|
controller = kwargs.delete(:controller) || name
|
|
159
153
|
route_root_to = kwargs.delete(:route_root_to)
|
|
160
154
|
if controller.is_a?(Class)
|
|
@@ -184,7 +178,7 @@ module ActionDispatch::Routing
|
|
|
184
178
|
RESTFramework::RRF_BUILTIN_ACTIONS.each do |action, methods|
|
|
185
179
|
next unless controller_class.method_defined?(action)
|
|
186
180
|
|
|
187
|
-
[methods].flatten.each do |m|
|
|
181
|
+
[ methods ].flatten.each do |m|
|
|
188
182
|
public_send(m, "", action: action) if self.respond_to?(m)
|
|
189
183
|
end
|
|
190
184
|
end
|
|
@@ -201,7 +195,7 @@ module ActionDispatch::Routing
|
|
|
201
195
|
end
|
|
202
196
|
|
|
203
197
|
# Route a controller's `#root` to '/' in the current scope/namespace, along with other actions.
|
|
204
|
-
def rest_root(name=nil, **kwargs, &block)
|
|
198
|
+
def rest_root(name = nil, **kwargs, &block)
|
|
205
199
|
# By default, use RootController#root.
|
|
206
200
|
root_action = kwargs.delete(:action) || :root
|
|
207
201
|
controller = kwargs.delete(:controller) || name || :root
|
|
@@ -211,7 +205,7 @@ module ActionDispatch::Routing
|
|
|
211
205
|
kwargs[:path] = ""
|
|
212
206
|
end
|
|
213
207
|
|
|
214
|
-
|
|
208
|
+
rest_route(controller, route_root_to: root_action, **kwargs) do
|
|
215
209
|
yield if block_given?
|
|
216
210
|
end
|
|
217
211
|
end
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# This is a helper factory to wrap an ActiveModelSerializer to provide a `serialize` method which
|
|
2
2
|
# accepts both collections and individual records. Use `.for` to build adapters.
|
|
3
|
+
# :nocov:
|
|
3
4
|
class RESTFramework::Serializers::ActiveModelSerializerAdapterFactory
|
|
4
5
|
def self.for(active_model_serializer)
|
|
5
|
-
|
|
6
|
+
Class.new(active_model_serializer) do
|
|
6
7
|
def serialize
|
|
7
8
|
if self.object.respond_to?(:to_ary)
|
|
8
9
|
return self.object.map { |r| self.class.superclass.new(r).serializable_hash }
|
|
9
10
|
end
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
self.serializable_hash
|
|
12
13
|
end
|
|
13
14
|
end
|
|
14
15
|
end
|
|
15
16
|
end
|
|
17
|
+
# :nocov:
|
|
16
18
|
|
|
17
19
|
# Alias for convenience.
|
|
18
20
|
# rubocop:disable Layout/LineLength
|
|
@@ -4,7 +4,7 @@ class RESTFramework::Serializers::BaseSerializer
|
|
|
4
4
|
attr_accessor :object
|
|
5
5
|
|
|
6
6
|
# Accept/ignore `*args` to be compatible with the `ActiveModel::Serializer#initialize` signature.
|
|
7
|
-
def initialize(object=nil, *args, controller: nil, **kwargs)
|
|
7
|
+
def initialize(object = nil, *args, controller: nil, **kwargs)
|
|
8
8
|
@object = object
|
|
9
9
|
@controller = controller
|
|
10
10
|
end
|
|
@@ -16,24 +16,26 @@ class RESTFramework::Serializers::BaseSerializer
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Synonym for `serialize` for compatibility with `active_model_serializers`.
|
|
19
|
+
# :nocov:
|
|
19
20
|
def serializable_hash(*args)
|
|
20
|
-
|
|
21
|
+
self.serialize(*args)
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
# For compatibility with `active_model_serializers`.
|
|
24
25
|
def self.cache_enabled?
|
|
25
|
-
|
|
26
|
+
false
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
# For compatibility with `active_model_serializers`.
|
|
29
30
|
def self.fragment_cache_enabled?
|
|
30
|
-
|
|
31
|
+
false
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
# For compatibility with `active_model_serializers`.
|
|
34
35
|
def associations(*args, **kwargs)
|
|
35
|
-
|
|
36
|
+
[]
|
|
36
37
|
end
|
|
38
|
+
# :nocov:
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# Alias for convenience.
|