rest_framework 0.9.2 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/views/layouts/rest_framework.html.erb +6 -1
  4. data/app/views/rest_framework/_head.html.erb +2 -194
  5. data/app/views/rest_framework/head/_external.html.erb +13 -0
  6. data/{docs/assets/js/rest_framework.js → app/views/rest_framework/head/_shared.html} +69 -42
  7. data/docs/Gemfile +1 -0
  8. data/docs/Gemfile.lock +14 -14
  9. data/docs/_config.yml +4 -2
  10. data/docs/_guide/2_controllers.md +342 -0
  11. data/docs/_guide/3_serializers.md +1 -1
  12. data/docs/_guide/4_filtering_and_ordering.md +8 -8
  13. data/docs/_includes/external.html +9 -0
  14. data/docs/_includes/head.html +135 -15
  15. data/docs/_includes/shared.html +164 -0
  16. data/lib/rest_framework/controller_mixins/base.rb +23 -36
  17. data/lib/rest_framework/controller_mixins/models.rb +86 -75
  18. data/lib/rest_framework/controller_mixins.rb +1 -0
  19. data/lib/rest_framework/engine.rb +9 -0
  20. data/lib/rest_framework/filters/base.rb +9 -0
  21. data/lib/rest_framework/filters/model_ordering.rb +48 -0
  22. data/lib/rest_framework/filters/model_query.rb +51 -0
  23. data/lib/rest_framework/filters/model_search.rb +41 -0
  24. data/lib/rest_framework/filters/ransack.rb +25 -0
  25. data/lib/rest_framework/filters.rb +6 -150
  26. data/lib/rest_framework/paginators.rb +7 -11
  27. data/lib/rest_framework/serializers.rb +10 -10
  28. data/lib/rest_framework/utils.rb +15 -7
  29. data/lib/rest_framework.rb +93 -4
  30. data/vendor/assets/javascripts/rest_framework/bootstrap.js +7 -0
  31. data/vendor/assets/javascripts/rest_framework/highlight-json.js +7 -0
  32. data/vendor/assets/javascripts/rest_framework/highlight-xml.js +29 -0
  33. data/vendor/assets/javascripts/rest_framework/highlight.js +1202 -0
  34. data/vendor/assets/javascripts/rest_framework/neatjson.js +8 -0
  35. data/vendor/assets/javascripts/rest_framework/trix.js +6 -0
  36. data/vendor/assets/stylesheets/rest_framework/bootstrap-icons.css +13 -0
  37. data/vendor/assets/stylesheets/rest_framework/bootstrap.css +6 -0
  38. data/vendor/assets/stylesheets/rest_framework/highlight-a11y-dark.css +7 -0
  39. data/vendor/assets/stylesheets/rest_framework/highlight-a11y-light.css +7 -0
  40. data/vendor/assets/stylesheets/rest_framework/trix.css +410 -0
  41. metadata +23 -5
  42. data/docs/_guide/2_controller_mixins.md +0 -293
  43. data/docs/assets/css/rest_framework.css +0 -159
