rest_framework 0.9.4 → 0.9.6
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/LICENSE +1 -1
- data/README.md +130 -0
- data/VERSION +1 -1
- data/app/views/layouts/rest_framework.html.erb +9 -183
- data/app/views/rest_framework/_breadcrumbs.html.erb +27 -0
- data/app/views/rest_framework/_head.html.erb +348 -190
- data/app/views/rest_framework/_header.html.erb +15 -0
- data/app/views/rest_framework/_heading.html.erb +10 -0
- data/app/views/rest_framework/_payloads.html.erb +36 -0
- data/app/views/rest_framework/_request_metadata.html.erb +16 -0
- data/app/views/rest_framework/_routes_and_forms.html.erb +52 -0
- data/app/views/rest_framework/head/_external.html.erb +7 -2
- data/app/views/rest_framework/header/_mode.html.erb +14 -0
- data/app/views/rest_framework/heading/_actions.html.erb +9 -0
- data/app/views/rest_framework/{_routes.html.erb → routes_and_forms/_routes.html.erb} +2 -2
- data/lib/rest_framework/controller_mixins/base.rb +11 -12
- data/lib/rest_framework/engine.rb +5 -3
- data/lib/rest_framework/filters/ransack.rb +6 -6
- data/lib/rest_framework/version.rb +0 -6
- data/lib/rest_framework.rb +25 -13
- data/vendor/assets/javascripts/rest_framework/external.min.js +1256 -0
- data/vendor/assets/stylesheets/rest_framework/{bootstrap-icons.css → external.min.css} +415 -1
- data/vendor/assets/stylesheets/rest_framework/{highlight-a11y-dark.css → highlight-a11y-dark.min.css} +1 -1
- data/vendor/assets/stylesheets/rest_framework/{highlight-a11y-light.css → highlight-a11y-light.min.css} +1 -1
- metadata +18 -35
- data/README.md +0 -1
- data/app/views/rest_framework/head/_shared.html +0 -164
- data/docs/CNAME +0 -1
- data/docs/Gemfile +0 -5
- data/docs/Gemfile.lock +0 -264
- data/docs/_config.yml +0 -19
- data/docs/_guide/1_routers.md +0 -110
- data/docs/_guide/2_controllers.md +0 -342
- data/docs/_guide/3_serializers.md +0 -60
- data/docs/_guide/4_filtering_and_ordering.md +0 -41
- data/docs/_guide/5_pagination.md +0 -21
- data/docs/_includes/anchor_headings.html +0 -144
- data/docs/_includes/external.html +0 -9
- data/docs/_includes/head.html +0 -155
- data/docs/_includes/header.html +0 -58
- data/docs/_includes/shared.html +0 -164
- data/docs/_layouts/default.html +0 -11
- data/docs/assets/images/favicon.ico +0 -0
- data/docs/index.md +0 -133
- data/vendor/assets/javascripts/rest_framework/bootstrap.js +0 -7
- data/vendor/assets/javascripts/rest_framework/highlight-json.js +0 -7
- data/vendor/assets/javascripts/rest_framework/highlight-xml.js +0 -29
- data/vendor/assets/javascripts/rest_framework/highlight.js +0 -1202
- data/vendor/assets/javascripts/rest_framework/neatjson.js +0 -8
- data/vendor/assets/javascripts/rest_framework/trix.js +0 -6
- data/vendor/assets/stylesheets/rest_framework/bootstrap.css +0 -6
- data/vendor/assets/stylesheets/rest_framework/trix.css +0 -410
- /data/app/views/rest_framework/{_html_form.html.erb → routes_and_forms/_html_form.html.erb} +0 -0
- /data/app/views/rest_framework/{_raw_form.html.erb → routes_and_forms/_raw_form.html.erb} +0 -0
- /data/app/views/rest_framework/{_route.html.erb → routes_and_forms/routes/_route.html.erb} +0 -0
@@ -2,219 +2,377 @@
|
|
2
2
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
3
3
|
|
4
4
|
<%= render partial: "rest_framework/head/external" %>
|
5
|
-
<%= render partial: "rest_framework/head/shared" %>
|
6
5
|
|
7
6
|
<style>
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
}
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
-
|
40
|
-
|
41
|
-
|
42
|
-
}
|
43
|
-
|
44
|
-
/*
|
45
|
-
.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
.
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
}
|
7
|
+
:root {
|
8
|
+
--rrf-red: #900;
|
9
|
+
--rrf-red-hover: #5f0c0c;
|
10
|
+
--rrf-light-red: #db2525;
|
11
|
+
--rrf-light-red-hover: #b80404;
|
12
|
+
}
|
13
|
+
#rrfAccentBar {
|
14
|
+
background-color: var(--rrf-red);
|
15
|
+
height: .3em;
|
16
|
+
width: 100%;
|
17
|
+
margin: 0;
|
18
|
+
padding: 0;
|
19
|
+
}
|
20
|
+
header nav { background-color: black; }
|
21
|
+
|
22
|
+
/* Header adjustments. */
|
23
|
+
h1 { font-size: 2rem; }
|
24
|
+
h2 { font-size: 1.7rem; }
|
25
|
+
h3 { font-size: 1.5rem; }
|
26
|
+
h4 { font-size: 1.3rem; }
|
27
|
+
h5 { font-size: 1.1rem; }
|
28
|
+
h6 { font-size: 1rem; }
|
29
|
+
h1, h2, h3, h4, h5, h6 {
|
30
|
+
color: var(--rrf-red);
|
31
|
+
font-weight: normal;
|
32
|
+
margin-bottom: 0;
|
33
|
+
}
|
34
|
+
html[data-bs-theme="dark"] h1,
|
35
|
+
html[data-bs-theme="dark"] h2,
|
36
|
+
html[data-bs-theme="dark"] h3,
|
37
|
+
html[data-bs-theme="dark"] h4,
|
38
|
+
html[data-bs-theme="dark"] h5,
|
39
|
+
html[data-bs-theme="dark"] h6 {
|
40
|
+
color: var(--rrf-light-red);
|
41
|
+
}
|
42
|
+
|
43
|
+
/* Improve code and code blocks. */
|
44
|
+
pre code, .trix-content pre {
|
45
|
+
display: block;
|
46
|
+
overflow-x: auto;
|
47
|
+
padding: .5em !important;
|
48
|
+
}
|
49
|
+
code, .trix-content pre {
|
50
|
+
--bs-code-color: black;
|
51
|
+
background-color: #eee !important;
|
52
|
+
border: 1px solid #aaa;
|
53
|
+
border-radius: 3px;
|
54
|
+
padding: .1em .3em;
|
55
|
+
}
|
56
|
+
html[data-bs-theme="dark"] code, html[data-bs-theme="dark"] .trix-content pre {
|
57
|
+
--bs-code-color: white;
|
58
|
+
background-color: #2b2b2b !important;
|
59
|
+
}
|
60
|
+
|
61
|
+
/* Anchors */
|
62
|
+
a:not(.nav-link) {
|
63
|
+
text-decoration: none;
|
64
|
+
color: var(--rrf-red);
|
65
|
+
}
|
66
|
+
a:hover:not(.nav-link) {
|
67
|
+
text-decoration: underline;
|
68
|
+
color: var(--rrf-red-hover);
|
69
|
+
}
|
70
|
+
html[data-bs-theme="dark"] a:not(.nav-link) { color: var(--rrf-light-red); }
|
71
|
+
html[data-bs-theme="dark"] a:hover:not(.nav-link) { color: var(--rrf-light-red-hover); }
|
72
|
+
|
73
|
+
/* Reduce label font size. */
|
74
|
+
label.form-label {
|
75
|
+
font-size: .8em;
|
76
|
+
}
|
77
|
+
|
78
|
+
/* Make Trix buttons visible even in dark mode. */
|
79
|
+
trix-toolbar .trix-button-group {
|
80
|
+
background-color: #eee;
|
81
|
+
}
|
82
|
+
|
83
|
+
/* Make Trix dialog URL input visible in dark mode. */
|
84
|
+
input.trix-input--dialog {
|
85
|
+
color: black;
|
86
|
+
}
|
87
|
+
|
88
|
+
/* Make route group expansion obvious to the user. */
|
89
|
+
.rrf-routes .rrf-route-group-header:hover {
|
90
|
+
background-color: #ddd;
|
91
|
+
}
|
92
|
+
html[data-bs-theme="dark"] .rrf-routes .rrf-route-group-header:hover {
|
93
|
+
background-color: #333;
|
94
|
+
}
|
95
|
+
.rrf-routes .rrf-route-group-header td {
|
96
|
+
cursor: pointer;
|
97
|
+
}
|
98
|
+
|
99
|
+
/* Disable bootstrap's collapsing animation because in tables it causes delayed jerkiness. */
|
100
|
+
.rrf-routes .collapsing {
|
101
|
+
-webkit-transition: none;
|
102
|
+
transition: none;
|
103
|
+
display: none;
|
104
|
+
}
|
105
|
+
|
106
|
+
/* Copy-to-clipboard styles. */
|
107
|
+
.rrf-copy {
|
108
|
+
position: relative;
|
109
|
+
}
|
110
|
+
.rrf-copy .rrf-copy-link {
|
111
|
+
position: absolute;
|
112
|
+
top: .25em;
|
113
|
+
right: .4em;
|
114
|
+
transition: 0.3s ease;
|
115
|
+
font-size: 1.5em;
|
116
|
+
}
|
117
|
+
.rrf-copy .rrf-copy-link.rrf-clicked {
|
118
|
+
color: green;
|
119
|
+
}
|
120
|
+
.rrf-copy .rrf-copy-link.rrf-clicked:hover {
|
121
|
+
color: green;
|
122
|
+
}
|
61
123
|
</style>
|
62
124
|
|
63
125
|
<script>
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
.replaceAll("<", "<")
|
73
|
-
.replaceAll(">", ">")
|
74
|
-
.replaceAll('"', """)
|
75
|
-
.replaceAll("'", "'")
|
76
|
-
})
|
126
|
+
// Javascript for dark/light mode.
|
127
|
+
;(() => {
|
128
|
+
// Get the real mode from a selected mode. Anything other than "light" or "dark" is treated as
|
129
|
+
// "system" mode.
|
130
|
+
const rrfGetRealMode = (selectedMode) => {
|
131
|
+
if (selectedMode === "light" || selectedMode === "dark") {
|
132
|
+
return selectedMode
|
133
|
+
}
|
77
134
|
|
78
|
-
|
79
|
-
|
80
|
-
|
135
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
136
|
+
return "dark"
|
137
|
+
}
|
81
138
|
|
82
|
-
|
83
|
-
|
84
|
-
el.innerHTML = rrfLinkify(el.innerHTML)
|
85
|
-
})
|
139
|
+
return "light"
|
140
|
+
}
|
86
141
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
"
|
91
|
-
|
92
|
-
|
93
|
-
})
|
142
|
+
// Set the mode, given a "selected" mode.
|
143
|
+
const rrfSetSelectedMode = (selectedMode) => {
|
144
|
+
// Anything except "light" or "dark" is casted to "system".
|
145
|
+
if (selectedMode !== "light" && selectedMode !== "dark") {
|
146
|
+
selectedMode = "system"
|
147
|
+
}
|
94
148
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
}
|
149
|
+
// Store selected mode in `localStorage`.
|
150
|
+
localStorage.setItem("rrfMode", selectedMode)
|
151
|
+
|
152
|
+
// Set the mode selector to the selected mode.
|
153
|
+
const modeComponent = document.getElementById("rrfModeComponent")
|
154
|
+
if (modeComponent) {
|
155
|
+
let labelHTML
|
156
|
+
modeComponent.querySelectorAll("button[data-rrf-mode-value]").forEach((el) => {
|
157
|
+
if (el.getAttribute("data-rrf-mode-value") === selectedMode) {
|
158
|
+
el.classList.add("active")
|
159
|
+
labelHTML = el.querySelector("i").outerHTML.replace("ms-2", "me-1")
|
160
|
+
} else {
|
161
|
+
el.classList.remove("active")
|
162
|
+
}
|
163
|
+
})
|
164
|
+
modeComponent.querySelector("button[data-bs-toggle]").innerHTML = labelHTML
|
110
165
|
}
|
111
|
-
event.preventDefault()
|
112
|
-
})
|
113
|
-
})
|
114
166
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
167
|
+
// Get the real mode to use.
|
168
|
+
realMode = rrfGetRealMode(selectedMode)
|
169
|
+
|
170
|
+
// Set the `realMode` effects.
|
171
|
+
if (realMode === "light") {
|
172
|
+
document.querySelectorAll(".rrf-light-mode").forEach((el) => {
|
173
|
+
el.disabled = false
|
174
|
+
})
|
175
|
+
document.querySelectorAll(".rrf-dark-mode").forEach((el) => {
|
176
|
+
el.disabled = true
|
177
|
+
})
|
178
|
+
document.querySelectorAll(".rrf-mode").forEach((el) => {
|
179
|
+
el.setAttribute("data-bs-theme", "light")
|
180
|
+
})
|
181
|
+
} else if (realMode === "dark") {
|
182
|
+
document.querySelectorAll(".rrf-light-mode").forEach((el) => {
|
183
|
+
el.disabled = true
|
184
|
+
})
|
185
|
+
document.querySelectorAll(".rrf-dark-mode").forEach((el) => {
|
186
|
+
el.disabled = false
|
187
|
+
})
|
188
|
+
document.querySelectorAll(".rrf-mode").forEach((el) => {
|
189
|
+
el.setAttribute("data-bs-theme", "dark")
|
190
|
+
})
|
124
191
|
} else {
|
125
|
-
|
192
|
+
console.log(`RRF: Unknown mode: ${mode}`)
|
126
193
|
}
|
127
194
|
}
|
128
195
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
//
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
// If the media type is `multipart/form-data`, then we need to build a FormData object.
|
178
|
-
if (mediaType == "multipart/form-data") {
|
179
|
-
let formData = new FormData()
|
180
|
-
|
181
|
-
// Add body to `formData`.
|
182
|
-
const bodySearchParams = new URLSearchParams(body)
|
183
|
-
bodySearchParams.forEach((value, key) => {
|
184
|
-
formData.append(key, value)
|
196
|
+
// Initialize dark/light mode before page fully loads to prevent flash.
|
197
|
+
rrfSetSelectedMode(localStorage.getItem("rrfMode"))
|
198
|
+
|
199
|
+
// Initialize dark/light mode after page load (mostly so mode component is updated).
|
200
|
+
document.addEventListener("DOMContentLoaded", (event) => {
|
201
|
+
rrfSetSelectedMode(localStorage.getItem("rrfMode"))
|
202
|
+
|
203
|
+
// Also set up mode selector.
|
204
|
+
document.querySelectorAll("#rrfModeComponent button[data-rrf-mode-value]").forEach((el) => {
|
205
|
+
el.addEventListener("click", (event) => {
|
206
|
+
rrfSetSelectedMode(event.target.getAttribute("data-rrf-mode-value"))
|
207
|
+
})
|
208
|
+
})
|
209
|
+
})
|
210
|
+
|
211
|
+
// Handle case where user changes system theme.
|
212
|
+
if (window.matchMedia) {
|
213
|
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
214
|
+
const selectedMode = localStorage.getItem("rrfMode")
|
215
|
+
if (selectedMode !== "light" && selectedMode !== "dark") {
|
216
|
+
rrfSetSelectedMode("system")
|
217
|
+
}
|
218
|
+
})
|
219
|
+
}
|
220
|
+
})();
|
221
|
+
|
222
|
+
document.addEventListener("DOMContentLoaded", (event) => {
|
223
|
+
// Pretty-print JSON.
|
224
|
+
document.querySelectorAll(".language-json").forEach((el) => {
|
225
|
+
el.innerHTML = neatJSON(JSON.parse(el.innerText), {
|
226
|
+
wrap: 80,
|
227
|
+
afterComma: 1,
|
228
|
+
afterColon: 1,
|
229
|
+
}).replaceAll("&", "&")
|
230
|
+
.replaceAll("<", "<")
|
231
|
+
.replaceAll(">", ">")
|
232
|
+
.replaceAll('"', """)
|
233
|
+
.replaceAll("'", "'")
|
234
|
+
})
|
235
|
+
|
236
|
+
// Then highlight it.
|
237
|
+
hljs.configure({cssSelector: "pre code"})
|
238
|
+
hljs.highlightAll()
|
239
|
+
|
240
|
+
// Replace text node links with anchor tag links.
|
241
|
+
document.querySelectorAll(".rrf-copy code").forEach((el) => {
|
242
|
+
el.innerHTML = rrfLinkify(el.innerHTML)
|
185
243
|
})
|
186
244
|
|
187
|
-
//
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
245
|
+
// Insert copy links.
|
246
|
+
document.querySelectorAll(".rrf-copy").forEach((el) => {
|
247
|
+
el.insertAdjacentHTML(
|
248
|
+
"afterbegin",
|
249
|
+
'<a class="rrf-copy-link" title="Copy to Clipboard" href="#"><i class="bi bi-clipboard-fill"></i></a>',
|
250
|
+
)
|
251
|
+
})
|
252
|
+
|
253
|
+
// Copy link implementation.
|
254
|
+
document.querySelectorAll(".rrf-copy-link").forEach((el) => {
|
255
|
+
el.addEventListener("click", (event) => {
|
256
|
+
const range = document.createRange()
|
257
|
+
range.selectNode(el.nextSibling)
|
258
|
+
window.getSelection().removeAllRanges()
|
259
|
+
window.getSelection().addRange(range)
|
260
|
+
if (document.execCommand("copy")) {
|
261
|
+
// Trigger clicked animation.
|
262
|
+
el.classList.add("rrf-clicked")
|
263
|
+
el.innerHTML = '<i class="bi bi-clipboard-check-fill">'
|
264
|
+
setTimeout(() => {
|
265
|
+
el.classList.remove("rrf-clicked")
|
266
|
+
el.innerHTML = '<i class="bi bi-clipboard-fill">'
|
267
|
+
}, 1000)
|
194
268
|
}
|
269
|
+
event.preventDefault()
|
195
270
|
})
|
271
|
+
})
|
272
|
+
|
273
|
+
// Check if `rawFilesFormWrapper` should be displayed when media type is changed.
|
274
|
+
const rawFormRouteSelect = document.getElementById("rawFormRoute")
|
275
|
+
const rawFormMediaTypeSelect = document.getElementById("rawFormMediaType")
|
276
|
+
const rawFilesFormWrapper = document.getElementById("rawFilesFormWrapper")
|
277
|
+
if (rawFilesFormWrapper) {
|
278
|
+
const rawFormFilesHandler = () => {
|
279
|
+
const selectedRouteOption = rawFormRouteSelect.options[rawFormRouteSelect.selectedIndex]
|
280
|
+
if (rawFormMediaTypeSelect.value === "multipart/form-data" && selectedRouteOption.dataset.supportsFiles) {
|
281
|
+
rawFilesFormWrapper.style.display = "block"
|
282
|
+
} else {
|
283
|
+
rawFilesFormWrapper.style.display = "none"
|
284
|
+
}
|
285
|
+
}
|
286
|
+
|
287
|
+
rawFormRouteSelect.addEventListener("change", rawFormFilesHandler)
|
288
|
+
rawFormMediaTypeSelect.addEventListener("change", rawFormFilesHandler)
|
196
289
|
}
|
290
|
+
})
|
197
291
|
|
198
|
-
|
199
|
-
|
292
|
+
// Convert plain-text links to anchor tag links.
|
293
|
+
function rrfLinkify(text) {
|
294
|
+
return text.replace(/(https?:\/\/[^\s<>"]+)/g, "<a href=\"$1\" target=\"_blank\">$1</a>")
|
200
295
|
}
|
201
296
|
|
202
|
-
//
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
})
|
209
|
-
}
|
297
|
+
// Replace the document when doing form submission (mainly to support PUT/PATCH/DELETE).
|
298
|
+
function rrfReplaceDocument(content) {
|
299
|
+
// Replace the document with provided content.
|
300
|
+
document.open()
|
301
|
+
document.write(content)
|
302
|
+
document.close()
|
210
303
|
|
211
|
-
//
|
212
|
-
|
213
|
-
|
214
|
-
delete kwargs.headers
|
304
|
+
// Trigger `DOMContentLoaded` manually so our custom JavaScript works.
|
305
|
+
document.dispatchEvent(new Event("DOMContentLoaded", {bubbles: true, cancelable: true}))
|
306
|
+
}
|
215
307
|
|
216
|
-
|
217
|
-
|
218
|
-
.
|
219
|
-
|
308
|
+
// Refresh the window as a `GET` request.
|
309
|
+
function rrfGet(button) {
|
310
|
+
button.disabled = true
|
311
|
+
window.location.replace(window.location.href)
|
312
|
+
}
|
313
|
+
|
314
|
+
// Call `DELETE` on the current path.
|
315
|
+
function rrfDelete(button) {
|
316
|
+
button.disabled = true
|
317
|
+
rrfAPICall(window.location.pathname, "DELETE")
|
318
|
+
}
|
319
|
+
|
320
|
+
// Call `OPTIONS` on the current path.
|
321
|
+
function rrfOptions(button) {
|
322
|
+
button.disabled = true
|
323
|
+
rrfAPICall(window.location.pathname, "OPTIONS")
|
324
|
+
}
|
325
|
+
|
326
|
+
// Submit the raw form.
|
327
|
+
function rrfSubmitRawForm(button) {
|
328
|
+
button.disabled = true
|
329
|
+
|
330
|
+
// Grab the selected route/method, media type, and the body.
|
331
|
+
const [method, path] = document.getElementById("rawFormRoute").value.split(":")
|
332
|
+
const mediaType = document.getElementById("rawFormMediaType").value
|
333
|
+
let body = document.getElementById("rawFormContent").value
|
334
|
+
|
335
|
+
// If the media type is `multipart/form-data`, then we need to build a FormData object.
|
336
|
+
if (mediaType == "multipart/form-data") {
|
337
|
+
let formData = new FormData()
|
338
|
+
|
339
|
+
// Add body to `formData`.
|
340
|
+
const bodySearchParams = new URLSearchParams(body)
|
341
|
+
bodySearchParams.forEach((value, key) => {
|
342
|
+
formData.append(key, value)
|
343
|
+
})
|
344
|
+
|
345
|
+
// Add file(s) to `formData`.
|
346
|
+
const rawFilesForm = document.getElementById("rawFilesForm")
|
347
|
+
if (rawFilesForm) {
|
348
|
+
rawFilesForm.querySelectorAll("input[type=file]").forEach((el) => {
|
349
|
+
const files = el.files
|
350
|
+
for (let i = 0; i < files.length; i++) {
|
351
|
+
formData.append(el.name, files[i])
|
352
|
+
}
|
353
|
+
})
|
354
|
+
}
|
355
|
+
|
356
|
+
// Set body to be the form data.
|
357
|
+
body = formData
|
358
|
+
}
|
359
|
+
|
360
|
+
// Perform the API call.
|
361
|
+
rrfAPICall(path, method, {
|
362
|
+
body,
|
363
|
+
// If the media type is `multipart/form-data`, then we don't want to set the content type
|
364
|
+
// because it must be set by `fetch` to include boundary.
|
365
|
+
headers: mediaType == "multipart/form-data" ? {} : {"Content-Type": mediaType},
|
366
|
+
})
|
367
|
+
}
|
368
|
+
|
369
|
+
// Make an HTML API call and replace the document with the response.
|
370
|
+
function rrfAPICall(path, method, kwargs={}) {
|
371
|
+
const headers = kwargs.headers || {}
|
372
|
+
delete kwargs.headers
|
373
|
+
|
374
|
+
fetch(path, {method, headers: {"Accept": "text/html", ...headers}, ...kwargs})
|
375
|
+
.then((response) => response.text())
|
376
|
+
.then((body) => { rrfReplaceDocument(body) })
|
377
|
+
}
|
220
378
|
</script>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div class="w-100 m-0 p-0" id="rrfAccentBar"></div>
|
2
|
+
<nav class="navbar navbar-expand-md py-0" data-bs-theme="dark">
|
3
|
+
<div class="container">
|
4
|
+
<span class="navbar-brand p-0">
|
5
|
+
<h1 title="RRF v<%= RESTFramework::VERSION %>" class="text-light font-weight-light m-0 p-0" style="font-size: 1em">
|
6
|
+
<%= @template_logo_text || @header_title || "Rails REST Framework" %>
|
7
|
+
</h1>
|
8
|
+
</span>
|
9
|
+
<% if content_for? :header_menu %>
|
10
|
+
<%= yield :header_menu %>
|
11
|
+
<% else %>
|
12
|
+
<%= render partial: "rest_framework/header/mode" %>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
15
|
+
</nav>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div class="row">
|
2
|
+
<div>
|
3
|
+
<%= render partial: "rest_framework/heading/actions" if @route_groups.present? %>
|
4
|
+
<h1 class="m-0"><%= @heading_title || @title %></h1>
|
5
|
+
<% if @description.present? %>
|
6
|
+
<br><br><p style="display: inline-block; margin-bottom: 0"><%= @description %></p>
|
7
|
+
<% end %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
<hr/>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<div class="row">
|
2
|
+
<div>
|
3
|
+
<ul class="nav nav-tabs">
|
4
|
+
<% if @json_payload.present? %>
|
5
|
+
<li class="nav-item">
|
6
|
+
<a class="nav-link active" href="#tab-json" data-bs-toggle="tab" role="tab">
|
7
|
+
.json
|
8
|
+
</a>
|
9
|
+
</li>
|
10
|
+
<% end %>
|
11
|
+
<% if @xml_payload.present? %>
|
12
|
+
<li class="nav-item">
|
13
|
+
<a class="nav-link" href="#tab-xml" data-bs-toggle="tab" role="tab">
|
14
|
+
.xml
|
15
|
+
</a>
|
16
|
+
</li>
|
17
|
+
<% end %>
|
18
|
+
</ul>
|
19
|
+
</div>
|
20
|
+
<div class="tab-content pt-2">
|
21
|
+
<div class="tab-pane fade show active" id="tab-json" role="tabpanel">
|
22
|
+
<% if @json_payload.present? %>
|
23
|
+
<div><pre class="rrf-copy"><code class="language-json"><%=
|
24
|
+
JSON.pretty_generate(JSON.parse(@json_payload)) unless @json_payload == ""
|
25
|
+
%></code></pre></div>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
<div class="tab-pane fade" id="tab-xml" role="tabpanel">
|
29
|
+
<% if @xml_payload.present? %>
|
30
|
+
<div><pre class="rrf-copy"><code class="language-xml"><%=
|
31
|
+
CGI.unescapeHTML(@xml_payload)
|
32
|
+
%></code></pre></div>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div class="row">
|
2
|
+
<div>
|
3
|
+
<pre><code><%
|
4
|
+
concat request.request_method
|
5
|
+
if request.method != request.request_method
|
6
|
+
concat " (via #{request.method})"
|
7
|
+
end
|
8
|
+
concat " #{request.path}"
|
9
|
+
%></code></pre>
|
10
|
+
<pre><code><%
|
11
|
+
concat "HTTP #{response.status} #{response.message}"
|
12
|
+
concat "\n"
|
13
|
+
concat "Content-Type: #{response.content_type}"
|
14
|
+
%></code></pre>
|
15
|
+
</div>
|
16
|
+
</div>
|