rest_framework 0.7.4 → 0.7.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/app/views/rest_framework/_head.html.erb +19 -10
- data/lib/rest_framework/controller_mixins/base.rb +14 -3
- data/lib/rest_framework/controller_mixins/bulk.rb +51 -0
- data/lib/rest_framework/controller_mixins/models.rb +28 -24
- data/lib/rest_framework/controller_mixins.rb +1 -0
- data/lib/rest_framework/routers.rb +9 -0
- data/lib/rest_framework/serializers.rb +5 -6
- data/lib/rest_framework.rb +4 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af670b7b95c56b3afafeb8947b732be7e5ff3adc9f26b7bdd8f3dc6705c48094
|
4
|
+
data.tar.gz: ba4de3b422fcae2451bbb5ea5e979acaaaedfba53773ddf3a77f16c97fd4db85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f98df76846047b3435c6caaf071ebd4ac60a9ddd9da517b621d21d9e84498a28d5387e2338128c4b7815d36f0750e6c06d84147409a9ce3982a329d626a9c60
|
7
|
+
data.tar.gz: 3aa62deb130160763d08b1487496973bdc587968c278cf26be0390d9e3464d4c408ea9f30d01d8fb38702a93e311df26a81b54f3b5adf9a49b344667af5d7508
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.5
|
@@ -3,8 +3,20 @@
|
|
3
3
|
<%= csrf_meta_tags %>
|
4
4
|
<%= csp_meta_tag rescue nil %>
|
5
5
|
|
6
|
+
<!-- Bootstrap -->
|
6
7
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
|
7
|
-
<
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
|
9
|
+
|
10
|
+
<!-- Highlight.js -->
|
11
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/vs.min.css" integrity="sha512-AVoZ71dJLtHRlsgWwujPT1hk2zxtFWsPlpTPCc/1g0WgpbmlzkqlDFduAvnOV4JJWKUquPc1ZyMc5eq4fRnKOQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
12
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js" integrity="sha512-bgHRAiTjGrzHzLyKOnpFvaEpGzJet3z4tZnXGjpsCcqOnAH6VGUx9frc5bcIhKTVLEiCO6vEhNAgx5jtLUYrfA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
13
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/json.min.js" integrity="sha512-0xYvyncS9OLE7GOpNBZFnwyh9+bq4HVgk4yVVYI678xRvE22ASicF1v6fZ1UiST+M6pn17MzFZdvVCI3jTHSyw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
14
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/xml.min.js" integrity="sha512-5zBcw+OKRkaNyvUEPlTSfYylVzgpi7KpncY36b0gRudfxIYIH0q0kl2j26uCUB3YBRM6ytQQEZSgRg+ZlBTmdA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
15
|
+
|
16
|
+
<!-- NeatJSON -->
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/neatjson@0.10.5/javascript/neatjson.min.js"></script>
|
18
|
+
|
19
|
+
<!-- Custom Style -->
|
8
20
|
<style>
|
9
21
|
/* Adjust headers to always take up their entire row, and tweak the sizing. */
|
10
22
|
h1,h2,h3,h4,h5,h6 { display: inline-block; font-weight: normal; margin-bottom: 0; }
|
@@ -17,7 +29,7 @@ h6 { font-size: 1rem; }
|
|
17
29
|
|
18
30
|
/* Make code and code blocks a little nicer looking. */
|
19
31
|
code {
|
20
|
-
padding:
|
32
|
+
padding: .5em !important;
|
21
33
|
background-color: #f3f3f3 !important;
|
22
34
|
border: 1px solid #aaa;
|
23
35
|
border-radius: 3px;
|
@@ -56,25 +68,22 @@ code {
|
|
56
68
|
}
|
57
69
|
</style>
|
58
70
|
|
59
|
-
|
60
|
-
<script src="https://cdn.jsdelivr.net/npm/neatjson@0.10.5/javascript/neatjson.min.js"></script>
|
61
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/highlight.min.js" integrity="sha512-TDKKr+IvoqZnPzc3l35hdjpHD0m+b2EC2SrLEgKDRWpxf2rFCxemkgvJ5kfU48ip+Y+m2XVKyOCD85ybtlZDmw==" crossorigin="anonymous"></script>
|
62
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/languages/json.min.js" integrity="sha512-FoN8JE+WWCdIGXAIT8KQXwpiavz0Mvjtfk7Rku3MDUNO0BDCiRMXAsSX+e+COFyZTcDb9HDgP+pM2RX12d4j+A==" crossorigin="anonymous"></script>
|
63
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/languages/xml.min.js" integrity="sha512-dICltIgnUP+QSJrnYGCV8943p3qSDgvcg2NU4W8IcOZP4tdrvxlXjbhIznhtVQEcXow0mOjLM0Q6/NvZsmUH4g==" crossorigin="anonymous"></script>
|
71
|
+
<!-- Custom JavaScript -->
|
64
72
|
<script>
|
65
|
-
hljs.initHighlightingOnLoad()
|
66
|
-
|
67
73
|
// What to do when document loads.
|
68
74
|
document.addEventListener("DOMContentLoaded", (event) => {
|
69
75
|
// Pretty-print JSON.
|
70
76
|
[...document.getElementsByClassName("language-json")].forEach((element, index) => {
|
71
|
-
element.innerHTML = neatJSON(JSON.parse(element.
|
77
|
+
element.innerHTML = neatJSON(JSON.parse(element.innerText), {
|
72
78
|
wrap: 80,
|
73
79
|
afterComma: 1,
|
74
80
|
afterColon: 1,
|
75
81
|
})
|
76
82
|
});
|
77
83
|
|
84
|
+
// Then highlight it.
|
85
|
+
hljs.highlightAll();
|
86
|
+
|
78
87
|
// Insert copy link and callback to copy contents of `<code>` element.
|
79
88
|
[...document.getElementsByClassName("rrf-copy")].forEach((element, index) => {
|
80
89
|
element.insertAdjacentHTML(
|
@@ -18,7 +18,7 @@ module RESTFramework::BaseControllerMixin
|
|
18
18
|
filter_backends: nil,
|
19
19
|
singleton_controller: nil,
|
20
20
|
|
21
|
-
#
|
21
|
+
# Options related to metadata and display.
|
22
22
|
title: nil,
|
23
23
|
description: nil,
|
24
24
|
inflect_acronyms: ["ID", "REST", "API"].freeze,
|
@@ -36,6 +36,10 @@ module RESTFramework::BaseControllerMixin
|
|
36
36
|
page_size_query_param: "page_size",
|
37
37
|
max_page_size: nil,
|
38
38
|
|
39
|
+
# Options related to bulk actions and batch processing.
|
40
|
+
bulk_guard_query_param: nil,
|
41
|
+
enable_batch_processing: nil,
|
42
|
+
|
39
43
|
# Option to disable serializer adapters by default, mainly introduced because Active Model
|
40
44
|
# Serializers will do things like serialize `[]` into `{"":[]}`.
|
41
45
|
disable_adapters_by_default: true,
|
@@ -74,6 +78,13 @@ module RESTFramework::BaseControllerMixin
|
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
81
|
+
# Add builtin bulk actions.
|
82
|
+
RESTFramework::RRF_BUILTIN_BULK_ACTIONS.each do |action, methods|
|
83
|
+
if self.method_defined?(action)
|
84
|
+
actions[action] = {path: "", methods: methods, type: :builtin}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
77
88
|
# Add extra actions.
|
78
89
|
if extra_actions = self.try(:extra_actions)
|
79
90
|
actions.merge!(RESTFramework::Utils.parse_extra_actions(extra_actions, controller: self))
|
@@ -268,8 +279,8 @@ module RESTFramework::BaseControllerMixin
|
|
268
279
|
)
|
269
280
|
end
|
270
281
|
|
271
|
-
#
|
272
|
-
#
|
282
|
+
# Render a browsable API for `html` format, along with basic `json`/`xml` formats, and with
|
283
|
+
# support or passing custom `kwargs` to the underlying `render` calls.
|
273
284
|
def api_response(payload, html_kwargs: nil, **kwargs)
|
274
285
|
html_kwargs ||= {}
|
275
286
|
json_kwargs = kwargs.delete(:json_kwargs) || {}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative "models"
|
2
|
+
|
3
|
+
# Mixin for creating records in bulk. This is unique compared to update/destroy because we overload
|
4
|
+
# the existing `create` action to support bulk creation.
|
5
|
+
module RESTFramework::BulkCreateModelMixin
|
6
|
+
def create
|
7
|
+
raise NotImplementedError, "TODO"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Perform the `create!` call and return the created record.
|
11
|
+
def create_all!
|
12
|
+
raise NotImplementedError, "TODO"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Mixin for updating records in bulk.
|
17
|
+
module RESTFramework::BulkUpdateModelMixin
|
18
|
+
def update_all
|
19
|
+
raise NotImplementedError, "TODO"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Perform the `update!` call and return the updated record.
|
23
|
+
def update_all!
|
24
|
+
raise NotImplementedError, "TODO"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Mixin for destroying records in bulk.
|
29
|
+
module RESTFramework::BulkDestroyModelMixin
|
30
|
+
def destroy_all
|
31
|
+
raise NotImplementedError, "TODO"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Perform the `destroy!` call and return the destroyed (and frozen) record.
|
35
|
+
def destroy_all!
|
36
|
+
raise NotImplementedError, "TODO"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Mixin that includes all the CRUD bulk mixins.
|
41
|
+
module RESTFramework::BulkModelControllerMixin
|
42
|
+
include RESTFramework::ModelControllerMixin
|
43
|
+
|
44
|
+
include RESTFramework::BulkCreateModelMixin
|
45
|
+
include RESTFramework::BulkUpdateModelMixin
|
46
|
+
include RESTFramework::BulkDestroyModelMixin
|
47
|
+
|
48
|
+
def self.included(base)
|
49
|
+
RESTFramework::ModelControllerMixin.included(base)
|
50
|
+
end
|
51
|
+
end
|
@@ -163,15 +163,20 @@ module RESTFramework::BaseModelControllerMixin
|
|
163
163
|
# Get metadata about the resource's associations (reflections).
|
164
164
|
def get_associations_metadata
|
165
165
|
return self.get_model.reflections.map { |k, v|
|
166
|
+
begin
|
167
|
+
pk = v.active_record_primary_key
|
168
|
+
rescue ActiveRecord::UnknownPrimaryKey
|
169
|
+
end
|
170
|
+
|
166
171
|
next [k, {
|
167
172
|
macro: v.macro,
|
168
173
|
label: self.get_label(k),
|
169
174
|
class_name: v.class_name,
|
170
175
|
foreign_key: v.foreign_key,
|
171
|
-
primary_key:
|
176
|
+
primary_key: pk,
|
172
177
|
polymorphic: v.polymorphic?,
|
173
178
|
table_name: v.table_name,
|
174
|
-
options: v.options,
|
179
|
+
options: v.options.presence,
|
175
180
|
}.compact]
|
176
181
|
}.to_h
|
177
182
|
end
|
@@ -238,9 +243,10 @@ module RESTFramework::BaseModelControllerMixin
|
|
238
243
|
end
|
239
244
|
|
240
245
|
def self.included(base)
|
246
|
+
RESTFramework::BaseControllerMixin.included(base)
|
247
|
+
|
241
248
|
return unless base.is_a?(Class)
|
242
249
|
|
243
|
-
RESTFramework::BaseControllerMixin.included(base)
|
244
250
|
base.extend(ClassMethods)
|
245
251
|
|
246
252
|
# Add class attributes (with defaults) unless they already exist.
|
@@ -311,12 +317,12 @@ module RESTFramework::BaseModelControllerMixin
|
|
311
317
|
) || self.get_fields
|
312
318
|
end
|
313
319
|
|
314
|
-
#
|
320
|
+
# Get the configured serializer class, or `NativeSerializer` as a default.
|
315
321
|
def get_serializer_class
|
316
322
|
return super || RESTFramework::NativeSerializer
|
317
323
|
end
|
318
324
|
|
319
|
-
#
|
325
|
+
# Get filtering backends, defaulting to using `ModelFilter` and `ModelOrderingFilter`.
|
320
326
|
def get_filter_backends
|
321
327
|
return self.class.filter_backends || [
|
322
328
|
RESTFramework::ModelFilter, RESTFramework::ModelOrderingFilter
|
@@ -324,14 +330,16 @@ module RESTFramework::BaseModelControllerMixin
|
|
324
330
|
end
|
325
331
|
|
326
332
|
# Filter the request body for keys in current action's allowed_parameters/fields config.
|
327
|
-
def get_body_params
|
333
|
+
def get_body_params(request_parameters: nil)
|
334
|
+
request_parameters ||= request.request_parameters
|
335
|
+
|
328
336
|
# Filter the request body and map to strings. Return all params if we cannot resolve a list of
|
329
337
|
# allowed parameters or fields.
|
330
338
|
allowed_params = self.get_allowed_parameters&.map(&:to_s)
|
331
339
|
body_params = if allowed_params
|
332
|
-
|
340
|
+
request_parameters.select { |p| allowed_params.include?(p) }
|
333
341
|
else
|
334
|
-
|
342
|
+
request_parameters
|
335
343
|
end
|
336
344
|
|
337
345
|
# Add query params in place of missing body params, if configured.
|
@@ -370,7 +378,7 @@ module RESTFramework::BaseModelControllerMixin
|
|
370
378
|
return @recordset = nil
|
371
379
|
end
|
372
380
|
|
373
|
-
#
|
381
|
+
# Get the records this controller has access to *after* any filtering is applied.
|
374
382
|
def get_records
|
375
383
|
return @records if instance_variable_defined?(:@records)
|
376
384
|
|
@@ -413,7 +421,7 @@ module RESTFramework::ListModelMixin
|
|
413
421
|
return api_response(self.get_index_records)
|
414
422
|
end
|
415
423
|
|
416
|
-
#
|
424
|
+
# Get records with both filtering and pagination applied.
|
417
425
|
def get_index_records
|
418
426
|
records = self.get_records
|
419
427
|
|
@@ -448,7 +456,7 @@ module RESTFramework::CreateModelMixin
|
|
448
456
|
return api_response(self.create!, status: :created)
|
449
457
|
end
|
450
458
|
|
451
|
-
#
|
459
|
+
# Perform the `create!` call and return the created record.
|
452
460
|
def create!
|
453
461
|
if self.get_recordset.respond_to?(:create!) && self.create_from_recordset
|
454
462
|
# Create with any properties inherited from the recordset. We exclude any `select` clauses in
|
@@ -468,7 +476,7 @@ module RESTFramework::UpdateModelMixin
|
|
468
476
|
return api_response(self.update!)
|
469
477
|
end
|
470
478
|
|
471
|
-
#
|
479
|
+
# Perform the `update!` call and return the updated record.
|
472
480
|
def update!
|
473
481
|
record = self.get_record
|
474
482
|
record.update!(self.get_update_params)
|
@@ -483,7 +491,7 @@ module RESTFramework::DestroyModelMixin
|
|
483
491
|
return api_response("")
|
484
492
|
end
|
485
493
|
|
486
|
-
#
|
494
|
+
# Perform the `destroy!` call and return the destroyed (and frozen) record.
|
487
495
|
def destroy!
|
488
496
|
return self.get_record.destroy!
|
489
497
|
end
|
@@ -493,29 +501,25 @@ end
|
|
493
501
|
module RESTFramework::ReadOnlyModelControllerMixin
|
494
502
|
include RESTFramework::BaseModelControllerMixin
|
495
503
|
|
496
|
-
|
497
|
-
|
504
|
+
include RESTFramework::ListModelMixin
|
505
|
+
include RESTFramework::ShowModelMixin
|
498
506
|
|
507
|
+
def self.included(base)
|
499
508
|
RESTFramework::BaseModelControllerMixin.included(base)
|
500
509
|
end
|
501
|
-
|
502
|
-
include RESTFramework::ListModelMixin
|
503
|
-
include RESTFramework::ShowModelMixin
|
504
510
|
end
|
505
511
|
|
506
512
|
# Mixin that includes all the CRUD mixins.
|
507
513
|
module RESTFramework::ModelControllerMixin
|
508
514
|
include RESTFramework::BaseModelControllerMixin
|
509
515
|
|
510
|
-
def self.included(base)
|
511
|
-
return unless base.is_a?(Class)
|
512
|
-
|
513
|
-
RESTFramework::BaseModelControllerMixin.included(base)
|
514
|
-
end
|
515
|
-
|
516
516
|
include RESTFramework::ListModelMixin
|
517
517
|
include RESTFramework::ShowModelMixin
|
518
518
|
include RESTFramework::CreateModelMixin
|
519
519
|
include RESTFramework::UpdateModelMixin
|
520
520
|
include RESTFramework::DestroyModelMixin
|
521
|
+
|
522
|
+
def self.included(base)
|
523
|
+
RESTFramework::BaseModelControllerMixin.included(base)
|
524
|
+
end
|
521
525
|
end
|
@@ -102,6 +102,15 @@ module ActionDispatch::Routing
|
|
102
102
|
public_send(m, "", action: action) if self.respond_to?(m)
|
103
103
|
end
|
104
104
|
end
|
105
|
+
|
106
|
+
# Route bulk actions, if configured.
|
107
|
+
RESTFramework::RRF_BUILTIN_BULK_ACTIONS.each do |action, methods|
|
108
|
+
next unless controller_class.method_defined?(action)
|
109
|
+
|
110
|
+
[methods].flatten.each do |m|
|
111
|
+
public_send(m, "", action: action) if self.respond_to?(m)
|
112
|
+
end
|
113
|
+
end
|
105
114
|
end
|
106
115
|
|
107
116
|
if unscoped
|
@@ -88,7 +88,7 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
88
88
|
return self.config || self.singular_config || self.plural_config
|
89
89
|
end
|
90
90
|
|
91
|
-
#
|
91
|
+
# Get a native serializer configuration from the controller.
|
92
92
|
def get_controller_native_serializer_config
|
93
93
|
return nil unless @controller
|
94
94
|
|
@@ -101,9 +101,8 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
101
101
|
return controller_serializer || @controller.class.try(:native_serializer_config)
|
102
102
|
end
|
103
103
|
|
104
|
-
#
|
105
|
-
#
|
106
|
-
# behavior:
|
104
|
+
# Filter a single subconfig for specific keys. By default, keys from `fields` are removed from the
|
105
|
+
# provided `subcfg`. There are two (mutually exclusive) options to adjust the behavior:
|
107
106
|
#
|
108
107
|
# `add`: Add any `fields` to the `subcfg` which aren't already in the `subcfg`.
|
109
108
|
# `only`: Remove any values found in the `subcfg` not in `fields`.
|
@@ -149,7 +148,7 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
149
148
|
return subcfg
|
150
149
|
end
|
151
150
|
|
152
|
-
#
|
151
|
+
# Filter out configuration properties based on the :except query parameter.
|
153
152
|
def filter_except(cfg)
|
154
153
|
return cfg unless @controller
|
155
154
|
|
@@ -245,7 +244,7 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
245
244
|
return filter_except(self._get_raw_serializer_config)
|
246
245
|
end
|
247
246
|
|
248
|
-
#
|
247
|
+
# Serialize a single record and merge results of `serializer_methods`.
|
249
248
|
def _serialize(record, config, serializer_methods)
|
250
249
|
# Ensure serializer_methods is either falsy, or an array.
|
251
250
|
if serializer_methods && !serializer_methods.respond_to?(:to_ary)
|
data/lib/rest_framework.rb
CHANGED
@@ -13,6 +13,10 @@ module RESTFramework
|
|
13
13
|
RRF_BUILTIN_ACTIONS = {
|
14
14
|
options: :options,
|
15
15
|
}.freeze
|
16
|
+
RRF_BUILTIN_BULK_ACTIONS = {
|
17
|
+
update_all: [:put, :patch].freeze,
|
18
|
+
destroy_all: :delete,
|
19
|
+
}.freeze
|
16
20
|
|
17
21
|
# Global configuration should be kept minimal, as controller-level configurations allows multiple
|
18
22
|
# APIs to be defined to behave differently.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregory N. Schmit
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-01-
|
11
|
+
date: 2023-01-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -42,6 +42,7 @@ files:
|
|
42
42
|
- lib/rest_framework.rb
|
43
43
|
- lib/rest_framework/controller_mixins.rb
|
44
44
|
- lib/rest_framework/controller_mixins/base.rb
|
45
|
+
- lib/rest_framework/controller_mixins/bulk.rb
|
45
46
|
- lib/rest_framework/controller_mixins/models.rb
|
46
47
|
- lib/rest_framework/engine.rb
|
47
48
|
- lib/rest_framework/errors.rb
|