rest_framework 0.9.14 → 0.9.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/app/views/layouts/rest_framework.html.erb +1 -2
- data/app/views/rest_framework/_head.html.erb +3 -2
- data/app/views/rest_framework/_heading.html.erb +1 -1
- data/app/views/rest_framework/routes_and_forms/_html_form.html.erb +3 -3
- data/app/views/rest_framework/routes_and_forms/_raw_form.html.erb +1 -1
- data/lib/rest_framework/filters/base_filter.rb +1 -1
- data/lib/rest_framework/filters/{model_ordering_filter.rb → ordering_filter.rb} +6 -3
- data/lib/rest_framework/filters/{model_query_filter.rb → query_filter.rb} +10 -7
- data/lib/rest_framework/filters/ransack_filter.rb +1 -1
- data/lib/rest_framework/filters/{model_search_filter.rb → search_filter.rb} +6 -4
- data/lib/rest_framework/filters.rb +3 -3
- data/lib/rest_framework/mixins/base_controller_mixin.rb +39 -63
- data/lib/rest_framework/mixins/bulk_model_controller_mixin.rb +1 -1
- data/lib/rest_framework/mixins/model_controller_mixin.rb +103 -69
- data/lib/rest_framework/serializers/native_serializer.rb +7 -3
- data/lib/rest_framework/utils.rb +13 -3
- data/lib/rest_framework.rb +16 -16
- data/vendor/assets/javascripts/rest_framework/external.min.js +618 -575
- data/vendor/assets/stylesheets/rest_framework/external.min.css +12 -19
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59084182eeedcec7d3f1a1a913a99e55a6139bb394e4c712744c5d80778db800
|
4
|
+
data.tar.gz: 552ea8a39833dc6a3ec4c689f120ba549af0db49a3fafa29d63cd4cb17d85a85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cffd1cde2cbf956d46344018c137b723397118f3aa4ea16c2d9abf5c9d9694c8a78d8f706f96e28c637ec4f58d692553da372a832a0bd44cc58154134b733d33
|
7
|
+
data.tar.gz: 1eea97e45fc0f4c47edbdb04a8de05fe6e383e259ea1d3ff48196b6601aeb87341aa6750f8eb4411dd0e16934452c5a02b2d26877a9ec8382ae538312f305603
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.16
|
@@ -3,11 +3,10 @@
|
|
3
3
|
<head>
|
4
4
|
<title><%= @title %></title>
|
5
5
|
|
6
|
-
<%# These dynamic tags cannot be cached, so include directly instead of in `head` partial. %>
|
7
6
|
<%= csrf_meta_tags %>
|
8
7
|
<%= csp_meta_tag rescue nil %>
|
9
8
|
|
10
|
-
<%= render partial: "rest_framework/head"
|
9
|
+
<%= render partial: "rest_framework/head" %>
|
11
10
|
|
12
11
|
<%= yield :head %>
|
13
12
|
</head>
|
@@ -296,8 +296,9 @@
|
|
296
296
|
document.write(content)
|
297
297
|
document.close()
|
298
298
|
|
299
|
-
//
|
300
|
-
|
299
|
+
// It seems that `DOMContentLoaded` is already triggered on `document.close()`.
|
300
|
+
// // Trigger `DOMContentLoaded` manually so our custom JavaScript works.
|
301
|
+
// // document.dispatchEvent(new Event("DOMContentLoaded", {bubbles: true, cancelable: true}))
|
301
302
|
}
|
302
303
|
|
303
304
|
// Refresh the window as a `GET` request.
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<%= render partial: "rest_framework/heading/actions" if @route_groups.present? %>
|
4
4
|
<h1 style="margin: 0"><%= @heading_title || @title %></h1>
|
5
5
|
<% if @description.present? %>
|
6
|
-
<
|
6
|
+
<p style="display: inline-block; margin-bottom: 0; margin-top: 1em"><%= @description %></p>
|
7
7
|
<% end %>
|
8
8
|
</div>
|
9
9
|
</div>
|
@@ -26,14 +26,14 @@
|
|
26
26
|
%>
|
27
27
|
<div class="mb-2">
|
28
28
|
<% if metadata[:kind] == "rich_text" %>
|
29
|
-
<label class="form-label w-100"><%= controller.class.
|
29
|
+
<label class="form-label w-100"><%= controller.class.label_for(f) %></label>
|
30
30
|
<%= form.rich_text_area f %>
|
31
31
|
<% elsif metadata[:kind] == "attachment" %>
|
32
|
-
<label class="form-label w-100"><%= controller.class.
|
32
|
+
<label class="form-label w-100"><%= controller.class.label_for(f) %>
|
33
33
|
<%= form.file_field f, multiple: metadata.dig(:attachment, :macro) == :has_many_attached %>
|
34
34
|
</label>
|
35
35
|
<% else %>
|
36
|
-
<label class="form-label w-100"><%= controller.class.
|
36
|
+
<label class="form-label w-100"><%= controller.class.label_for(f) %>
|
37
37
|
<%= form.text_field f, class: "form-control form-control-sm" %>
|
38
38
|
</label>
|
39
39
|
<% end %>
|
@@ -40,7 +40,7 @@
|
|
40
40
|
local: true,
|
41
41
|
}.compact) do |form| %>
|
42
42
|
<% attachment_reflections.each do |field, ref| %>
|
43
|
-
<label class="form-label w-100"><%= controller.class.
|
43
|
+
<label class="form-label w-100"><%= controller.class.label_for(field) %>
|
44
44
|
<%= form.file_field field, multiple: ref.macro == :has_many_attached %>
|
45
45
|
</label>
|
46
46
|
<% end %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# A filter backend which handles ordering of the recordset.
|
2
|
-
class RESTFramework::Filters::
|
2
|
+
class RESTFramework::Filters::OrderingFilter < RESTFramework::Filters::BaseFilter
|
3
3
|
# Get a list of ordering fields for the current action.
|
4
4
|
def _get_fields
|
5
5
|
return @controller.ordering_fields&.map(&:to_s) || @controller.get_fields
|
@@ -38,7 +38,7 @@ class RESTFramework::Filters::ModelOrderingFilter < RESTFramework::Filters::Base
|
|
38
38
|
end
|
39
39
|
|
40
40
|
# Order data according to the request query parameters.
|
41
|
-
def
|
41
|
+
def filter_data(data)
|
42
42
|
ordering = self._get_ordering
|
43
43
|
reorder = !@controller.ordering_no_reorder
|
44
44
|
|
@@ -51,4 +51,7 @@ class RESTFramework::Filters::ModelOrderingFilter < RESTFramework::Filters::Base
|
|
51
51
|
end
|
52
52
|
|
53
53
|
# Alias for convenience.
|
54
|
-
RESTFramework::
|
54
|
+
RESTFramework::OrderingFilter = RESTFramework::Filters::OrderingFilter
|
55
|
+
|
56
|
+
# TODO: Compatibility; remove in 1.0.
|
57
|
+
RESTFramework::ModelOrderingFilter = RESTFramework::Filters::OrderingFilter
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# A simple filtering backend that supports filtering a recordset based on query parameters.
|
2
|
-
class RESTFramework::Filters::
|
2
|
+
class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
|
3
3
|
NIL_VALUES = ["nil", "null"].freeze
|
4
4
|
|
5
|
-
# Get a list of
|
5
|
+
# Get a list of filter fields for the current action.
|
6
6
|
def _get_fields
|
7
7
|
# Always return a list of strings; `@controller.get_fields` already does this.
|
8
|
-
return @controller.class.
|
8
|
+
return @controller.class.filter_fields&.map(&:to_s) || @controller.get_fields
|
9
9
|
end
|
10
10
|
|
11
|
-
# Filter params for keys allowed by the current action's
|
11
|
+
# Filter params for keys allowed by the current action's filter_fields/fields config.
|
12
12
|
def _get_filter_params
|
13
13
|
fields = self._get_fields
|
14
14
|
includes = []
|
@@ -22,7 +22,7 @@ class RESTFramework::Filters::ModelQueryFilter < RESTFramework::Filters::BaseFil
|
|
22
22
|
field, sub_field = match[1..2]
|
23
23
|
next false unless field.in?(fields)
|
24
24
|
|
25
|
-
sub_fields = @controller.class.
|
25
|
+
sub_fields = @controller.class.field_config_for(field)[:sub_fields] || []
|
26
26
|
if sub_field.in?(sub_fields)
|
27
27
|
includes << field.to_sym
|
28
28
|
next true
|
@@ -49,7 +49,7 @@ class RESTFramework::Filters::ModelQueryFilter < RESTFramework::Filters::BaseFil
|
|
49
49
|
end
|
50
50
|
|
51
51
|
# Filter data according to the request query parameters.
|
52
|
-
def
|
52
|
+
def filter_data(data)
|
53
53
|
filter_params, includes = self._get_filter_params
|
54
54
|
|
55
55
|
if filter_params.any?
|
@@ -65,4 +65,7 @@ class RESTFramework::Filters::ModelQueryFilter < RESTFramework::Filters::BaseFil
|
|
65
65
|
end
|
66
66
|
|
67
67
|
# Alias for convenience.
|
68
|
-
RESTFramework::
|
68
|
+
RESTFramework::QueryFilter = RESTFramework::Filters::QueryFilter
|
69
|
+
|
70
|
+
# TODO: Compatibility; remove in 1.0.
|
71
|
+
RESTFramework::ModelQueryFilter = RESTFramework::Filters::QueryFilter
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Adapter for the `ransack` gem.
|
2
2
|
class RESTFramework::Filters::RansackFilter < RESTFramework::Filters::BaseFilter
|
3
3
|
# Filter data according to the request query parameters.
|
4
|
-
def
|
4
|
+
def filter_data(data)
|
5
5
|
q = @controller.request.query_parameters[@controller.ransack_query_param]
|
6
6
|
|
7
7
|
if q.present?
|
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
class RESTFramework::Filters::ModelSearchFilter < RESTFramework::Filters::BaseFilter
|
1
|
+
class RESTFramework::Filters::SearchFilter < RESTFramework::Filters::BaseFilter
|
3
2
|
# Get a list of search fields for the current action.
|
4
3
|
def _get_fields
|
5
4
|
if search_fields = @controller.search_fields
|
@@ -13,7 +12,7 @@ class RESTFramework::Filters::ModelSearchFilter < RESTFramework::Filters::BaseFi
|
|
13
12
|
end
|
14
13
|
|
15
14
|
# Filter data according to the request query parameters.
|
16
|
-
def
|
15
|
+
def filter_data(data)
|
17
16
|
search = @controller.request.query_parameters[@controller.search_query_param]
|
18
17
|
|
19
18
|
if search.present?
|
@@ -41,4 +40,7 @@ class RESTFramework::Filters::ModelSearchFilter < RESTFramework::Filters::BaseFi
|
|
41
40
|
end
|
42
41
|
|
43
42
|
# Alias for convenience.
|
44
|
-
RESTFramework::
|
43
|
+
RESTFramework::SearchFilter = RESTFramework::Filters::SearchFilter
|
44
|
+
|
45
|
+
# TODO: Compatibility; remove in 1.0.
|
46
|
+
RESTFramework::ModelSearchFilter = RESTFramework::Filters::SearchFilter
|
@@ -3,7 +3,7 @@ end
|
|
3
3
|
|
4
4
|
require_relative "filters/base_filter"
|
5
5
|
|
6
|
-
require_relative "filters/
|
7
|
-
require_relative "filters/
|
8
|
-
require_relative "filters/model_search_filter"
|
6
|
+
require_relative "filters/ordering_filter"
|
7
|
+
require_relative "filters/query_filter"
|
9
8
|
require_relative "filters/ransack_filter"
|
9
|
+
require_relative "filters/search_filter"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# This module provides the common functionality for any controller mixins, a `root` action, and
|
2
|
-
# the ability to route arbitrary actions with `extra_actions`. This is also where `
|
3
|
-
#
|
2
|
+
# the ability to route arbitrary actions with `extra_actions`. This is also where `render_api` is
|
3
|
+
# implemented.
|
4
4
|
module RESTFramework::Mixins::BaseControllerMixin
|
5
5
|
RRF_BASE_CONFIG = {
|
6
6
|
extra_actions: nil,
|
@@ -11,16 +11,14 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
11
11
|
title: nil,
|
12
12
|
description: nil,
|
13
13
|
inflect_acronyms: ["ID", "IDs", "REST", "API", "APIs"].freeze,
|
14
|
-
}
|
15
|
-
RRF_BASE_INSTANCE_CONFIG = {
|
16
|
-
filter_backends: nil,
|
17
14
|
|
18
15
|
# Options related to serialization.
|
19
16
|
rescue_unknown_format_with: :json,
|
20
17
|
serializer_class: nil,
|
21
18
|
serialize_to_json: true,
|
22
19
|
serialize_to_xml: true,
|
23
|
-
|
20
|
+
}
|
21
|
+
RRF_BASE_INSTANCE_CONFIG = {
|
24
22
|
# Options related to pagination.
|
25
23
|
paginator_class: nil,
|
26
24
|
page_size: 20,
|
@@ -35,12 +33,12 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
35
33
|
|
36
34
|
# Default action for API root.
|
37
35
|
def root
|
38
|
-
|
36
|
+
render_api({message: "This is the API root."})
|
39
37
|
end
|
40
38
|
|
41
39
|
module ClassMethods
|
42
|
-
#
|
43
|
-
#
|
40
|
+
# By default, this is the name of the controller class, titleized and with any custom inflection
|
41
|
+
# acronyms applied.
|
44
42
|
def get_title
|
45
43
|
return self.title || RESTFramework::Utils.inflect(
|
46
44
|
self.name.demodulize.chomp("Controller").titleize(keep_id_suffix: true),
|
@@ -49,7 +47,7 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
49
47
|
end
|
50
48
|
|
51
49
|
# Get a label from a field/column name, titleized and inflected.
|
52
|
-
def
|
50
|
+
def label_for(s)
|
53
51
|
return RESTFramework::Utils.inflect(
|
54
52
|
s.to_s.titleize(keep_id_suffix: true),
|
55
53
|
self.inflect_acronyms,
|
@@ -57,7 +55,7 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
57
55
|
end
|
58
56
|
|
59
57
|
# Collect actions (including extra actions) metadata for this controller.
|
60
|
-
def
|
58
|
+
def actions_metadata
|
61
59
|
actions = {}
|
62
60
|
|
63
61
|
# Start with builtin actions.
|
@@ -67,7 +65,7 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
67
65
|
next unless self.method_defined?(action)
|
68
66
|
|
69
67
|
actions[action] = {
|
70
|
-
path: "", methods: methods, type: :builtin, metadata: {label: self.
|
68
|
+
path: "", methods: methods, type: :builtin, metadata: {label: self.label_for(action)}
|
71
69
|
}
|
72
70
|
end
|
73
71
|
|
@@ -76,7 +74,7 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
76
74
|
next unless self.method_defined?(action)
|
77
75
|
|
78
76
|
actions[action] = {
|
79
|
-
path: "", methods: methods, type: :builtin, metadata: {label: self.
|
77
|
+
path: "", methods: methods, type: :builtin, metadata: {label: self.label_for(action)}
|
80
78
|
}
|
81
79
|
end
|
82
80
|
|
@@ -89,7 +87,7 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
89
87
|
end
|
90
88
|
|
91
89
|
# Collect member actions (including extra member actions) metadata for this controller.
|
92
|
-
def
|
90
|
+
def member_actions_metadata
|
93
91
|
actions = {}
|
94
92
|
|
95
93
|
# Start with builtin actions.
|
@@ -97,7 +95,7 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
97
95
|
next unless self.method_defined?(action)
|
98
96
|
|
99
97
|
actions[action] = {
|
100
|
-
path: "", methods: methods, type: :builtin, metadata: {label: self.
|
98
|
+
path: "", methods: methods, type: :builtin, metadata: {label: self.label_for(action)}
|
101
99
|
}
|
102
100
|
end
|
103
101
|
|
@@ -109,8 +107,7 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
109
107
|
return actions
|
110
108
|
end
|
111
109
|
|
112
|
-
|
113
|
-
def get_options_metadata
|
110
|
+
def options_metadata
|
114
111
|
return {
|
115
112
|
title: self.get_title,
|
116
113
|
description: self.description,
|
@@ -119,8 +116,8 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
119
116
|
self.serialize_to_json ? "application/json" : nil,
|
120
117
|
self.serialize_to_xml ? "application/xml" : nil,
|
121
118
|
].compact,
|
122
|
-
actions: self.
|
123
|
-
member_actions: self.
|
119
|
+
actions: self.actions_metadata,
|
120
|
+
member_actions: self.member_actions_metadata,
|
124
121
|
}.compact
|
125
122
|
end
|
126
123
|
|
@@ -203,47 +200,24 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
203
200
|
end
|
204
201
|
end
|
205
202
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
# Support dynamically resolving serializer given a symbol or string.
|
211
|
-
serializer_class = serializer_class.to_s if serializer_class.is_a?(Symbol)
|
212
|
-
if serializer_class.is_a?(String)
|
213
|
-
serializer_class = self.class.const_get(serializer_class)
|
214
|
-
end
|
215
|
-
|
216
|
-
# Wrap it with an adapter if it's an active_model_serializer.
|
217
|
-
if defined?(ActiveModel::Serializer) && (serializer_class < ActiveModel::Serializer)
|
218
|
-
serializer_class = RESTFramework::ActiveModelSerializerAdapterFactory.for(serializer_class)
|
203
|
+
def serializer_class
|
204
|
+
# TODO: Compatibility; remove in 1.0.
|
205
|
+
if klass = self.try(:get_serializer_class)
|
206
|
+
return klass
|
219
207
|
end
|
220
208
|
|
221
|
-
return serializer_class
|
209
|
+
return self.class.serializer_class
|
222
210
|
end
|
223
211
|
|
224
212
|
# Serialize the given data using the `serializer_class`.
|
225
213
|
def serialize(data, **kwargs)
|
226
|
-
return self.
|
214
|
+
return RESTFramework::Utils.wrap_ams(self.serializer_class).new(
|
215
|
+
data, controller: self, **kwargs
|
216
|
+
).serialize
|
227
217
|
end
|
228
218
|
|
229
|
-
|
230
|
-
|
231
|
-
return self.filter_backends || []
|
232
|
-
end
|
233
|
-
|
234
|
-
# Filter an arbitrary data set over all configured filter backends.
|
235
|
-
def get_filtered_data(data)
|
236
|
-
# Apply each filter sequentially.
|
237
|
-
self.get_filter_backends.each do |filter_class|
|
238
|
-
filter = filter_class.new(controller: self)
|
239
|
-
data = filter.get_filtered_data(data)
|
240
|
-
end
|
241
|
-
|
242
|
-
return data
|
243
|
-
end
|
244
|
-
|
245
|
-
def get_options_metadata
|
246
|
-
return self.class.get_options_metadata
|
219
|
+
def options_metadata
|
220
|
+
return self.class.options_metadata
|
247
221
|
end
|
248
222
|
|
249
223
|
def rrf_error_handler(e)
|
@@ -295,25 +269,25 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
295
269
|
begin
|
296
270
|
respond_to do |format|
|
297
271
|
if payload == ""
|
298
|
-
format.json { head(kwargs[:status] || :no_content) } if self.serialize_to_json
|
299
|
-
format.xml { head(kwargs[:status] || :no_content) } if self.serialize_to_xml
|
272
|
+
format.json { head(kwargs[:status] || :no_content) } if self.class.serialize_to_json
|
273
|
+
format.xml { head(kwargs[:status] || :no_content) } if self.class.serialize_to_xml
|
300
274
|
else
|
301
275
|
format.json {
|
302
276
|
render(json: payload, **kwargs.merge(json_kwargs))
|
303
|
-
} if self.serialize_to_json
|
277
|
+
} if self.class.serialize_to_json
|
304
278
|
format.xml {
|
305
279
|
render(xml: payload, **kwargs.merge(xml_kwargs))
|
306
|
-
} if self.serialize_to_xml
|
280
|
+
} if self.class.serialize_to_xml
|
307
281
|
# TODO: possibly support more formats here if supported?
|
308
282
|
end
|
309
283
|
format.html {
|
310
284
|
@payload = payload
|
311
285
|
if payload == ""
|
312
|
-
@json_payload = "" if self.serialize_to_json
|
313
|
-
@xml_payload = "" if self.serialize_to_xml
|
286
|
+
@json_payload = "" if self.class.serialize_to_json
|
287
|
+
@xml_payload = "" if self.class.serialize_to_xml
|
314
288
|
else
|
315
|
-
@json_payload = payload.to_json if self.serialize_to_json
|
316
|
-
@xml_payload = payload.to_xml if self.serialize_to_xml
|
289
|
+
@json_payload = payload.to_json if self.class.serialize_to_json
|
290
|
+
@xml_payload = payload.to_xml if self.class.serialize_to_xml
|
317
291
|
end
|
318
292
|
@title ||= self.class.get_title
|
319
293
|
@description ||= self.class.description
|
@@ -329,7 +303,7 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
329
303
|
}
|
330
304
|
end
|
331
305
|
rescue ActionController::UnknownFormat
|
332
|
-
if !already_rescued_unknown_format && rescue_format = self.rescue_unknown_format_with
|
306
|
+
if !already_rescued_unknown_format && rescue_format = self.class.rescue_unknown_format_with
|
333
307
|
request.format = rescue_format
|
334
308
|
already_rescued_unknown_format = true
|
335
309
|
retry
|
@@ -339,9 +313,11 @@ module RESTFramework::Mixins::BaseControllerMixin
|
|
339
313
|
end
|
340
314
|
end
|
341
315
|
|
342
|
-
#
|
316
|
+
# TODO: Might make this the default render method in the future.
|
317
|
+
alias_method :render_api, :api_response
|
318
|
+
|
343
319
|
def options
|
344
|
-
return api_response(self.
|
320
|
+
return api_response(self.options_metadata)
|
345
321
|
end
|
346
322
|
end
|
347
323
|
|
@@ -6,7 +6,7 @@ module RESTFramework::Mixins::BulkCreateModelMixin
|
|
6
6
|
# While bulk update/destroy are obvious because they create new router endpoints, bulk create
|
7
7
|
# overloads the existing collection `POST` endpoint, so we add a special key to the options
|
8
8
|
# metadata to indicate bulk create is supported.
|
9
|
-
def
|
9
|
+
def options_metadata
|
10
10
|
return super.merge({bulk_create: true})
|
11
11
|
end
|
12
12
|
|