rest_framework 0.8.15 → 0.8.17
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/VERSION +1 -1
- data/app/views/layouts/rest_framework.html.erb +149 -128
- data/app/views/rest_framework/_head.html.erb +249 -28
- data/app/views/rest_framework/_html_form.html.erb +55 -3
- data/app/views/rest_framework/_raw_form.html.erb +31 -3
- data/docs/CNAME +1 -0
- data/docs/Gemfile +4 -0
- data/docs/Gemfile.lock +264 -0
- data/docs/_config.yml +17 -0
- data/docs/_guide/1_routers.md +110 -0
- data/docs/_guide/2_controller_mixins.md +293 -0
- data/docs/_guide/3_serializers.md +60 -0
- data/docs/_guide/4_filtering_and_ordering.md +41 -0
- data/docs/_guide/5_pagination.md +21 -0
- data/docs/_includes/anchor_headings.html +144 -0
- data/docs/_includes/head.html +35 -0
- data/docs/_includes/header.html +58 -0
- data/docs/_layouts/default.html +11 -0
- data/docs/assets/css/rest_framework.css +159 -0
- data/docs/assets/images/favicon.ico +0 -0
- data/docs/assets/js/rest_framework.js +132 -0
- data/docs/index.md +133 -0
- data/lib/rest_framework/controller_mixins/base.rb +10 -3
- data/lib/rest_framework/controller_mixins/models.rb +120 -44
- data/lib/rest_framework/filters.rb +7 -9
- data/lib/rest_framework/serializers.rb +35 -12
- data/lib/rest_framework/utils.rb +11 -2
- data/lib/rest_framework/version.rb +4 -1
- data/lib/rest_framework.rb +2 -8
- metadata +22 -6
- data/app/views/rest_framework/_form_routes.html.erb +0 -10
@@ -4,11 +4,13 @@
|
|
4
4
|
<%= csp_meta_tag rescue nil %>
|
5
5
|
|
6
6
|
<!-- Bootstrap -->
|
7
|
-
<link
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.
|
7
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp" crossorigin="anonymous">
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/js/bootstrap.bundle.min.js" integrity="sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N" crossorigin="anonymous"></script>
|
9
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css">
|
9
10
|
|
10
11
|
<!-- Highlight.js -->
|
11
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/
|
12
|
+
<link rel="stylesheet" class="rrf-light-mode" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/a11y-light.min.css" integrity="sha512-WDk6RzwygsN9KecRHAfm9HTN87LQjqdygDmkHSJxVkVI7ErCZ8ZWxP6T8RvBujY1n2/E4Ac+bn2ChXnp5rnnHA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
13
|
+
<link rel="stylesheet" class="rrf-dark-mode" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/a11y-dark.min.css" integrity="sha512-Vj6gPCk8EZlqnoveEyuGyYaWZ1+jyjMPg8g4shwyyNlRQl6d3L9At02ZHQr5K6s5duZl/+YKMnM3/8pDhoUphg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
12
14
|
<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
15
|
<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
16
|
<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>
|
@@ -16,31 +18,105 @@
|
|
16
18
|
<!-- NeatJSON -->
|
17
19
|
<script src="https://cdn.jsdelivr.net/npm/neatjson@0.10.5/javascript/neatjson.min.js"></script>
|
18
20
|
|
21
|
+
<!-- Trix -->
|
22
|
+
<link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.0/dist/trix.css">
|
23
|
+
<script type="text/javascript" src="https://unpkg.com/trix@2.0.0/dist/trix.umd.min.js"></script>
|
24
|
+
|
19
25
|
<!-- Custom Style -->
|
20
26
|
<style>
|
21
|
-
|
22
|
-
|
27
|
+
/********************************
|
28
|
+
* START OF LIB/DOCS COMMON CSS *
|
29
|
+
********************************/
|
30
|
+
|
31
|
+
:root {
|
32
|
+
--rrf-red: #900;
|
33
|
+
--rrf-red-hover: #5f0c0c;
|
34
|
+
--rrf-light-red: #db2525;
|
35
|
+
--rrf-light-red-hover: #b80404;
|
36
|
+
}
|
37
|
+
#rrfAccentBar {
|
38
|
+
background-color: var(--rrf-red);
|
39
|
+
height: .3em;
|
40
|
+
}
|
41
|
+
header nav { background-color: black; }
|
42
|
+
|
43
|
+
/* Header adjustments. */
|
23
44
|
h1 { font-size: 2rem; }
|
24
45
|
h2 { font-size: 1.7rem; }
|
25
46
|
h3 { font-size: 1.5rem; }
|
26
47
|
h4 { font-size: 1.3rem; }
|
27
48
|
h5 { font-size: 1.1rem; }
|
28
49
|
h6 { font-size: 1rem; }
|
50
|
+
h1, h2, h3, h4, h5, h6 {
|
51
|
+
color: var(--rrf-red);
|
52
|
+
}
|
53
|
+
html[data-bs-theme="dark"] h1,
|
54
|
+
html[data-bs-theme="dark"] h2,
|
55
|
+
html[data-bs-theme="dark"] h3,
|
56
|
+
html[data-bs-theme="dark"] h4,
|
57
|
+
html[data-bs-theme="dark"] h5,
|
58
|
+
html[data-bs-theme="dark"] h6 {
|
59
|
+
color: var(--rrf-light-red);
|
60
|
+
}
|
29
61
|
|
30
|
-
/*
|
31
|
-
code {
|
62
|
+
/* Improve code and code blocks. */
|
63
|
+
pre code {
|
64
|
+
display: block;
|
65
|
+
overflow-x: auto;
|
66
|
+
}
|
67
|
+
code, .trix-content pre {
|
32
68
|
padding: .5em !important;
|
33
|
-
background-color: #
|
69
|
+
background-color: #eee !important;
|
34
70
|
border: 1px solid #aaa;
|
35
71
|
border-radius: 3px;
|
36
72
|
}
|
73
|
+
p code {
|
74
|
+
padding: .1em .3em !important;
|
75
|
+
}
|
76
|
+
html[data-bs-theme="dark"] code, html[data-bs-theme="dark"] .trix-content pre {
|
77
|
+
background-color: #2b2b2b !important;
|
78
|
+
}
|
37
79
|
|
38
|
-
/*
|
39
|
-
.
|
40
|
-
|
80
|
+
/* Anchors */
|
81
|
+
a:not(.nav-link) {
|
82
|
+
text-decoration: none;
|
83
|
+
color: var(--rrf-red);
|
84
|
+
}
|
85
|
+
a:hover:not(.nav-link) {
|
86
|
+
text-decoration: underline;
|
87
|
+
color: var(--rrf-red-hover);
|
88
|
+
}
|
89
|
+
html[data-bs-theme="dark"] a:not(.nav-link) { color: var(--rrf-light-red); }
|
90
|
+
html[data-bs-theme="dark"] a:hover:not(.nav-link) { color: var(--rrf-light-red-hover); }
|
91
|
+
|
92
|
+
/******************************
|
93
|
+
* END OF LIB/DOCS COMMON CSS *
|
94
|
+
******************************/
|
95
|
+
|
96
|
+
/* Header adjustments. */
|
97
|
+
h1,h2,h3,h4,h5,h6 { display: inline-block; font-weight: normal; margin-bottom: 0; }
|
98
|
+
|
99
|
+
/* Reduce label font size. */
|
100
|
+
label.form-label {
|
101
|
+
font-size: .8em;
|
102
|
+
}
|
103
|
+
|
104
|
+
/* Make Trix buttons visible even in dark mode. */
|
105
|
+
trix-toolbar .trix-button-group {
|
106
|
+
background-color: #eee;
|
107
|
+
}
|
108
|
+
|
109
|
+
/* Make Trix dialog URL input visible in dark mode. */
|
110
|
+
input.trix-input--dialog {
|
111
|
+
color: black;
|
41
112
|
}
|
113
|
+
|
114
|
+
/* Make route group expansion obvious to the user. */
|
42
115
|
.rrf-routes .rrf-route-group-header:hover {
|
43
|
-
background-color: #
|
116
|
+
background-color: #ddd;
|
117
|
+
}
|
118
|
+
html[data-bs-theme="dark"] .rrf-routes .rrf-route-group-header:hover {
|
119
|
+
background-color: #333;
|
44
120
|
}
|
45
121
|
.rrf-routes .rrf-route-group-header td {
|
46
122
|
cursor: pointer;
|
@@ -70,32 +146,134 @@ code {
|
|
70
146
|
|
71
147
|
<!-- Custom JavaScript -->
|
72
148
|
<script>
|
73
|
-
|
149
|
+
/*******************************
|
150
|
+
* START OF LIB/DOCS COMMON JS *
|
151
|
+
*******************************/
|
152
|
+
|
153
|
+
;(() => {
|
154
|
+
// Get the real mode from a selected mode. Anything other than "light" or "dark" is treated as
|
155
|
+
// "system" mode.
|
156
|
+
const rrfGetRealMode = (selectedMode) => {
|
157
|
+
if (selectedMode === "light" || selectedMode === "dark") {
|
158
|
+
return selectedMode
|
159
|
+
}
|
160
|
+
|
161
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
162
|
+
return "dark"
|
163
|
+
}
|
164
|
+
|
165
|
+
return "light"
|
166
|
+
}
|
167
|
+
|
168
|
+
// Set the mode, given a "selected" mode.
|
169
|
+
const rrfSetSelectedMode = (selectedMode) => {
|
170
|
+
const modeComponent = document.getElementById("rrfModeComponent")
|
171
|
+
|
172
|
+
// Anything except "light" or "dark" is casted to "system".
|
173
|
+
if (selectedMode !== "light" && selectedMode !== "dark") {
|
174
|
+
selectedMode = "system"
|
175
|
+
}
|
176
|
+
|
177
|
+
// Store selected mode in `localStorage`.
|
178
|
+
localStorage.setItem("rrfMode", selectedMode)
|
179
|
+
|
180
|
+
// Set the mode selector to the selected mode.
|
181
|
+
let labelHTML
|
182
|
+
modeComponent.querySelectorAll("button[data-rrf-mode-value]").forEach((el) => {
|
183
|
+
if (el.getAttribute("data-rrf-mode-value") === selectedMode) {
|
184
|
+
el.classList.add("active")
|
185
|
+
labelHTML = el.querySelector("i").outerHTML.replace("ms-2", "me-1")
|
186
|
+
} else {
|
187
|
+
el.classList.remove("active")
|
188
|
+
}
|
189
|
+
})
|
190
|
+
modeComponent.querySelector("button[data-bs-toggle]").innerHTML = labelHTML
|
191
|
+
|
192
|
+
// Get the real mode to use.
|
193
|
+
realMode = rrfGetRealMode(selectedMode)
|
194
|
+
|
195
|
+
// Set the `realMode` effects.
|
196
|
+
if (realMode === "light") {
|
197
|
+
document.querySelectorAll(".rrf-light-mode").forEach((el) => {
|
198
|
+
el.disabled = false
|
199
|
+
})
|
200
|
+
document.querySelectorAll(".rrf-dark-mode").forEach((el) => {
|
201
|
+
el.disabled = true
|
202
|
+
})
|
203
|
+
document.querySelectorAll(".rrf-mode").forEach((el) => {
|
204
|
+
el.setAttribute("data-bs-theme", "light")
|
205
|
+
})
|
206
|
+
} else if (realMode === "dark") {
|
207
|
+
document.querySelectorAll(".rrf-light-mode").forEach((el) => {
|
208
|
+
el.disabled = true
|
209
|
+
})
|
210
|
+
document.querySelectorAll(".rrf-dark-mode").forEach((el) => {
|
211
|
+
el.disabled = false
|
212
|
+
})
|
213
|
+
document.querySelectorAll(".rrf-mode").forEach((el) => {
|
214
|
+
el.setAttribute("data-bs-theme", "dark")
|
215
|
+
})
|
216
|
+
} else {
|
217
|
+
console.log(`RRF: Unknown mode: ${mode}`)
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
// Initialize dark/light mode.
|
222
|
+
document.addEventListener("DOMContentLoaded", (event) => {
|
223
|
+
const selectedMode = localStorage.getItem("rrfMode")
|
224
|
+
rrfSetSelectedMode(selectedMode)
|
225
|
+
document.querySelectorAll("#rrfModeComponent button[data-rrf-mode-value]").forEach((el) => {
|
226
|
+
el.addEventListener("click", (event) => {
|
227
|
+
rrfSetSelectedMode(event.target.getAttribute("data-rrf-mode-value"))
|
228
|
+
})
|
229
|
+
})
|
230
|
+
})
|
231
|
+
|
232
|
+
// Handle case where user changes system theme.
|
233
|
+
if (window.matchMedia) {
|
234
|
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
235
|
+
const selectedMode = localStorage.getItem("rrfMode")
|
236
|
+
if (selectedMode !== "light" && selectedMode !== "dark") {
|
237
|
+
rrfSetSelectedMode("system")
|
238
|
+
}
|
239
|
+
})
|
240
|
+
}
|
241
|
+
})()
|
242
|
+
|
243
|
+
/*****************************
|
244
|
+
* END OF LIB/DOCS COMMON JS *
|
245
|
+
*****************************/
|
246
|
+
|
74
247
|
document.addEventListener("DOMContentLoaded", (event) => {
|
75
248
|
// Pretty-print JSON.
|
76
|
-
|
249
|
+
document.querySelectorAll(".language-json").forEach((el, index) => {
|
77
250
|
el.innerHTML = neatJSON(JSON.parse(el.innerText), {
|
78
251
|
wrap: 80,
|
79
252
|
afterComma: 1,
|
80
253
|
afterColon: 1,
|
81
|
-
})
|
82
|
-
|
254
|
+
}).replaceAll("&", "&")
|
255
|
+
.replaceAll("<", "<")
|
256
|
+
.replaceAll(">", ">")
|
257
|
+
.replaceAll('"', """)
|
258
|
+
.replaceAll("'", "'")
|
259
|
+
})
|
83
260
|
|
84
261
|
// Then highlight it.
|
85
|
-
hljs.
|
262
|
+
hljs.configure({cssSelector: "pre code.auto-hljs"})
|
263
|
+
hljs.highlightAll()
|
86
264
|
|
87
|
-
// Replace
|
88
|
-
|
265
|
+
// Replace text node links with anchor tag links.
|
266
|
+
document.querySelectorAll(".rrf-copy code").forEach((el, index) => {
|
89
267
|
el.innerHTML = rrfLinkify(el.innerHTML)
|
90
|
-
})
|
268
|
+
})
|
91
269
|
|
92
270
|
// Insert copy link and callback to copy contents of `<code>` element.
|
93
|
-
|
271
|
+
document.querySelectorAll("rrf-copy").forEach((el, index) => {
|
94
272
|
el.insertAdjacentHTML(
|
95
273
|
"afterbegin",
|
96
274
|
"<a class=\"rrf-copy-link\" onclick=\"return rrfCopyToClipboard(this)\" href=\"#\">Copy to Clipboard</a>",
|
97
275
|
)
|
98
|
-
})
|
276
|
+
})
|
99
277
|
})
|
100
278
|
|
101
279
|
// Convert plain-text links to anchor tag links.
|
@@ -134,10 +312,10 @@ function rrfCopyToClipboard(element) {
|
|
134
312
|
return false
|
135
313
|
}
|
136
314
|
|
137
|
-
// Refresh the window.
|
138
|
-
function
|
315
|
+
// Refresh the window as a `GET` request.
|
316
|
+
function rrfGet(button) {
|
139
317
|
button.disabled = true
|
140
|
-
window.location.
|
318
|
+
window.location.replace(window.location.href)
|
141
319
|
}
|
142
320
|
|
143
321
|
// Call `DELETE` on the current path.
|
@@ -158,11 +336,41 @@ function rrfSubmitRawForm(button) {
|
|
158
336
|
|
159
337
|
// Grab the selected route/method, media type, and the body.
|
160
338
|
const [method, path] = document.getElementById("rawFormRoute").value.split(":")
|
161
|
-
const
|
162
|
-
|
339
|
+
const mediaType = document.getElementById("rawFormMediaType").value
|
340
|
+
let body = document.getElementById("rawFormContent").value
|
341
|
+
|
342
|
+
// If the media type is `multipart/form-data`, then we need to build a FormData object.
|
343
|
+
if (mediaType == "multipart/form-data") {
|
344
|
+
let formData = new FormData()
|
345
|
+
|
346
|
+
// Add body to `formData`.
|
347
|
+
const bodySearchParams = new URLSearchParams(body)
|
348
|
+
bodySearchParams.forEach((value, key) => {
|
349
|
+
formData.append(key, value)
|
350
|
+
})
|
351
|
+
|
352
|
+
// Add file(s) to `formData`.
|
353
|
+
const rawFilesForm = document.getElementById("rawFilesForm")
|
354
|
+
if (rawFilesForm) {
|
355
|
+
rawFilesForm.querySelectorAll("input[type=file]").forEach((el, index) => {
|
356
|
+
const files = el.files
|
357
|
+
for (let i = 0; i < files.length; i++) {
|
358
|
+
formData.append(el.name, files[i])
|
359
|
+
}
|
360
|
+
})
|
361
|
+
}
|
362
|
+
|
363
|
+
// Set body to be the form data.
|
364
|
+
body = formData
|
365
|
+
}
|
163
366
|
|
164
367
|
// Perform the API call.
|
165
|
-
rrfAPICall(path, method, {
|
368
|
+
rrfAPICall(path, method, {
|
369
|
+
body,
|
370
|
+
// If the media type is `multipart/form-data`, then we don't want to set the content type
|
371
|
+
// because it must be set by `fetch` to include boundary.
|
372
|
+
headers: mediaType == "multipart/form-data" ? {} : {"Content-Type": mediaType},
|
373
|
+
})
|
166
374
|
}
|
167
375
|
|
168
376
|
// Make an HTML API call and replace the document with the response.
|
@@ -174,4 +382,17 @@ function rrfAPICall(path, method, kwargs={}) {
|
|
174
382
|
.then((response) => response.text())
|
175
383
|
.then((body) => { rrfReplaceDocument(body) })
|
176
384
|
}
|
385
|
+
|
386
|
+
// Check if `rawFilesFormWrapper` should be displayed when media type is changed.
|
387
|
+
function rrfCheckRawFilesFormDisplay(el) {
|
388
|
+
const rawFilesFormWrapper = document.getElementById("rawFilesFormWrapper")
|
389
|
+
|
390
|
+
if (rawFilesFormWrapper) {
|
391
|
+
if (el.value === "multipart/form-data") {
|
392
|
+
rawFilesFormWrapper.style.display = "block"
|
393
|
+
} else {
|
394
|
+
rawFilesFormWrapper.style.display = "none"
|
395
|
+
}
|
396
|
+
}
|
397
|
+
}
|
177
398
|
</script>
|
@@ -1,7 +1,59 @@
|
|
1
1
|
<div style="max-width: 60em; margin: auto">
|
2
|
-
|
2
|
+
<div class="mb-2">
|
3
|
+
<label class="form-label w-100">Route
|
4
|
+
<select class="form-control form-control-sm" id="htmlFormRoute">
|
5
|
+
<% @_rrf_form_routes.each do |route| %>
|
6
|
+
<% path = @route_props[:with_path_args].call(route[:route]) %>
|
7
|
+
<option value="<%= route[:verb] %>:<%= path %>"><%= route[:verb] %> <%= route[:relative_path] %></option>
|
8
|
+
<% end %>
|
9
|
+
</select>
|
10
|
+
</label>
|
11
|
+
</div>
|
3
12
|
|
4
|
-
<%=
|
5
|
-
|
13
|
+
<%= form_with(
|
14
|
+
model: @record,
|
15
|
+
url: "",
|
16
|
+
method: :patch,
|
17
|
+
id: "htmlForm",
|
18
|
+
scope: "",
|
19
|
+
) do |form| %>
|
20
|
+
<% controller.get_fields.map(&:to_s).each do |f| %>
|
21
|
+
<%
|
22
|
+
# Don't provide form fields for associations or primary keys.
|
23
|
+
metadata = controller.class.fields_metadata[f]
|
24
|
+
next if !metadata || metadata[:kind] == "association" || metadata[:read_only]
|
25
|
+
%>
|
26
|
+
<div class="mb-2">
|
27
|
+
<% if metadata[:kind] == "rich_text" %>
|
28
|
+
<label class="form-label w-100"><%= controller.class.get_label(f) %></label>
|
29
|
+
<%= form.rich_text_area f %>
|
30
|
+
<% elsif metadata[:kind] == "attachment" %>
|
31
|
+
<label class="form-label w-100"><%= controller.class.get_label(f) %>
|
32
|
+
<%= form.file_field f, multiple: metadata.dig(:attachment, :macro) == :has_many_attached %>
|
33
|
+
</label>
|
34
|
+
<% else %>
|
35
|
+
<label class="form-label w-100"><%= controller.class.get_label(f) %>
|
36
|
+
<%= form.text_field f, class: "form-control form-control-sm" %>
|
37
|
+
</label>
|
38
|
+
<% end %>
|
39
|
+
</div>
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
<%= form.submit "Submit", name: "", class: "btn btn-primary", style: "float: right" %>
|
6
43
|
<% end %>
|
44
|
+
|
45
|
+
<script>
|
46
|
+
// Update form anytime the route changes.
|
47
|
+
document.getElementById("htmlFormRoute").addEventListener("change", (event) => {
|
48
|
+
const [verb, path] = event.target.value.split(":")
|
49
|
+
const form = document.getElementById("htmlForm")
|
50
|
+
form.action = path
|
51
|
+
form.querySelector("input[name='_method']").value = verb
|
52
|
+
})
|
53
|
+
|
54
|
+
document.addEventListener("DOMContentLoaded", (event) => {
|
55
|
+
// Trigger the change event to update the form initially.
|
56
|
+
document.getElementById("htmlFormRoute").dispatchEvent(new Event("change"))
|
57
|
+
})
|
58
|
+
</script>
|
7
59
|
</div>
|
@@ -1,9 +1,18 @@
|
|
1
1
|
<div style="max-width: 60em; margin: auto">
|
2
|
-
|
2
|
+
<div class="mb-2">
|
3
|
+
<label class="form-label w-100">Route
|
4
|
+
<select class="form-control form-control-sm" id="rawFormRoute">
|
5
|
+
<% @_rrf_form_routes.each do |route| %>
|
6
|
+
<% path = @route_props[:with_path_args].call(route[:route]) %>
|
7
|
+
<option value="<%= route[:verb] %>:<%= path %>"><%= route[:verb] %> <%= route[:relative_path] %></option>
|
8
|
+
<% end %>
|
9
|
+
</select>
|
10
|
+
</label>
|
11
|
+
</div>
|
3
12
|
|
4
13
|
<div class="mb-2">
|
5
14
|
<label class="form-label w-100">Media Type
|
6
|
-
<select class="form-control" id="rawFormMediaType">
|
15
|
+
<select class="form-control form-control-sm" id="rawFormMediaType" onchange="rrfCheckRawFilesFormDisplay(this)">
|
7
16
|
<% ["application/json", "application/x-www-form-urlencoded", "multipart/form-data"].each do |t| %>
|
8
17
|
<option value="<%= t %>"><%= t %></option>
|
9
18
|
<% end %>
|
@@ -13,9 +22,28 @@
|
|
13
22
|
|
14
23
|
<div class="mb-2">
|
15
24
|
<label class="form-label w-100">Content
|
16
|
-
<textarea class="form-control" style="font-family: monospace" id="rawFormContent" rows="8" cols="60"></textarea>
|
25
|
+
<textarea class="form-control form-control-sm" style="font-family: monospace" id="rawFormContent" rows="8" cols="60"></textarea>
|
17
26
|
</label>
|
18
27
|
</div>
|
19
28
|
|
29
|
+
<% if @is_model_controller && model = controller.class.get_model %>
|
30
|
+
<% if attachment_reflections = model.attachment_reflections.presence %>
|
31
|
+
<div class="mb-2" style="display: none" id="rawFilesFormWrapper">
|
32
|
+
<%= form_with(
|
33
|
+
model: @record,
|
34
|
+
url: "",
|
35
|
+
id: "rawFilesForm",
|
36
|
+
scope: "",
|
37
|
+
) do |form| %>
|
38
|
+
<% attachment_reflections.each do |field, ref| %>
|
39
|
+
<label class="form-label w-100"><%= controller.class.get_label(field) %>
|
40
|
+
<%= form.file_field field, multiple: ref.macro == :has_many_attached %>
|
41
|
+
</label>
|
42
|
+
<% end %>
|
43
|
+
<% end %>
|
44
|
+
</div>
|
45
|
+
<% end %>
|
46
|
+
<% end %>
|
47
|
+
|
20
48
|
<button type="button" class="btn btn-primary" style="float: right" onclick="rrfSubmitRawForm(this)">Submit</button>
|
21
49
|
</div>
|
data/docs/CNAME
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rails-rest-framework.com
|
data/docs/Gemfile
ADDED