@@ -1,293 +0,0 @@
1
- ---
2
- layout: default
3
- title: Controller Mixins
4
- position: 2
5
- slug: controller-mixins
6
- ---
7
- # Controller Mixins
8
-
9
- This is the core of the REST Framework. Generally speaking, projects already have an existing
10
- controller inheritance hierarchy, so we want developers to be able to maintain that project
11
- structure while leveraging the power of the REST Framework. Also, different controllers which
12
- inherit from the same parent often need different REST Framework mixins. For these reasons, REST
13
- Framework provides the controller functionality as modules that you mix into your controllers.
14
-
15
- ## Response Rendering
16
-
17
- Before we go into the various controller mixins, one of the core capabilities of the REST Framework
18
- is to provide system-consumable responses along side a browsable API for developers. While you can
19
- use Rails' builtin rendering tools, such as `render`, the REST Framework provides a rendering helper
20
- called `api_response`. This helper allows you to return a browsable API response for the `html`
21
- format which shows you what the JSON/XML response would look like, along with faster and lighter
22
- responses for `json` and `xml` formats.
23
-
24
- Here is an example:
25
-
26
- ```ruby
27
- class ApiController < ApplicationController
28
- include RESTFramework::BaseControllerMixin
29
- self.extra_actions = {test: :get}
30
-
31
- def test
32
- api_response({message: "Test successful!"})
33
- end
34
- end
35
- ```
36
-
37
- ## `BaseControllerMixin`
38
-
39
- To transform a controller into the simplest possible RESTful controller, you can include
40
- `BaseControllerMixin`, which provides a simple `root` action so it can be used at the API root.
41
-
42
- ```ruby
43
- class ApiController < ApplicationController
44
- include RESTFramework::BaseControllerMixin
45
- end
46
- ```
47
-
48
- ### Controller Attributes
49
-
50
- You can customize the behavior of `BaseControllerMixin` by setting or mutating various class
51
- attributes.
52
-
53
- #### `singleton_controller`
54
-
55
- This property primarily controls the routes that are generated for a RESTful controller. If you use
56
- `api_resource`/`api_resources` to define whether the generates routes are for a collection or for
57
- a single member, then you do not need to use this property. However, if you are autogenerating those
58
- routers, then `singleton_controller` will tell REST Framework whether to provide collection routes
59
- (when `singleton_controller` is falsy) or member routes (when `singleton_controller` is truthy). To
60
- read more about singular vs plural routing, see Rails' documentation here:
61
- https://guides.rubyonrails.org/routing.html#singular-resources.
62
-
63
- #### `extra_actions`
64
-
65
- This property defines extra actions on the controller to be routed. It is a hash of
66
- `endpoint -> method(s)` (where `method(s)` can be a method symbol or an array of method symbols).
67
-
68
- ```ruby
69
- class ApiController < ApplicationController
70
- include RESTFramework::BaseControllerMixin
71
- self.extra_actions = {test: :get}
72
-
73
- def test
74
- api_response({message: "Test successful!"})
75
- end
76
- end
77
- ```
78
-
79
- Or with multiple methods:
80
-
81
- ```ruby
82
- class ApiController < ApplicationController
83
- include RESTFramework::BaseControllerMixin
84
- self.extra_actions = {test: [:get, :post]}
85
-
86
- def test
87
- api_response({message: "Test successful!"})
88
- end
89
- end
90
- ```
91
-
92
- If your action conflicts with a builtin method, then you can also override the path:
93
-
94
- ```ruby
95
- class ApiController < ApplicationController
96
- include RESTFramework::BaseControllerMixin
97
-
98
- # This will route `test_action` to `/test`, in case there is already a `test` method that cannot
99
- # be overridden.
100
- self.extra_actions = {test_action: {path: :test, methods: :get}}
101
-
102
- def test_action
103
- api_response({message: "Test successful!"})
104
- end
105
- end
106
- ```
107
-
108
- ## `ModelControllerMixin`
109
-
110
- `ModelControllerMixin` assists with providing the standard model CRUD (create, read, update,
111
- destroy) for your controller. This is the most commonly used mixin since it provides default
112
- behavior for models which matches Rails' default routing.
113
-
114
- ```ruby
115
- class Api::MoviesController < ApiController
116
- include RESTFramework::ModelControllerMixin
117
- end
118
- ```
119
-
120
- By default, all columns and associations are included in `self.fields`, which can be helpful when
121
- developing an administrative API. For user-facing APIs, however, `self.fields` should always be
122
- explicitly defined.
123
-
124
- ### Controller Attributes
125
-
126
- You can customize the behavior of `ModelControllerMixin` by setting or mutating various class
127
- attributes.
128
-
129
- #### `model`
130
-
131
- The `model` property allows you to define the model if it is not obvious from the controller name.
132
-
133
- ```ruby
134
- class Api::CoolMoviesController < ApiController
135
- include RESTFramework::ModelControllerMixin
136
-
137
- self.model = Movie
138
- end
139
- ```
140
-
141
- #### `recordset`
142
-
143
- The `recordset` property allows you to define the set of records this API should be limited to. If
144
- you need to change the recordset based on properties of the request, then you can override the
145
- `get_recordset()` method.
146
-
147
- ```ruby
148
- class Api::CoolMoviesController < ApiController
149
- include RESTFramework::ModelControllerMixin
150
-
151
- self.recordset = Movie.where(cool: true).order({id: :asc})
152
- end
153
- ```
154
-
155
- #### `extra_member_actions`
156
-
157
- The `extra_member_actions` property allows you to define additional actions on individual records.
158
-
159
- ```ruby
160
- class Api::MoviesController < ApiController
161
- include RESTFramework::ModelControllerMixin
162
-
163
- self.extra_member_actions = {disable: :post}
164
-
165
- def disable
166
- @record = self.get_record # REST Framework will rescue ActiveRecord::RecordNotFound
167
-
168
- # REST Framework will rescue ActiveRecord::RecordInvalid or ActiveRecord::RecordNotSaved
169
- @record.update!(enabled: false)
170
-
171
- return api_response(@record)
172
- end
173
- end
174
- ```
175
-
176
- #### `fields`
177
-
178
- The `fields` property defines the default fields for serialization and for parameters allowed from
179
- the body or query string.
180
-
181
- ```ruby
182
- class Api::MoviesController < ApiController
183
- include RESTFramework::ModelControllerMixin
184
-
185
- self.fields = [:id, :name]
186
- end
187
- ```
188
-
189
- #### `action_fields`
190
-
191
- The `action_fields` property is similar to `fields`, but allows you to define different fields for
192
- different actions. A good example is to serialize expensive computed properties only in the `show`
193
- action, but not in the `list` action (where many records are serialized).
194
-
195
- ```ruby
196
- class Api::MoviesController < ApiController
197
- include RESTFramework::ModelControllerMixin
198
-
199
- self.fields = [:id, :name]
200
- self.action_fields = {
201
- show: [:id, :name, :some_expensive_computed_property],
202
- }
203
- end
204
- ```
205
-
206
- #### `native_serializer_config`
207
-
208
- These properties define the serializer configuration if you are using the native `ActiveModel`
209
- serializer. You can also specify serializers for singular/plural
210
-
211
- ```ruby
212
- class Api::MoviesController < ApiController
213
- include RESTFramework::ModelControllerMixin
214
-
215
- self.native_serializer_config = {
216
- only: [:id, :name],
217
- methods: [:active, :some_expensive_computed_property],
218
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
219
- }
220
-
221
- # Or you could configure a default and a plural serializer:
222
- self.native_serializer_plural_config = {
223
- only: [:id, :name],
224
- methods: [:active],
225
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
226
- }
227
- self.native_serializer_config = {
228
- only: [:id, :name],
229
- methods: [:active, :some_expensive_computed_property],
230
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
231
- }
232
-
233
- # Or you could configure a default and a singular serializer:
234
- self.native_serializer_config = {
235
- only: [:id, :name],
236
- methods: [:active],
237
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
238
- }
239
- self.native_serializer_singular_config = {
240
- only: [:id, :name],
241
- methods: [:active, :some_expensive_computed_property],
242
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
243
- }
244
- end
245
- ```
246
-
247
- #### `allowed_parameters` / `allowed_action_parameters`
248
-
249
- These properties define the permitted parameters to be used in the request body for create/update
250
- actions. If you need different allowed parameters, then you can also override the
251
- `get_create_params` or `get_update_params` methods.
252
-
253
- ```ruby
254
- class Api::MoviesController < ApiController
255
- include RESTFramework::ModelControllerMixin
256
-
257
- self.allowed_parameters = [:name]
258
- end
259
- ```
260
-
261
- #### `create_from_recordset` (default: `true`)
262
-
263
- The `create_from_recordset` attribute (`true` by default) is a boolean to control the behavior in
264
- the `create` action. If it is disabled, records will not be created from the filtered recordset, but
265
- rather will be created directly from the model interface.
266
-
267
- For example, if this is your controller:
268
-
269
- ```ruby
270
- class Api::CoolMoviesController < ApiController
271
- include RESTFramework::ModelControllerMixin
272
-
273
- def get_recordset
274
- return Movie.where(cool: true)
275
- end
276
- end
277
- ```
278
-
279
- Then if you hit the `create` action with the payload `{name: "Superman"}`, it will also set `cool`
280
- to `true` on the new record, because that property is inherited from the recordset.
281
-
282
- ## `ReadOnlyModelControllerMixin`
283
-
284
- `ReadOnlyModelControllerMixin` only enables list/show actions. In this example, since we're naming
285
- this controller in a way that doesn't make the model obvious, we can set that explicitly:
286
-
287
- ```ruby
288
- class Api::ReadOnlyMoviesController < ApiController
289
- include RESTFramework::ReadOnlyModelControllerMixin
290
-
291
- self.model = Movie
292
- end
293
- ```
@@ -1,159 +0,0 @@
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
- }