rest_framework 0.9.5 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +130 -0
  4. data/VERSION +1 -1
  5. data/app/views/layouts/rest_framework.html.erb +9 -183
  6. data/app/views/rest_framework/_breadcrumbs.html.erb +27 -0
  7. data/app/views/rest_framework/_head.html.erb +347 -190
  8. data/app/views/rest_framework/_header.html.erb +15 -0
  9. data/app/views/rest_framework/_heading.html.erb +10 -0
  10. data/app/views/rest_framework/_payloads.html.erb +36 -0
  11. data/app/views/rest_framework/_request_metadata.html.erb +16 -0
  12. data/app/views/rest_framework/_routes_and_forms.html.erb +52 -0
  13. data/app/views/rest_framework/head/_external.html.erb +7 -2
  14. data/app/views/rest_framework/header/_mode.html.erb +14 -0
  15. data/app/views/rest_framework/heading/_actions.html.erb +9 -0
  16. data/app/views/rest_framework/{_routes.html.erb → routes_and_forms/_routes.html.erb} +2 -2
  17. data/lib/rest_framework/controller_mixins/base.rb +11 -12
  18. data/lib/rest_framework/engine.rb +5 -3
  19. data/lib/rest_framework/filters/ransack.rb +6 -6
  20. data/lib/rest_framework/version.rb +3 -8
  21. data/lib/rest_framework.rb +11 -2
  22. data/vendor/assets/javascripts/rest_framework/external.min.js +1256 -0
  23. data/vendor/assets/stylesheets/rest_framework/{bootstrap-icons.min.css → external.min.css} +415 -0
  24. data/vendor/assets/stylesheets/rest_framework/highlight-a11y-dark.min.css +1 -1
  25. data/vendor/assets/stylesheets/rest_framework/highlight-a11y-light.min.css +1 -1
  26. metadata +16 -33
  27. data/README.md +0 -1
  28. data/app/views/rest_framework/head/_shared.html +0 -164
  29. data/docs/CNAME +0 -1
  30. data/docs/Gemfile +0 -5
  31. data/docs/Gemfile.lock +0 -264
  32. data/docs/_config.yml +0 -19
  33. data/docs/_guide/1_routers.md +0 -110
  34. data/docs/_guide/2_controllers.md +0 -342
  35. data/docs/_guide/3_serializers.md +0 -60
  36. data/docs/_guide/4_filtering_and_ordering.md +0 -41
  37. data/docs/_guide/5_pagination.md +0 -21
  38. data/docs/_includes/anchor_headings.html +0 -144
  39. data/docs/_includes/external.html +0 -9
  40. data/docs/_includes/head.html +0 -155
  41. data/docs/_includes/header.html +0 -58
  42. data/docs/_includes/shared.html +0 -164
  43. data/docs/_layouts/default.html +0 -11
  44. data/docs/assets/images/favicon.ico +0 -0
  45. data/docs/index.md +0 -133
  46. data/vendor/assets/javascripts/rest_framework/bootstrap.min.js +0 -6
  47. data/vendor/assets/javascripts/rest_framework/highlight-json.min.js +0 -7
  48. data/vendor/assets/javascripts/rest_framework/highlight-xml.min.js +0 -29
  49. data/vendor/assets/javascripts/rest_framework/highlight.min.js +0 -1202
  50. data/vendor/assets/javascripts/rest_framework/neatjson.min.js +0 -7
  51. data/vendor/assets/javascripts/rest_framework/trix.min.js +0 -6
  52. data/vendor/assets/stylesheets/rest_framework/bootstrap.min.css +0 -5
  53. data/vendor/assets/stylesheets/rest_framework/trix.min.css +0 -410
  54. /data/app/views/rest_framework/{_html_form.html.erb → routes_and_forms/_html_form.html.erb} +0 -0
  55. /data/app/views/rest_framework/{_raw_form.html.erb → routes_and_forms/_raw_form.html.erb} +0 -0
  56. /data/app/views/rest_framework/{_route.html.erb → routes_and_forms/routes/_route.html.erb} +0 -0
