rest_framework 0.8.16 → 0.9.0
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/layouts/rest_framework.html.erb +149 -132
- data/app/views/rest_framework/_head.html.erb +204 -44
- data/app/views/rest_framework/_html_form.html.erb +1 -1
- 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 +137 -0
- data/docs/index.md +133 -0
- data/lib/rest_framework/serializers.rb +1 -1
- metadata +19 -2
@@ -0,0 +1,35 @@
|
|
1
|
+
<head>
|
2
|
+
<meta charset="utf-8">
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
4
|
+
<link rel='icon' type='image/x-icon' href='/assets/images/favicon.ico' />
|
5
|
+
|
6
|
+
<title>{% if page.title %}{{ page.title | escape }}{% else %}{{ site.title | escape }}{% endif %}</title>
|
7
|
+
|
8
|
+
<!-- Bootstrap -->
|
9
|
+
<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">
|
10
|
+
<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>
|
11
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css">
|
12
|
+
|
13
|
+
<!-- Highlight.js -->
|
14
|
+
<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" />
|
15
|
+
<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" />
|
16
|
+
<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>
|
17
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/shell.min.js" integrity="sha512-X2JngetHwVsp0j3n6lo8HGdXQKLpz2hwFfQkG996OfanpFaQJFgjKJlmzsdefWsHTQIwY539tD09JF48kCPMXw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
18
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/erb.min.js" integrity="sha512-flbEiCcectGeyRXyuMZW5jlAGIQ1/qrTZS6DsZDTqObM0JG/isYHvUyehOyt14ssmY85gZRYra+IJR9+azRuqw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
19
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/ruby.min.js" integrity="sha512-xRUQANk9Iw3wtAp0cBOa1Ghr7yIFrMiJiEujrMGf04qOau23exxj4R7DLUeLGfLiDbVSK0FyN8v2ns4m/6iNmQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
20
|
+
<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>
|
21
|
+
<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>
|
22
|
+
|
23
|
+
<link rel="stylesheet" href="{{ "/assets/css/rest_framework.css" }}">
|
24
|
+
<script src="{{ "/assets/js/rest_framework.js" }}"></script>
|
25
|
+
|
26
|
+
<!-- Global site tag (gtag.js) - Google Analytics -->
|
27
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-P2KRPNXQMT"></script>
|
28
|
+
<script>
|
29
|
+
window.dataLayer = window.dataLayer || [];
|
30
|
+
function gtag(){dataLayer.push(arguments);}
|
31
|
+
gtag('js', new Date());
|
32
|
+
|
33
|
+
gtag('config', 'G-P2KRPNXQMT');
|
34
|
+
</script>
|
35
|
+
</head>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<header>
|
2
|
+
<div class="w-100 m-0 p-0" id="rrfAccentBar"></div>
|
3
|
+
<nav class="navbar py-0 navbar-expand-md" data-bs-theme="dark">
|
4
|
+
<div class="container">
|
5
|
+
|
6
|
+
<span class="navbar-brand p-0">
|
7
|
+
<a href="/">
|
8
|
+
<h1 class="text-light font-weight-light m-0 p-0" style="font-size: 1em; ">{{ site.title }}</h1>
|
9
|
+
</a>
|
10
|
+
</span>
|
11
|
+
|
12
|
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent">
|
13
|
+
<span class="navbar-toggler-icon"></span>
|
14
|
+
</button>
|
15
|
+
|
16
|
+
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
17
|
+
<ul class="navbar-nav ms-auto">
|
18
|
+
<li class="nav-item">
|
19
|
+
<a class="nav-link{% if page.url == '/' %} active{% endif %}" href="/">Home</a>
|
20
|
+
</li>
|
21
|
+
|
22
|
+
<li class="nav-item dropdown">
|
23
|
+
<a class="nav-link dropdown-toggle {% if page.url contains 'guide' %} active{% endif %}" href="#" role="button" data-bs-toggle="dropdown">
|
24
|
+
Guide
|
25
|
+
</a>
|
26
|
+
<div class="rrf-mode dropdown-menu">
|
27
|
+
{% for section in site.guide %}
|
28
|
+
<a class="dropdown-item" href="{{ section.url }}">{{ section.title }}</a>
|
29
|
+
{% endfor %}
|
30
|
+
</div>
|
31
|
+
</li>
|
32
|
+
|
33
|
+
<li class="nav-item ps-2" id="rrfGithubAndModeWrapper">
|
34
|
+
<span id="rrfGithubComponent">
|
35
|
+
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
36
|
+
<a class="github-button" href="https://github.com/gregschmit/rails-rest-framework" data-show-count="true" aria-label="Star gregschmit/rails-rest-framework on GitHub">Star</a>
|
37
|
+
</span>
|
38
|
+
<div class="dropdown ms-auto float-end" id="rrfModeComponent">
|
39
|
+
<button class="btn btn-dark dropdown-toggle rounded-0 bg-black" style="border-color: black" data-bs-toggle="dropdown"></button>
|
40
|
+
<div class="rrf-mode dropdown-menu dropdown-menu-end py-0 rounded-0" style="font-size: .8em; min-width: 0">
|
41
|
+
<button class="dropdown-item text-end" data-rrf-mode-value="system">
|
42
|
+
System<i class="bi bi-circle-half ms-2"></i>
|
43
|
+
</button>
|
44
|
+
<button class="dropdown-item text-end" data-rrf-mode-value="light">
|
45
|
+
Light<i class="bi bi-sun-fill ms-2"></i>
|
46
|
+
</button>
|
47
|
+
<button class="dropdown-item text-end" data-rrf-mode-value="dark">
|
48
|
+
Dark<i class="bi bi-moon-stars-fill ms-2"></i>
|
49
|
+
</button>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</li>
|
53
|
+
</ul>
|
54
|
+
</div>
|
55
|
+
|
56
|
+
</div>
|
57
|
+
</nav>
|
58
|
+
</header>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html class="rrf-mode">
|
3
|
+
{% include head.html %}
|
4
|
+
<body>
|
5
|
+
{% include header.html %}
|
6
|
+
<div class="container pb-3">
|
7
|
+
<div id="headersTable" class="headers-table float-md-end m-2"></div>
|
8
|
+
{% include anchor_headings.html html=content anchorBody="#" %}
|
9
|
+
</div>
|
10
|
+
</body>
|
11
|
+
</html>
|
@@ -0,0 +1,159 @@
|
|
1
|
+
/********************************
|
2
|
+
* START OF LIB/DOCS COMMON CSS *
|
3
|
+
********************************/
|
4
|
+
|
5
|
+
:root {
|
6
|
+
--rrf-red: #900;
|
7
|
+
--rrf-red-hover: #5f0c0c;
|
8
|
+
--rrf-light-red: #db2525;
|
9
|
+
--rrf-light-red-hover: #b80404;
|
10
|
+
}
|
11
|
+
#rrfAccentBar {
|
12
|
+
background-color: var(--rrf-red);
|
13
|
+
height: .3em;
|
14
|
+
}
|
15
|
+
header nav { background-color: black; }
|
16
|
+
|
17
|
+
/* Header adjustments. */
|
18
|
+
h1 { font-size: 2rem; }
|
19
|
+
h2 { font-size: 1.7rem; }
|
20
|
+
h3 { font-size: 1.5rem; }
|
21
|
+
h4 { font-size: 1.3rem; }
|
22
|
+
h5 { font-size: 1.1rem; }
|
23
|
+
h6 { font-size: 1rem; }
|
24
|
+
h1, h2, h3, h4, h5, h6 {
|
25
|
+
color: var(--rrf-red);
|
26
|
+
}
|
27
|
+
html[data-bs-theme="dark"] h1,
|
28
|
+
html[data-bs-theme="dark"] h2,
|
29
|
+
html[data-bs-theme="dark"] h3,
|
30
|
+
html[data-bs-theme="dark"] h4,
|
31
|
+
html[data-bs-theme="dark"] h5,
|
32
|
+
html[data-bs-theme="dark"] h6 {
|
33
|
+
color: var(--rrf-light-red);
|
34
|
+
}
|
35
|
+
|
36
|
+
/* Improve code and code blocks. */
|
37
|
+
pre code {
|
38
|
+
display: block;
|
39
|
+
overflow-x: auto;
|
40
|
+
}
|
41
|
+
code, .trix-content pre {
|
42
|
+
padding: .5em !important;
|
43
|
+
background-color: #eee !important;
|
44
|
+
border: 1px solid #aaa;
|
45
|
+
border-radius: 3px;
|
46
|
+
}
|
47
|
+
p code {
|
48
|
+
padding: .1em .3em !important;
|
49
|
+
}
|
50
|
+
html[data-bs-theme="dark"] code, html[data-bs-theme="dark"] .trix-content pre {
|
51
|
+
background-color: #2b2b2b !important;
|
52
|
+
}
|
53
|
+
|
54
|
+
/* Anchors */
|
55
|
+
a:not(.nav-link) {
|
56
|
+
text-decoration: none;
|
57
|
+
color: var(--rrf-red);
|
58
|
+
}
|
59
|
+
a:hover:not(.nav-link) {
|
60
|
+
text-decoration: underline;
|
61
|
+
color: var(--rrf-red-hover);
|
62
|
+
}
|
63
|
+
html[data-bs-theme="dark"] a:not(.nav-link) { color: var(--rrf-light-red); }
|
64
|
+
html[data-bs-theme="dark"] a:hover:not(.nav-link) { color: var(--rrf-light-red-hover); }
|
65
|
+
|
66
|
+
/******************************
|
67
|
+
* END OF LIB/DOCS COMMON CSS *
|
68
|
+
******************************/
|
69
|
+
|
70
|
+
/* Header adjustments. */
|
71
|
+
h1, h2, h3, h4, h5, h6 {
|
72
|
+
width: 100%;
|
73
|
+
font-weight: normal;
|
74
|
+
margin-top: 1.8rem;
|
75
|
+
margin-bottom: 1.2rem;
|
76
|
+
}
|
77
|
+
h1 a:not(:hover),
|
78
|
+
h2 a:not(:hover),
|
79
|
+
h3 a:not(:hover),
|
80
|
+
h4 a:not(:hover),
|
81
|
+
h5 a:not(:hover),
|
82
|
+
h6 a:not(:hover) {
|
83
|
+
color: #ddd;
|
84
|
+
}
|
85
|
+
html[data-bs-theme="dark"] h1 a:not(:hover),
|
86
|
+
html[data-bs-theme="dark"] h2 a:not(:hover),
|
87
|
+
html[data-bs-theme="dark"] h3 a:not(:hover),
|
88
|
+
html[data-bs-theme="dark"] h4 a:not(:hover),
|
89
|
+
html[data-bs-theme="dark"] h5 a:not(:hover),
|
90
|
+
html[data-bs-theme="dark"] h6 a:not(:hover) {
|
91
|
+
color: #444;
|
92
|
+
}
|
93
|
+
h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover {
|
94
|
+
text-decoration: none !important;
|
95
|
+
}
|
96
|
+
|
97
|
+
/* Navbar */
|
98
|
+
.navbar .navbar-toggler {
|
99
|
+
margin: .2em 0;
|
100
|
+
padding: .2em .3em;
|
101
|
+
border: none;
|
102
|
+
}
|
103
|
+
.navbar .navbar-toggler .navbar-toggler-icon {
|
104
|
+
height: 1.1em;
|
105
|
+
width: 1.1em;
|
106
|
+
}
|
107
|
+
.navbar .navbar-nav .nav-item .nav-link {
|
108
|
+
padding: .45em .6em;
|
109
|
+
}
|
110
|
+
.navbar .navbar-nav .nav-item .nav-link:hover {
|
111
|
+
background-color: #262a2f;
|
112
|
+
}
|
113
|
+
.navbar .dropdown-menu a.dropdown-item {
|
114
|
+
font-size: .9em;
|
115
|
+
padding: .2em .8em;
|
116
|
+
}
|
117
|
+
|
118
|
+
/* Headers table. */
|
119
|
+
.headers-table {
|
120
|
+
padding: .5em 1em;
|
121
|
+
background-color: #eee;
|
122
|
+
border: 1px solid #aaa;
|
123
|
+
border-radius: .3em;
|
124
|
+
font-size: .9em;
|
125
|
+
}
|
126
|
+
html[data-bs-theme="dark"] .headers-table {
|
127
|
+
background-color: #2b2b2b;
|
128
|
+
}
|
129
|
+
.headers-table:empty { display: none; }
|
130
|
+
.headers-table ul {
|
131
|
+
list-style-type: none;
|
132
|
+
margin: 0;
|
133
|
+
padding-left: 0;
|
134
|
+
padding-right: .6em;
|
135
|
+
}
|
136
|
+
.headers-table ul li { margin: .3em 0; }
|
137
|
+
.headers-table ul ul { padding-left: .8em; padding-right: 0; }
|
138
|
+
.headers-table > ul > li {
|
139
|
+
font-weight: bold;
|
140
|
+
}
|
141
|
+
|
142
|
+
/* Style the github and mode component. */
|
143
|
+
#rrfGithubAndModeWrapper {
|
144
|
+
height: 2.4em;
|
145
|
+
}
|
146
|
+
#rrfGithubComponent {
|
147
|
+
display: inline-block;
|
148
|
+
position: relative;
|
149
|
+
top: .6em;
|
150
|
+
}
|
151
|
+
#rrfModeComponent .dropdown-toggle {
|
152
|
+
float: right;
|
153
|
+
}
|
154
|
+
#rrfModeComponent .dropdown-menu {
|
155
|
+
position: absolute;
|
156
|
+
right: 0;
|
157
|
+
left: auto;
|
158
|
+
top: 100%;
|
159
|
+
}
|
Binary file
|
@@ -0,0 +1,137 @@
|
|
1
|
+
/*******************************
|
2
|
+
* START OF LIB/DOCS COMMON JS *
|
3
|
+
*******************************/
|
4
|
+
|
5
|
+
;(() => {
|
6
|
+
// Get the real mode from a selected mode. Anything other than "light" or "dark" is treated as
|
7
|
+
// "system" mode.
|
8
|
+
const rrfGetRealMode = (selectedMode) => {
|
9
|
+
if (selectedMode === "light" || selectedMode === "dark") {
|
10
|
+
return selectedMode
|
11
|
+
}
|
12
|
+
|
13
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
14
|
+
return "dark"
|
15
|
+
}
|
16
|
+
|
17
|
+
return "light"
|
18
|
+
}
|
19
|
+
|
20
|
+
// Set the mode, given a "selected" mode.
|
21
|
+
const rrfSetSelectedMode = (selectedMode) => {
|
22
|
+
// Anything except "light" or "dark" is casted to "system".
|
23
|
+
if (selectedMode !== "light" && selectedMode !== "dark") {
|
24
|
+
selectedMode = "system"
|
25
|
+
}
|
26
|
+
|
27
|
+
// Store selected mode in `localStorage`.
|
28
|
+
localStorage.setItem("rrfMode", selectedMode)
|
29
|
+
|
30
|
+
// Set the mode selector to the selected mode.
|
31
|
+
const modeComponent = document.getElementById("rrfModeComponent")
|
32
|
+
if (modeComponent) {
|
33
|
+
let labelHTML
|
34
|
+
modeComponent.querySelectorAll("button[data-rrf-mode-value]").forEach((el) => {
|
35
|
+
if (el.getAttribute("data-rrf-mode-value") === selectedMode) {
|
36
|
+
el.classList.add("active")
|
37
|
+
labelHTML = el.querySelector("i").outerHTML.replace("ms-2", "me-1")
|
38
|
+
} else {
|
39
|
+
el.classList.remove("active")
|
40
|
+
}
|
41
|
+
})
|
42
|
+
modeComponent.querySelector("button[data-bs-toggle]").innerHTML = labelHTML
|
43
|
+
}
|
44
|
+
|
45
|
+
// Get the real mode to use.
|
46
|
+
realMode = rrfGetRealMode(selectedMode)
|
47
|
+
|
48
|
+
// Set the `realMode` effects.
|
49
|
+
if (realMode === "light") {
|
50
|
+
document.querySelectorAll(".rrf-light-mode").forEach((el) => {
|
51
|
+
el.disabled = false
|
52
|
+
})
|
53
|
+
document.querySelectorAll(".rrf-dark-mode").forEach((el) => {
|
54
|
+
el.disabled = true
|
55
|
+
})
|
56
|
+
document.querySelectorAll(".rrf-mode").forEach((el) => {
|
57
|
+
el.setAttribute("data-bs-theme", "light")
|
58
|
+
})
|
59
|
+
} else if (realMode === "dark") {
|
60
|
+
document.querySelectorAll(".rrf-light-mode").forEach((el) => {
|
61
|
+
el.disabled = true
|
62
|
+
})
|
63
|
+
document.querySelectorAll(".rrf-dark-mode").forEach((el) => {
|
64
|
+
el.disabled = false
|
65
|
+
})
|
66
|
+
document.querySelectorAll(".rrf-mode").forEach((el) => {
|
67
|
+
el.setAttribute("data-bs-theme", "dark")
|
68
|
+
})
|
69
|
+
} else {
|
70
|
+
console.log(`RRF: Unknown mode: ${mode}`)
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
// Initialize dark/light mode before page fully loads to prevent flash.
|
75
|
+
rrfSetSelectedMode(localStorage.getItem("rrfMode"))
|
76
|
+
|
77
|
+
// Initialize dark/light mode after page load (mostly so mode component is updated).
|
78
|
+
document.addEventListener("DOMContentLoaded", (event) => {
|
79
|
+
rrfSetSelectedMode(localStorage.getItem("rrfMode"))
|
80
|
+
|
81
|
+
// Also set up mode selector.
|
82
|
+
document.querySelectorAll("#rrfModeComponent button[data-rrf-mode-value]").forEach((el) => {
|
83
|
+
el.addEventListener("click", (event) => {
|
84
|
+
rrfSetSelectedMode(event.target.getAttribute("data-rrf-mode-value"))
|
85
|
+
})
|
86
|
+
})
|
87
|
+
})
|
88
|
+
|
89
|
+
// Handle case where user changes system theme.
|
90
|
+
if (window.matchMedia) {
|
91
|
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
92
|
+
const selectedMode = localStorage.getItem("rrfMode")
|
93
|
+
if (selectedMode !== "light" && selectedMode !== "dark") {
|
94
|
+
rrfSetSelectedMode("system")
|
95
|
+
}
|
96
|
+
})
|
97
|
+
}
|
98
|
+
})()
|
99
|
+
|
100
|
+
/*****************************
|
101
|
+
* END OF LIB/DOCS COMMON JS *
|
102
|
+
*****************************/
|
103
|
+
|
104
|
+
document.addEventListener("DOMContentLoaded", () => {
|
105
|
+
// Initialize `Highlight.js`.
|
106
|
+
hljs.configure({ ignoreUnescapedHTML: true })
|
107
|
+
hljs.highlightAll()
|
108
|
+
|
109
|
+
// Setup the floating table of contents.
|
110
|
+
let table = "<ul>"
|
111
|
+
let hlevel = 2
|
112
|
+
let hprevlevel = 2
|
113
|
+
document.querySelectorAll("h2, h3, h4").forEach((header) => {
|
114
|
+
hlevel = parseInt(header.tagName[1])
|
115
|
+
|
116
|
+
if (hlevel > hprevlevel) {
|
117
|
+
table += "<ul>"
|
118
|
+
} else if (hlevel < hprevlevel) {
|
119
|
+
Array(hprevlevel - hlevel)
|
120
|
+
.fill(0)
|
121
|
+
.forEach(function () {
|
122
|
+
table += "</ul>"
|
123
|
+
})
|
124
|
+
}
|
125
|
+
table += `<li><a href="${
|
126
|
+
header.querySelectorAll("a")[0].href
|
127
|
+
}">${header.childNodes[0].nodeValue.trim()}</a></li>`
|
128
|
+
hprevlevel = hlevel
|
129
|
+
})
|
130
|
+
if (hlevel > hprevlevel) {
|
131
|
+
table += "</ul>"
|
132
|
+
}
|
133
|
+
table += "</ul>"
|
134
|
+
if (table != "<ul></ul>") {
|
135
|
+
document.getElementById("headersTable").innerHTML = table
|
136
|
+
}
|
137
|
+
})
|
data/docs/index.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
---
|
2
|
+
---
|
3
|
+
|
4
|
+
# Rails REST Framework
|
5
|
+
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/rest_framework.svg)](https://badge.fury.io/rb/rest_framework)
|
7
|
+
[![Pipeline](https://github.com/gregschmit/rails-rest-framework/actions/workflows/pipeline.yml/badge.svg)](https://github.com/gregschmit/rails-rest-framework/actions/workflows/pipeline.yml)
|
8
|
+
[![Coverage](https://coveralls.io/repos/github/gregschmit/rails-rest-framework/badge.svg?branch=master)](https://coveralls.io/github/gregschmit/rails-rest-framework?branch=master)
|
9
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/ba5df7706cb544d78555/maintainability)](https://codeclimate.com/github/gregschmit/rails-rest-framework/maintainability)
|
10
|
+
|
11
|
+
A framework for DRY RESTful APIs in Ruby on Rails.
|
12
|
+
|
13
|
+
**The Problem**: Building controllers for APIs usually involves writing a lot of redundant CRUD
|
14
|
+
logic, and routing them can be obnoxious. Building and maintaining features like ordering,
|
15
|
+
filtering, and pagination can be tedious.
|
16
|
+
|
17
|
+
**The Solution**: This framework implements browsable API responses, CRUD actions for your models,
|
18
|
+
and features like ordering/filtering/pagination, so you can focus on building awesome APIs.
|
19
|
+
|
20
|
+
Website/Guide: [rails-rest-framework.com](https://rails-rest-framework.com)
|
21
|
+
|
22
|
+
Demo: [demo.rails-rest-framework.com](https://demo.rails-rest-framework.com)
|
23
|
+
|
24
|
+
Source: [github.com/gregschmit/rails-rest-framework](https://github.com/gregschmit/rails-rest-framework)
|
25
|
+
|
26
|
+
YARD Docs: [rubydoc.info/gems/rest_framework](https://rubydoc.info/gems/rest_framework)
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
Add this line to your application's Gemfile:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
gem 'rest_framework'
|
34
|
+
```
|
35
|
+
|
36
|
+
And then execute:
|
37
|
+
|
38
|
+
```shell
|
39
|
+
$ bundle install
|
40
|
+
```
|
41
|
+
|
42
|
+
Or install it yourself with:
|
43
|
+
|
44
|
+
```shell
|
45
|
+
$ gem install rest_framework
|
46
|
+
```
|
47
|
+
|
48
|
+
## Quick Usage Tutorial
|
49
|
+
|
50
|
+
### Controller Mixins
|
51
|
+
|
52
|
+
To transform a controller into a RESTful controller, you can either include `BaseControllerMixin`,
|
53
|
+
`ReadOnlyModelControllerMixin`, or `ModelControllerMixin`. `BaseControllerMixin` provides a `root`
|
54
|
+
action and a simple interface for routing arbitrary additional actions:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class ApiController < ApplicationController
|
58
|
+
include RESTFramework::BaseControllerMixin
|
59
|
+
self.extra_actions = {test: [:get]}
|
60
|
+
|
61
|
+
def test
|
62
|
+
render api_response({message: "Test successful!"})
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
`ModelControllerMixin` assists with providing the standard model CRUD for your controller.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
class Api::MoviesController < ApiController
|
71
|
+
include RESTFramework::ModelControllerMixin
|
72
|
+
|
73
|
+
self.recordset = Movie.where(enabled: true)
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
`ReadOnlyModelControllerMixin` only enables list/show actions, but since we're naming this
|
78
|
+
controller in a way that doesn't make the model obvious, we can set that explicitly:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
class Api::ReadOnlyMoviesController < ApiController
|
82
|
+
include RESTFramework::ReadOnlyModelControllerMixin
|
83
|
+
|
84
|
+
self.model = Movie
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
Note that you can also override the `get_recordset` instance method to override the API behavior
|
89
|
+
dynamically per-request.
|
90
|
+
|
91
|
+
### Routing
|
92
|
+
|
93
|
+
You can use Rails' `resource`/`resources` routers to route your API, however if you want
|
94
|
+
`extra_actions` / `extra_member_actions` to be routed automatically, then you can use `rest_route`
|
95
|
+
for non-resourceful controllers, or `rest_resource` / `rest_resources` resourceful routers. You can
|
96
|
+
also use `rest_root` to route the root of your API:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
Rails.application.routes.draw do
|
100
|
+
rest_root :api # will find `api_controller` and route the `root` action to '/api'
|
101
|
+
namespace :api do
|
102
|
+
rest_resources :movies
|
103
|
+
rest_resources :users
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
Or if you want the API root to be routed to `Api::RootController#root`:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
Rails.application.routes.draw do
|
112
|
+
namespace :api do
|
113
|
+
rest_root # will route `Api::RootController#root` to '/' in this namespace ('/api')
|
114
|
+
rest_resources :movies
|
115
|
+
rest_resources :users
|
116
|
+
end
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
## Development/Testing
|
121
|
+
|
122
|
+
After you clone the repository, cd'ing into the directory should create a new gemset if you are
|
123
|
+
using RVM. Then run `bundle install` to install the appropriate gems.
|
124
|
+
|
125
|
+
To run the test suite:
|
126
|
+
|
127
|
+
```shell
|
128
|
+
$ rails test
|
129
|
+
```
|
130
|
+
|
131
|
+
The top-level `bin/rails` proxies all Rails commands to the test project, so you can operate it via
|
132
|
+
the usual commands. Ensure you run `rails db:setup` before running `rails server` or
|
133
|
+
`rails console`.
|
@@ -281,7 +281,7 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
|
|
281
281
|
end
|
282
282
|
else
|
283
283
|
self.define_singleton_method(f) do |record|
|
284
|
-
next record.send(f).map
|
284
|
+
next record.send(f).map(&:url)
|
285
285
|
end
|
286
286
|
end
|
287
287
|
elsif @model.method_defined?(f)
|
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.
|
4
|
+
version: 0.9.0
|
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-04-
|
11
|
+
date: 2023-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -40,6 +40,23 @@ files:
|
|
40
40
|
- app/views/rest_framework/_raw_form.html.erb
|
41
41
|
- app/views/rest_framework/_route.html.erb
|
42
42
|
- app/views/rest_framework/_routes.html.erb
|
43
|
+
- docs/CNAME
|
44
|
+
- docs/Gemfile
|
45
|
+
- docs/Gemfile.lock
|
46
|
+
- docs/_config.yml
|
47
|
+
- docs/_guide/1_routers.md
|
48
|
+
- docs/_guide/2_controller_mixins.md
|
49
|
+
- docs/_guide/3_serializers.md
|
50
|
+
- docs/_guide/4_filtering_and_ordering.md
|
51
|
+
- docs/_guide/5_pagination.md
|
52
|
+
- docs/_includes/anchor_headings.html
|
53
|
+
- docs/_includes/head.html
|
54
|
+
- docs/_includes/header.html
|
55
|
+
- docs/_layouts/default.html
|
56
|
+
- docs/assets/css/rest_framework.css
|
57
|
+
- docs/assets/images/favicon.ico
|
58
|
+
- docs/assets/js/rest_framework.js
|
59
|
+
- docs/index.md
|
43
60
|
- lib/rest_framework.rb
|
44
61
|
- lib/rest_framework/controller_mixins.rb
|
45
62
|
- lib/rest_framework/controller_mixins/base.rb
|