@@ -1,342 +0,0 @@
1
- ---
2
- layout: default
3
- title: Controllers
4
- position: 2
5
- slug: controllers
6
- ---
7
- # Controllers
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 functionality and behavior. For
13
- these reasons, REST Framework provides the controller functionality as modules that you mix into
14
- your controllers.
15
-
16
- Here is a graph of the controller mixins (all exist within the `RESTFramework` namespace), with
17
- supplementary mixins shown as well:
18
-
19
- ```shell
20
- BaseControllerMixin
21
- BaseModelControllerMixin (includes BaseControllerMixin)
22
- ModelControllerMixin (includes BaseModelControllerMixin)
23
- ├── ListModelMixin
24
- ├── ShowModelMixin
25
- ├── CreateModelMixin
26
- ├── UpdateModelMixin
27
- └── DestroyModelMixin
28
- ReadOnlyModelControllerMixin (includes BaseModelControllerMixin)
29
- ├── ListModelMixin
30
- └── ShowModelMixin
31
- BulkModelControllerMixin (includes ModelControllerMixin)
32
- ├── BulkCreateModelMixin
33
- ├── BulkUpdateModelMixin
34
- └── BulkDestroyModelMixin
35
- ```
36
-
37
- All API controllers should include at least one of these top-level controller mixins, and can
38
- include any of the supplementary mixins to add additional functionality. For example, if you want to
39
- permit create but not update or destroy, then you could do this:
40
-
41
- ```ruby
42
- class Api::MoviesController < ApiController
43
- include RESTFramework::BaseModelControllerMixin
44
- include RESTFramework::CreateModelMixin
45
- end
46
- ```
47
-
48
- ## BaseControllerMixin
49
-
50
- To transform a controller into the simplest possible RESTful controller, you can include
51
- `BaseControllerMixin`, which provides a simple `root` action so it can be used at the API root.
52
-
53
- ```ruby
54
- class ApiController < ApplicationController
55
- include RESTFramework::BaseControllerMixin
56
- end
57
- ```
58
-
59
- ### Response Rendering
60
-
61
- A fundamental feature that REST Framework provides is the ability to render a browsable API. This
62
- allows developers to discover and interact with the API's functionality, while also providing faster
63
- and more lightweight JSON/XML formats for consumption by the systems these developers create.
64
-
65
- The `api_response` method is how this is accomplished. The first argument is the data to be
66
- rendered (often an array or hash), and keyword arguments can be provided to customize the response
67
- (e.g., setting the HTTP status code). Using this method instead of the classic Rails helpers, such
68
- as `render`, will automatically provide the browsable API as well as JSON/XML rendering.
69
-
70
- Here is a simple example of rendering a hash with a `message` key:
71
-
72
- ```ruby
73
- class ApiController < ApplicationController
74
- include RESTFramework::BaseControllerMixin
75
- self.extra_actions = {test: :get}
76
-
77
- def test
78
- api_response({message: "Test successful!"})
79
- end
80
- end
81
- ```
82
-
83
- ### Routing Extra Actions
84
-
85
- The `extra_actions` property defines extra actions on the controller to be routed. It is a hash of
86
- `endpoint -> method(s)` (where `method(s)` can be a method symbol or an array of method symbols).
87
-
88
- ```ruby
89
- class ApiController < ApplicationController
90
- include RESTFramework::BaseControllerMixin
91
- self.extra_actions = {test: :get}
92
-
93
- def test
94
- api_response({message: "Test successful!"})
95
- end
96
- end
97
- ```
98
-
99
- Or with multiple methods:
100
-
101
- ```ruby
102
- class ApiController < ApplicationController
103
- include RESTFramework::BaseControllerMixin
104
- self.extra_actions = {test: [:get, :post]}
105
-
106
- def test
107
- api_response({message: "Test successful!"})
108
- end
109
- end
110
- ```
111
-
112
- If your action conflicts with a builtin method, then you can also override the path:
113
-
114
- ```ruby
115
- class ApiController < ApplicationController
116
- include RESTFramework::BaseControllerMixin
117
-
118
- # This will route `test_action` to `/test`, in case there is already a `test` method that cannot
119
- # be overridden.
120
- self.extra_actions = {test_action: {path: :test, methods: :get}}
121
-
122
- def test_action
123
- api_response({message: "Test successful!"})
124
- end
125
- end
126
- ```
127
-
128
- ## ModelControllerMixin
129
-
130
- `ModelControllerMixin` assists with providing the standard model CRUD (create, read, update,
131
- destroy) for your controller. This is the most commonly used mixin since it provides default
132
- behavior for models which matches Rails' resourceful routing.
133
-
134
- ```ruby
135
- class Api::MoviesController < ApiController
136
- include RESTFramework::ModelControllerMixin
137
- end
138
- ```
139
-
140
- By default, all columns and associations are included in the controller's `fields`, which can be
141
- helpful when developing an administrative API. For most APIs, however, `fields` should be explicitly
142
- defined. See [Specifying the Fields](#specifying-the-fields) for more information.
143
-
144
- ### Defining the Model
145
-
146
- The `model` property allows you to define the model if it is not derivable from the controller name.
147
-
148
- ```ruby
149
- class Api::CoolMoviesController < ApiController
150
- include RESTFramework::ModelControllerMixin
151
-
152
- self.model = Movie
153
- end
154
- ```
155
-
156
- ### Specifying the Recordset
157
-
158
- The `recordset` property allows you to define the set of records this API should be limited to. If
159
- you need to change the recordset based on properties of the request, then you can override the
160
- `get_recordset` method.
161
-
162
- ```ruby
163
- class Api::CoolMoviesController < ApiController
164
- include RESTFramework::ModelControllerMixin
165
-
166
- self.recordset = Movie.where(cool: true).order({id: :asc})
167
- end
168
- ```
169
-
170
- If you override `get_recordset`, you should ensure you also set the `model` property if it's not
171
- derivable from the controller name.
172
-
173
- ```ruby
174
- class Api::CoolMoviesController < ApiController
175
- include RESTFramework::ModelControllerMixin
176
-
177
- self.model = Movie
178
-
179
- def get_recordset
180
- return Movie.where(cool: true).order({id: :asc})
181
- end
182
- end
183
- ```
184
-
185
- ### Specifying Extra Member Actions
186
-
187
- While `extra_actions` (and the synonym `extra_collection_actions`) allows you to define additional
188
- actions on the controller, `extra_member_actions` allows you to define additional member actions on
189
- the controller, which require a record ID to be provided as a path param.
190
-
191
- ```ruby
192
- class Api::MoviesController < ApiController
193
- include RESTFramework::ModelControllerMixin
194
-
195
- self.extra_member_actions = {disable: :patch}
196
-
197
- def disable
198
- # REST Framework will rescue ActiveRecord::RecordNotFound.
199
- @record = self.get_record
200
-
201
- # REST Framework will rescue ActiveRecord::RecordInvalid or ActiveRecord::RecordNotSaved.
202
- @record.update!(enabled: false)
203
-
204
- return api_response(@record)
205
- end
206
- end
207
- ```
208
-
209
- ### Specifying the Fields
210
-
211
- The `fields` property defines the fields available for things like serialization and allowed
212
- parameters in body or query params. If `fields` is not set, then it will default to all columns and
213
- associations, which is helpful when developing an administrative API. For most APIs, however,
214
- `fields` should be explicitly defined.
215
-
216
- While you can also define per-request fields by overriding `get_fields`, you should also define a
217
- set of fields on the controller which is used for things like the `OPTIONS` metadata.
218
-
219
- ```ruby
220
- class Api::MoviesController < ApiController
221
- include RESTFramework::ModelControllerMixin
222
-
223
- self.fields = [:id, :name]
224
- end
225
- ```
226
-
227
- You can also mutate the default set of fields by removing existing columns/associations, and even
228
- adding methods. The framework will automatically detect if the given field is a column, association,
229
- or method, and will handle it appropriately.
230
-
231
- ```ruby
232
- class Api::MoviesController < ApiController
233
- include RESTFramework::ModelControllerMixin
234
-
235
- # This will include all columns, all associations except `owners`, and the `is_featured` method.
236
- self.fields = {
237
- exclude: [:owners],
238
- include: [:is_featured],
239
- }
240
- end
241
- ```
242
-
243
- ### Specifying the Serializer Configuration
244
-
245
- For most cases, the default serializer configuration is sufficient, and can be modified by adjusting
246
- the `fields` property on the controller. However, there are cases where you may want to define a
247
- serializer configuation, such as when you want to customize nested associations, or if you want to
248
- remove certain fields (like methods) when serializing multiple records.
249
-
250
- The property `native_serializer_config` defines the serializer configuration if you are using the
251
- default serializer. You can also specify serializers for singular/plural data.
252
-
253
- ```ruby
254
- class Api::MoviesController < ApiController
255
- include RESTFramework::ModelControllerMixin
256
-
257
- self.native_serializer_config = {
258
- only: [:id, :name],
259
- methods: [:active, :some_expensive_computed_property],
260
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
261
- }
262
-
263
- # Or you could configure a default and a plural serializer:
264
- self.native_serializer_config = {
265
- only: [:id, :name],
266
- methods: [:active, :some_expensive_computed_property],
267
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
268
- }
269
- self.native_serializer_plural_config = {
270
- only: [:id, :name],
271
- methods: [:active],
272
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
273
- }
274
-
275
- # Or you could configure a default and a singular serializer:
276
- self.native_serializer_config = {
277
- only: [:id, :name],
278
- methods: [:active],
279
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
280
- }
281
- self.native_serializer_singular_config = {
282
- only: [:id, :name],
283
- methods: [:active, :some_expensive_computed_property],
284
- include: {cast_members: { only: [:id, :name], methods: [:net_worth] }},
285
- }
286
- end
287
- ```
288
-
289
- ### Specifying Allowed Parameters
290
-
291
- The `allowed_parameters` property defines the allowed parameters for create/update actions. The
292
- framework uses strong parameters to execute this filtering.
293
-
294
- ```ruby
295
- class Api::MoviesController < ApiController
296
- include RESTFramework::ModelControllerMixin
297
-
298
- self.allowed_parameters = [:name]
299
- end
300
- ```
301
-
302
- If you want different allowed parameters for create/update actions, or if you need stronger control
303
- over what request body parameters get passed to create/update, then you can also override the
304
- `get_create_params` or `get_update_params` methods.
305
-
306
- ### Controlling Create Behavior
307
-
308
- The `create_from_recordset` attribute (`true` by default) is a boolean to control the behavior in
309
- the `create` action. If it is disabled, records will not be created from the filtered recordset, but
310
- rather will be created directly from the model interface.
311
-
312
- For example, if this is your controller:
313
-
314
- ```ruby
315
- class Api::CoolMoviesController < ApiController
316
- include RESTFramework::ModelControllerMixin
317
-
318
- # Notice that `cool` is read-only with the following two configurations:
319
- self.fields = [:id, :name]
320
- self.native_serializer_config = {only: [:id, :name, :cool]}
321
-
322
- # New records created from this controller will have `cool` set to `true`.
323
- def get_recordset
324
- return Movie.where(cool: true)
325
- end
326
- end
327
- ```
328
-
329
- Then if you hit the `create` action with the payload `{name: "Superman"}`, it will also set `cool`
330
- to `true` on the new record, because that property is inherited from the recordset.
331
-
332
- ## ReadOnlyModelControllerMixin
333
-
334
- `ReadOnlyModelControllerMixin` only enables list/show actions.
335
-
336
- ```ruby
337
- class Api::ReadOnlyMoviesController < ApiController
338
- include RESTFramework::ReadOnlyModelControllerMixin
339
-
340
- self.model = Movie
341
- end
342
- ```
@@ -1,60 +0,0 @@
1
- ---
2
- layout: default
3
- title: Serializers
4
- position: 3
5
- slug: serializers
6
- ---
7
- # Serializers
8
-
9
- Serializers allow complex objects to be converted to Ruby primitives (`Array` and `Hash` objects),
10
- which can then be converted to JSON or XML.
11
-
12
- ## NativeSerializer
13
-
14
- This serializer uses Rails' native `ActiveModel::Serialization.serializable_hash` method to convert
15
- records/recordsets to Ruby primitives (`Array` and `Hash`).
16
-
17
- This is the default serializer, you can configure it using the controller class attributes
18
- `native_serializer_config` (or `native_serializer_singular_config` /
19
- `native_serializer_plural_config`):
20
-
21
- ```ruby
22
- class Api::MoviesController < ApiController
23
- include RESTFramework::ModelControllerMixin
24
-
25
- self.native_serializer_config = {
26
- only: [:id, :name],
27
- methods: [:active, :some_expensive_computed_property],
28
- include: {cast_members: { only: [:id, :name] }},
29
- }
30
- end
31
- ```
32
-
33
- If you want to re-use a serializer, then you can define it as a standalone class, and you can even
34
- nest them. You can also define separate configurations for serializing individual records vs
35
- recordsets using `singular_config` and `plural_config`, respectively.
36
-
37
- ```ruby
38
- class Api::MoviesController < ApiController
39
- include RESTFramework::ModelControllerMixin
40
-
41
- class CastMemberSerializer < RESTFramework::NativeSerializer
42
- self.config = { only: [:id, :name], methods: [:net_worth] }
43
- self.plural_config = { only: [:id, :name] }
44
- end
45
-
46
- class MovieSerializer < RESTFramework::NativeSerializer
47
- self.config = {
48
- only: [:id, :name],
49
- include: {cast_members: CastMemberSerializer.new(many: true)},
50
- }
51
- self.singular_config = {
52
- only: [:id, :name],
53
- methods: [:active, :some_expensive_computed_property],
54
- include: {cast_members: CastMemberSerializer.new(many: true)},
55
- }
56
- end
57
-
58
- self.serializer_class = MovieSerializer
59
- end
60
- ```
@@ -1,41 +0,0 @@
1
- ---
2
- layout: default
3
- title: Filtering / Ordering
4
- position: 4
5
- slug: filtering-and-ordering
6
- ---
7
- # Filtering and Ordering
8
-
9
- While you can control the recordset that the API exposes, sometimes you want the user to control the
10
- records they want to see, or the order of those records. Both filtering and ordering are
11
- accomplished through a generic mechanism called "filters". To control the filter backends that a
12
- controller uses, you can either adjust the `filter_backends` controller attribute or you can
13
- override the `get_filter_backends()` method.
14
-
15
- ## ModelQueryFilter
16
-
17
- This filter provides basic user-controllable filtering of the recordset using query params. For
18
- example, a request to `/api/movies?cool=true` would return movies where `cool` is `true`.
19
-
20
- If you include `ModelControllerMixin` into your controller, `ModelQueryFilter` is included in the filter
21
- backends by default.
22
-
23
- ## ModelOrderingFilter
24
-
25
- This filter provides basic user-controllable ordering of the recordset using query params. For
26
- example, a request to `/api/movies?ordering=name` could order the movies by `name` rather than `id`.
27
- `ordering=-name` would invert the ordering. You can also order with multiple parameters with a comma
28
- separated list, like: `ordering=director,-name`.
29
-
30
- If you include `ModelControllerMixin` into your controller, `ModelOrderingFilter` is included in the
31
- filter backends by default. You can use `ordering_fields` to controller which fields are allowed to
32
- be ordered by. To adjust the parameter that the user passes, adjust `ordering_query_param`; the
33
- default is `"ordering"`.
34
-
35
- ## ModelSearchFilter
36
-
37
- This filter provides basic user-controllable searching of the recordset using the `search` query
38
- parameter (adjustable with the `search_query_param`). For example, a request to
39
- `/api/movies?search=Star` could return movies where `name` contains the string `Star`. The search is
40
- performed against the `search_fields` attribute, but if that is not set, then the search is
41
- performed against a configurable default set of fields (`search_columns`).
@@ -1,21 +0,0 @@
1
- ---
2
- layout: default
3
- title: Pagination
4
- position: 5
5
- slug: pagination
6
- ---
7
- # Pagination
8
-
9
- For large result sets, you may need to provide pagination. You can configure the paginator for a
10
- controller by setting the `paginator_class` to the paginator you want to use.
11
-
12
- ## PageNumberPaginator
13
-
14
- This is a simple paginator which splits a recordset into pages and allows the user to select the
15
- desired page using the `page` query parameter (e.g., `/api/movies?page=3`). To adjust this query
16
- parameter, set the `page_query_param` controller attribute.
17
-
18
- By default the user can adjust the page size using the `page_size` query param. To adjust this query
19
- parameter, you can set the `page_size_query_param` controller attribute, or set it to `nil` to
20
- disable this functionality. By default, there is no upper limit to the size of a page a user can
21
- request. To enforce an upper limit, set the `max_page_size` controller attribute.
@@ -1,144 +0,0 @@
1
- {% capture headingsWorkspace %}
2
- {% comment %}
3
- Copyright (c) 2018 Vladimir "allejo" Jimenez
4
-
5
- Permission is hereby granted, free of charge, to any person
6
- obtaining a copy of this software and associated documentation
7
- files (the "Software"), to deal in the Software without
8
- restriction, including without limitation the rights to use,
9
- copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the
11
- Software is furnished to do so, subject to the following
12
- conditions:
13
-
14
- The above copyright notice and this permission notice shall be
15
- included in all copies or substantial portions of the Software.
16
-
17
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
- OTHER DEALINGS IN THE SOFTWARE.
25
- {% endcomment %}
26
- {% comment %}
27
- Version 1.0.7
28
- https://github.com/allejo/jekyll-anchor-headings
29
-
30
- "Be the pull request you wish to see in the world." ~Ben Balter
31
-
32
- Usage:
33
- {% include anchor_headings.html html=content anchorBody="#" %}
34
-
35
- Parameters:
36
- * html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
37
-
38
- Optional Parameters:
39
- * beforeHeading (bool) : false - Set to true if the anchor should be placed _before_ the heading's content
40
- * anchorAttrs (string) : '' - Any custom HTML attributes that will be added to the `<a>` tag; you may NOT use `href`, `class` or `title`;
41
- the `%heading%` and `%html_id%` placeholders are available
42
- * anchorBody (string) : '' - The content that will be placed inside the anchor; the `%heading%` placeholder is available
43
- * anchorClass (string) : '' - The class(es) that will be used for each anchor. Separate multiple classes with a space
44
- * anchorTitle (string) : '' - The `title` attribute that will be used for anchors
45
- * h_min (int) : 1 - The minimum header level to build an anchor for; any header lower than this value will be ignored
46
- * h_max (int) : 6 - The maximum header level to build an anchor for; any header greater than this value will be ignored
47
- * bodyPrefix (string) : '' - Anything that should be inserted inside of the heading tag _before_ its anchor and content
48
- * bodySuffix (string) : '' - Anything that should be inserted inside of the heading tag _after_ its anchor and content
49
-
50
- Output:
51
- The original HTML with the addition of anchors inside of all of the h1-h6 headings.
52
- {% endcomment %}
53
-
54
- {% assign minHeader = include.h_min | default: 1 %}
55
- {% assign maxHeader = include.h_max | default: 6 %}
56
- {% assign beforeHeading = include.beforeHeading %}
57
- {% assign nodes = include.html | split: '<h' %}
58
-
59
- {% capture edited_headings %}{% endcapture %}
60
-
61
- {% for _node in nodes %}
62
- {% capture node %}{{ _node | strip }}{% endcapture %}
63
-
64
- {% if node == "" %}
65
- {% continue %}
66
- {% endif %}
67
-
68
- {% assign nextChar = node | replace: '"', '' | strip | slice: 0, 1 %}
69
- {% assign headerLevel = nextChar | times: 1 %}
70
-
71
- <!-- If the level is cast to 0, it means it's not a h1-h6 tag, so let's see if we need to fix it -->
72
- {% if headerLevel == 0 %}
73
- <!-- Split up the node based on closing angle brackets and get the first one. -->
74
- {% assign firstChunk = node | split: '>' | first %}
75
-
76
- <!-- If the first chunk does NOT contain a '<', that means we've broken another HTML tag that starts with 'h' -->
77
- {% unless firstChunk contains '<' %}
78
- {% capture node %}<h{{ node }}{% endcapture %}
79
- {% endunless %}
80
-
81
- {% capture edited_headings %}{{ edited_headings }}{{ node }}{% endcapture %}
82
- {% continue %}
83
- {% endif %}
84
-
85
- {% capture _closingTag %}</h{{ headerLevel }}>{% endcapture %}
86
- {% assign _workspace = node | split: _closingTag %}
87
- {% assign _idWorkspace = _workspace[0] | split: 'id="' %}
88
- {% assign _idWorkspace = _idWorkspace[1] | split: '"' %}
89
- {% assign html_id = _idWorkspace[0] %}
90
-
91
- {% capture _hAttrToStrip %}{{ _workspace[0] | split: '>' | first }}>{% endcapture %}
92
- {% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
93
-
94
- <!-- Build the anchor to inject for our heading -->
95
- {% capture anchor %}{% endcapture %}
96
-
97
- {% if html_id and headerLevel >= minHeader and headerLevel <= maxHeader %}
98
- {% capture anchor %}href="#{{ html_id }}"{% endcapture %}
99
-
100
- {% if include.anchorClass %}
101
- {% capture anchor %}{{ anchor }} class="{{ include.anchorClass }}"{% endcapture %}
102
- {% endif %}
103
-
104
- {% if include.anchorTitle %}
105
- {% capture anchor %}{{ anchor }} title="{{ include.anchorTitle | replace: '%heading%', header }}"{% endcapture %}
106
- {% endif %}
107
-
108
- {% if include.anchorAttrs %}
109
- {% capture anchor %}{{ anchor }} {{ include.anchorAttrs | replace: '%heading%', header | replace: '%html_id%', html_id }}{% endcapture %}
110
- {% endif %}
111
-
112
- {% capture anchor %}<a {{ anchor }}>{{ include.anchorBody | replace: '%heading%', header | default: '' }}</a>{% endcapture %}
113
-
114
- <!-- In order to prevent adding extra space after a heading, we'll let the 'anchor' value contain it -->
115
- {% if beforeHeading %}
116
- {% capture anchor %}{{ anchor }} {% endcapture %}
117
- {% else %}
118
- {% capture anchor %} {{ anchor }}{% endcapture %}
119
- {% endif %}
120
- {% endif %}
121
-
122
- {% capture new_heading %}
123
- <h{{ _hAttrToStrip }}
124
- {{ include.bodyPrefix }}
125
- {% if beforeHeading %}
126
- {{ anchor }}{{ header }}
127
- {% else %}
128
- {{ header }}{{ anchor }}
129
- {% endif %}
130
- {{ include.bodySuffix }}
131
- </h{{ headerLevel }}>
132
- {% endcapture %}
133
-
134
- <!--
135
- If we have content after the `</hX>` tag, then we'll want to append that here so we don't lost any content.
136
- -->
137
- {% assign chunkCount = _workspace | size %}
138
- {% if chunkCount > 1 %}
139
- {% capture new_heading %}{{ new_heading }}{{ _workspace | last }}{% endcapture %}
140
- {% endif %}
141
-
142
- {% capture edited_headings %}{{ edited_headings }}{{ new_heading }}{% endcapture %}
143
- {% endfor %}
144
- {% endcapture %}{% assign headingsWorkspace = '' %}{{ edited_headings | strip }}
@@ -1,9 +0,0 @@
1
- <!-- AUTOGENERATED -->
2
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/css/bootstrap.min.css" integrity="sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp" crossorigin="anonymous">
3
- <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" referrerpolicy="no-referrer" defer="defer"></script>
4
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.min.css" crossorigin="anonymous">
5
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js" integrity="sha512-bgHRAiTjGrzHzLyKOnpFvaEpGzJet3z4tZnXGjpsCcqOnAH6VGUx9frc5bcIhKTVLEiCO6vEhNAgx5jtLUYrfA==" crossorigin="anonymous" referrerpolicy="no-referrer" defer="defer"></script>
6
- <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" defer="defer"></script>
7
- <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" defer="defer"></script>
8
- <link rel="stylesheet" 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" class="rrf-dark-mode">
9
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/a11y-light.min.css" integrity="sha512-WDk6RzwygsN9KecRHAfm9HTN87LQjqdygDmkHSJxVkVI7ErCZ8ZWxP6T8RvBujY1n2/E4Ac+bn2ChXnp5rnnHA==" crossorigin="anonymous" class="rrf-light-mode">