rest_framework 1.0.0 → 1.0.1
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/rest_framework/_head.html.erb +3 -1
- data/app/views/rest_framework/_heading.html.erb +1 -0
- data/app/views/rest_framework/head/_extra.html.erb +0 -0
- data/app/views/rest_framework/heading/_actions.html.erb +2 -1
- data/app/views/rest_framework/heading/_extra.html.erb +0 -0
- data/app/views/rest_framework/heading/actions/_extra.html.erb +0 -0
- data/app/views/rest_framework/routes_and_forms/_html_form.html.erb +2 -2
- data/lib/rest_framework/filters/query_filter.rb +3 -2
- data/lib/rest_framework/mixins/bulk_model_controller_mixin.rb +1 -1
- data/lib/rest_framework/mixins/model_controller_mixin.rb +50 -31
- data/lib/rest_framework/routers.rb +3 -9
- data/lib/rest_framework/serializers/native_serializer.rb +67 -114
- data/lib/rest_framework/utils.rb +6 -5
- data/lib/rest_framework.rb +15 -8
- data/vendor/assets/javascripts/rest_framework/external.min.js +4 -4
- data/vendor/assets/stylesheets/rest_framework/external.min.css +3 -3
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '087da1898a00546ec0964c7f16a31ccdfdfefe6574e614764a9a3fe50dbbd89b'
|
|
4
|
+
data.tar.gz: a9720b7867fb57f681d786080d570161ff4ebb653015d429b331397a9962d95d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cd6d3507ce2a40c4dffa147b5bd381ff5ae4b9dedc0f42d879ef96c66c9f67b07abb7de1005fe46fdaa8412aedd1d0c2c5332c17a11c407a577997b70dc3455f
|
|
7
|
+
data.tar.gz: d2d76d723db9f61c731aeb58d9cfd0c5c02c73bc7c75406ae835c4a24fc75f0fdfc9df24083313b2dc792b1dc7fe86eb4d8bdc32f6c15dfd60a5603ee7114cea
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.1
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
el.setAttribute("data-bs-theme", "dark")
|
|
185
185
|
})
|
|
186
186
|
} else {
|
|
187
|
-
console.log(`RRF: Unknown mode: ${
|
|
187
|
+
console.log(`RRF: Unknown mode: ${realMode}`)
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
@@ -372,3 +372,5 @@
|
|
|
372
372
|
.then((body) => { rrfReplaceDocument(body) })
|
|
373
373
|
}
|
|
374
374
|
</script>
|
|
375
|
+
|
|
376
|
+
<%= render partial: "rest_framework/head/extra" %>
|
|
File without changes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<div style="float: right">
|
|
2
|
+
<%= render partial: "rest_framework/heading/actions/extra" %>
|
|
2
3
|
<% if @route_groups.values[0].any? { |r| r[:matches_path] && r[:verb] == "DELETE" && r[:action] == "destroy" } %>
|
|
3
4
|
<button type="button" class="btn btn-danger" onclick="rrfDelete(this)">DELETE</button>
|
|
4
5
|
<% end %>
|
|
@@ -6,4 +7,4 @@
|
|
|
6
7
|
<button type="button" class="btn btn-primary" onclick="rrfOptions(this)">OPTIONS</button>
|
|
7
8
|
<% end %>
|
|
8
9
|
<button type="button" class="btn btn-primary" onclick="rrfGet(this)">GET</button>
|
|
9
|
-
</div>
|
|
10
|
+
</div>
|
|
File without changes
|
|
File without changes
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
}.compact) do |form| %>
|
|
21
21
|
<% controller.get_fields.map(&:to_s).each do |f| %>
|
|
22
22
|
<%
|
|
23
|
-
# Don't provide form fields for associations or
|
|
23
|
+
# Don't provide form fields for associations or read-only fields.
|
|
24
24
|
cfg = controller.class.field_configuration[f]
|
|
25
|
-
next if !cfg || cfg[:kind] == "association" || cfg[:
|
|
25
|
+
next if !cfg || cfg[:kind] == "association" || cfg[:read_only]
|
|
26
26
|
%>
|
|
27
27
|
<div class="mb-2">
|
|
28
28
|
<% if cfg[:kind] == "rich_text" %>
|
|
@@ -15,6 +15,7 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
|
|
|
15
15
|
null: nil,
|
|
16
16
|
lt: ->(f, v) { { f => ...v } },
|
|
17
17
|
# `gt` must negate `lte` because Rails doesn't support `>` with endless ranges.
|
|
18
|
+
# Ref: https://github.com/rails/rails/pull/47345
|
|
18
19
|
gt: ->(f, v) { Not.new({ f => ..v }) },
|
|
19
20
|
lte: ->(f, v) { { f => ..v } },
|
|
20
21
|
gte: ->(f, v) { { f => v.. } },
|
|
@@ -22,9 +23,9 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
|
|
|
22
23
|
cont: ->(f, v) { [ "#{f} LIKE ?", "%#{ActiveRecord::Base.sanitize_sql_like(v)}%" ] },
|
|
23
24
|
in: ->(f, v) {
|
|
24
25
|
if v.is_a?(Array)
|
|
25
|
-
{ f => v.map { |
|
|
26
|
+
{ f => v.map { |el| el == "null" ? nil : el } }
|
|
26
27
|
elsif v.is_a?(String)
|
|
27
|
-
{ f => v.split(",").map { |
|
|
28
|
+
{ f => v.split(",").map { |el| el == "null" ? nil : el } }
|
|
28
29
|
end
|
|
29
30
|
},
|
|
30
31
|
}.freeze
|
|
@@ -25,7 +25,7 @@ module RESTFramework::Mixins::BulkCreateModelMixin
|
|
|
25
25
|
create_data = self.get_create_params(bulk_mode: true)[:_json]
|
|
26
26
|
|
|
27
27
|
# Perform bulk create in a transaction.
|
|
28
|
-
ActiveRecord::Base.transaction { self.
|
|
28
|
+
ActiveRecord::Base.transaction { self.create_from.create(create_data) }
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -23,27 +23,30 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
23
23
|
# Attributes for configuring record fields.
|
|
24
24
|
fields: nil,
|
|
25
25
|
field_config: nil,
|
|
26
|
+
read_only_fields: RESTFramework.config.read_only_fields,
|
|
27
|
+
write_only_fields: RESTFramework.config.write_only_fields,
|
|
28
|
+
hidden_fields: nil,
|
|
26
29
|
|
|
27
30
|
# Attributes for finding records.
|
|
28
31
|
find_by_fields: nil,
|
|
29
|
-
find_by_query_param: "find_by",
|
|
32
|
+
find_by_query_param: "find_by".freeze,
|
|
30
33
|
|
|
31
34
|
# Options for what should be included/excluded from default fields.
|
|
32
35
|
exclude_associations: false,
|
|
33
36
|
|
|
34
37
|
# Options for handling request body parameters.
|
|
35
38
|
allowed_parameters: nil,
|
|
36
|
-
filter_pk_from_request_body: true,
|
|
37
|
-
exclude_body_fields: RESTFramework.config.exclude_body_fields,
|
|
38
39
|
|
|
39
40
|
# Attributes for the default native serializer.
|
|
40
41
|
native_serializer_config: nil,
|
|
41
42
|
native_serializer_singular_config: nil,
|
|
42
43
|
native_serializer_plural_config: nil,
|
|
43
|
-
native_serializer_only_query_param: "only",
|
|
44
|
-
native_serializer_except_query_param: "except",
|
|
44
|
+
native_serializer_only_query_param: "only".freeze,
|
|
45
|
+
native_serializer_except_query_param: "except".freeze,
|
|
46
|
+
native_serializer_include_query_param: "include".freeze,
|
|
47
|
+
native_serializer_exclude_query_param: "exclude".freeze,
|
|
45
48
|
native_serializer_associations_limit: nil,
|
|
46
|
-
native_serializer_associations_limit_query_param: "associations_limit",
|
|
49
|
+
native_serializer_associations_limit_query_param: "associations_limit".freeze,
|
|
47
50
|
native_serializer_include_associations_count: false,
|
|
48
51
|
|
|
49
52
|
# Attributes for filtering, ordering, and searching.
|
|
@@ -51,19 +54,19 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
51
54
|
RESTFramework::QueryFilter,
|
|
52
55
|
RESTFramework::OrderingFilter,
|
|
53
56
|
RESTFramework::SearchFilter,
|
|
54
|
-
],
|
|
57
|
+
].freeze,
|
|
55
58
|
filter_recordset_before_find: true,
|
|
56
59
|
filter_fields: nil,
|
|
57
60
|
ordering_fields: nil,
|
|
58
|
-
ordering_query_param: "ordering",
|
|
61
|
+
ordering_query_param: "ordering".freeze,
|
|
59
62
|
ordering_no_reorder: false,
|
|
60
63
|
search_fields: nil,
|
|
61
|
-
search_query_param: "search",
|
|
64
|
+
search_query_param: "search".freeze,
|
|
62
65
|
search_ilike: false,
|
|
63
66
|
ransack_options: nil,
|
|
64
|
-
ransack_query_param: "q",
|
|
67
|
+
ransack_query_param: "q".freeze,
|
|
65
68
|
ransack_distinct: true,
|
|
66
|
-
ransack_distinct_query_param: "distinct",
|
|
69
|
+
ransack_distinct_query_param: "distinct".freeze,
|
|
67
70
|
|
|
68
71
|
# Options for association assignment.
|
|
69
72
|
permit_id_assignment: true,
|
|
@@ -135,7 +138,9 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
135
138
|
reflections = model.reflections
|
|
136
139
|
attributes = model._default_attributes
|
|
137
140
|
readonly_attributes = model.readonly_attributes
|
|
138
|
-
|
|
141
|
+
read_only_fields = self.read_only_fields&.map(&:to_s)&.to_set || Set[]
|
|
142
|
+
write_only_fields = self.write_only_fields&.map(&:to_s)&.to_set || Set[]
|
|
143
|
+
hidden_fields = self.hidden_fields&.map(&:to_s)&.to_set || Set[]
|
|
139
144
|
rich_text_association_names = model.reflect_on_all_associations(:has_one)
|
|
140
145
|
.collect(&:name)
|
|
141
146
|
.select { |n| n.to_s.start_with?("rich_text_") }
|
|
@@ -149,14 +154,29 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
149
154
|
if model.primary_key == f
|
|
150
155
|
cfg[:primary_key] = true
|
|
151
156
|
|
|
152
|
-
unless cfg.key?(:
|
|
153
|
-
cfg[:
|
|
157
|
+
unless cfg.key?(:read_only)
|
|
158
|
+
cfg[:read_only] = true
|
|
154
159
|
end
|
|
155
160
|
end
|
|
156
161
|
|
|
157
|
-
# Annotate
|
|
158
|
-
if f.in?(readonly_attributes) || f.in?(
|
|
159
|
-
|
|
162
|
+
# Annotate field mutability and display properties.
|
|
163
|
+
cfg[:read_only] = true if f.in?(readonly_attributes) || f.in?(read_only_fields)
|
|
164
|
+
cfg[:write_only] = true if f.in?(write_only_fields)
|
|
165
|
+
cfg[:hidden] = true if f.in?(hidden_fields)
|
|
166
|
+
|
|
167
|
+
# Raise warnings on some bad combinations of properties.
|
|
168
|
+
if cfg[:write_only]
|
|
169
|
+
if cfg[:read_only]
|
|
170
|
+
Rails.logger.warn("RRF: `#{f}` write_only conflicts with read_only.")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if cfg[:hidden]
|
|
174
|
+
Rails.logger.warn("RRF: `#{f}` write_only implies hidden.")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
if cfg[:hidden_from_index]
|
|
178
|
+
Rails.logger.warn("RRF: `#{f}` write_only implies hidden_from_index.")
|
|
179
|
+
end
|
|
160
180
|
end
|
|
161
181
|
|
|
162
182
|
# Annotate column data.
|
|
@@ -167,12 +187,12 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
167
187
|
end
|
|
168
188
|
|
|
169
189
|
# Add default values from the model's schema.
|
|
170
|
-
if column_default = column_defaults[f]
|
|
171
|
-
cfg[:default]
|
|
190
|
+
if cfg[:default].nil? && (column_default = column_defaults[f])
|
|
191
|
+
cfg[:default] = column_default
|
|
172
192
|
end
|
|
173
193
|
|
|
174
194
|
# Add metadata from the model's attributes hash.
|
|
175
|
-
if attribute = attributes[f]
|
|
195
|
+
if attributes.key?(f) && attribute = attributes[f]
|
|
176
196
|
if cfg[:default].nil? && default = attribute.value_before_type_cast
|
|
177
197
|
cfg[:default] = default
|
|
178
198
|
end
|
|
@@ -256,6 +276,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
256
276
|
# Determine if this is just a method.
|
|
257
277
|
if !cfg[:kind] && model.method_defined?(f)
|
|
258
278
|
cfg[:kind] = "method"
|
|
279
|
+
cfg[:read_only] = true if cfg[:read_only].nil?
|
|
259
280
|
end
|
|
260
281
|
|
|
261
282
|
# Collect validator options into a hash on their type, while also updating `required` based
|
|
@@ -284,7 +305,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
284
305
|
end
|
|
285
306
|
|
|
286
307
|
next [ f, cfg ]
|
|
287
|
-
}.to_h.with_indifferent_access
|
|
308
|
+
}.to_h.compact.with_indifferent_access
|
|
288
309
|
end
|
|
289
310
|
|
|
290
311
|
def openapi_schema
|
|
@@ -309,7 +330,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
309
330
|
v[:type] = cfg[:type]
|
|
310
331
|
end
|
|
311
332
|
|
|
312
|
-
v[:readOnly] = true if cfg[:
|
|
333
|
+
v[:readOnly] = true if cfg[:read_only]
|
|
313
334
|
v[:default] = cfg[:default] if cfg.key?(:default)
|
|
314
335
|
|
|
315
336
|
if enum_variants = cfg[:enum_variants]
|
|
@@ -497,7 +518,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
497
518
|
config = self.class.field_configuration[f]
|
|
498
519
|
|
|
499
520
|
# ActionText Integration:
|
|
500
|
-
if self.class.enable_action_text && reflections.key?("
|
|
521
|
+
if self.class.enable_action_text && reflections.key?("rich_text_#{f}")
|
|
501
522
|
next f
|
|
502
523
|
end
|
|
503
524
|
|
|
@@ -632,14 +653,12 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
632
653
|
body_params[k].unshift(*v)
|
|
633
654
|
end
|
|
634
655
|
|
|
635
|
-
# Filter
|
|
636
|
-
|
|
637
|
-
|
|
656
|
+
# Filter read-only fields.
|
|
657
|
+
body_params.delete_if do |f, _|
|
|
658
|
+
cfg = self.class.field_configuration[f]
|
|
659
|
+
cfg && cfg[:read_only]
|
|
638
660
|
end
|
|
639
661
|
|
|
640
|
-
# Filter fields in `exclude_body_fields`.
|
|
641
|
-
(self.class.exclude_body_fields || []).each { |f| body_params.delete(f) }
|
|
642
|
-
|
|
643
662
|
body_params
|
|
644
663
|
end
|
|
645
664
|
alias_method :get_create_params, :get_body_params
|
|
@@ -701,7 +720,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
|
|
|
701
720
|
end
|
|
702
721
|
|
|
703
722
|
# Determine what collection to call `create` on.
|
|
704
|
-
def
|
|
723
|
+
def create_from
|
|
705
724
|
if self.class.create_from_recordset
|
|
706
725
|
# Create with any properties inherited from the recordset. We exclude any `select` clauses
|
|
707
726
|
# in case model callbacks need to call `count` on this collection, which typically raises a
|
|
@@ -769,7 +788,7 @@ module RESTFramework::Mixins::CreateModelMixin
|
|
|
769
788
|
|
|
770
789
|
# Perform the `create!` call and return the created record.
|
|
771
790
|
def create!
|
|
772
|
-
self.
|
|
791
|
+
self.create_from.create!(self.get_create_params)
|
|
773
792
|
end
|
|
774
793
|
end
|
|
775
794
|
|
|
@@ -46,7 +46,7 @@ module ActionDispatch::Routing
|
|
|
46
46
|
controller
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
#
|
|
49
|
+
# Internal interface for routing extra actions.
|
|
50
50
|
def _route_extra_actions(actions, &block)
|
|
51
51
|
parsed_actions = RESTFramework::Utils.parse_extra_actions(actions)
|
|
52
52
|
|
|
@@ -84,13 +84,7 @@ module ActionDispatch::Routing
|
|
|
84
84
|
unscoped = kwargs.delete(:unscoped)
|
|
85
85
|
|
|
86
86
|
# Determine plural/singular resource.
|
|
87
|
-
|
|
88
|
-
force_plural = kwargs.delete(:force_plural)
|
|
89
|
-
if force_singular
|
|
90
|
-
singular = true
|
|
91
|
-
elsif force_plural
|
|
92
|
-
singular = false
|
|
93
|
-
elsif !controller_class.singleton_controller.nil?
|
|
87
|
+
if !controller_class.singleton_controller.nil?
|
|
94
88
|
singular = controller_class.singleton_controller
|
|
95
89
|
else
|
|
96
90
|
singular = default_singular
|
|
@@ -98,7 +92,7 @@ module ActionDispatch::Routing
|
|
|
98
92
|
resource_method = singular ? :resource : :resources
|
|
99
93
|
|
|
100
94
|
# Call either `resource` or `resources`, passing appropriate modifiers.
|
|
101
|
-
skip = RESTFramework::Utils.get_skipped_builtin_actions(controller_class)
|
|
95
|
+
skip = RESTFramework::Utils.get_skipped_builtin_actions(controller_class, singular)
|
|
102
96
|
public_send(resource_method, name, except: skip, **kwargs) do
|
|
103
97
|
if controller_class.respond_to?(:extra_member_actions)
|
|
104
98
|
member do
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# This serializer uses `.serializable_hash` to convert objects to Ruby primitives (with the
|
|
2
2
|
# top-level being either an array or a hash).
|
|
3
3
|
class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers::BaseSerializer
|
|
4
|
+
EXTRACT_FROM_QUERY = ->(p, controller) {
|
|
5
|
+
return Set[] if p.blank?
|
|
6
|
+
(
|
|
7
|
+
controller.request&.query_parameters&.[](p).presence&.split(",")&.map { |x|
|
|
8
|
+
x.strip.presence
|
|
9
|
+
}&.compact || []
|
|
10
|
+
).to_set
|
|
11
|
+
}
|
|
4
12
|
class_attribute :config
|
|
5
13
|
class_attribute :singular_config
|
|
6
14
|
class_attribute :plural_config
|
|
@@ -26,28 +34,66 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
|
|
|
26
34
|
@model ||= @controller.class.get_model if @controller
|
|
27
35
|
end
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@controller&.action_name&.to_sym
|
|
37
|
+
def action
|
|
38
|
+
@action ||= @controller&.action_name&.to_sym
|
|
32
39
|
end
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
def fields
|
|
42
|
+
return @fields if defined?(@fields)
|
|
43
|
+
return nil unless base_fields = @controller&.get_fields
|
|
44
|
+
|
|
45
|
+
only_param = @controller.class.native_serializer_only_query_param
|
|
46
|
+
except_param = @controller.class.native_serializer_except_query_param
|
|
47
|
+
include_param = @controller.class.native_serializer_include_query_param
|
|
48
|
+
exclude_param = @controller.class.native_serializer_exclude_query_param
|
|
49
|
+
|
|
50
|
+
only = EXTRACT_FROM_QUERY.call(only_param, @controller)
|
|
51
|
+
except = EXTRACT_FROM_QUERY.call(except_param, @controller)
|
|
52
|
+
include = EXTRACT_FROM_QUERY.call(include_param, @controller)
|
|
53
|
+
exclude = EXTRACT_FROM_QUERY.call(exclude_param, @controller)
|
|
54
|
+
|
|
55
|
+
field_configuration = @controller.class.field_configuration
|
|
56
|
+
@fields = base_fields.select do |f|
|
|
57
|
+
cfg = field_configuration[f]
|
|
58
|
+
|
|
59
|
+
# We never serialize write-only fields.
|
|
60
|
+
next false if cfg[:write_only]
|
|
61
|
+
|
|
62
|
+
# We never serialize `hidden_from_index` fields for collections as this is a performance
|
|
63
|
+
# option.
|
|
64
|
+
next false if cfg[:hidden_from_index] && @many
|
|
65
|
+
|
|
66
|
+
# Explicitly excluded fields should never be serialized.
|
|
67
|
+
next false if f.in?(except) || f.in?(exclude)
|
|
68
|
+
|
|
69
|
+
# Hidden fields must be in `only` or `include` to be serialized; for non-hidden fields, either
|
|
70
|
+
# `only` must be empty, or the field must be in `only` or `include`.
|
|
71
|
+
if cfg[:hidden]
|
|
72
|
+
next true if f.in?(only) || f.in?(include)
|
|
73
|
+
elsif only.empty? || f.in?(only) || f.in?(include)
|
|
74
|
+
next true
|
|
75
|
+
end
|
|
37
76
|
|
|
38
|
-
|
|
77
|
+
next false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
@fields
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def get_local_native_serializer_config
|
|
84
|
+
if (action = self.action) && (cfg = action_config)
|
|
39
85
|
# Index action should use :list serializer config if :index is not provided.
|
|
40
|
-
action = :list if action == :index && !
|
|
86
|
+
action = :list if action == :index && !cfg.key?(:index)
|
|
41
87
|
|
|
42
|
-
return
|
|
88
|
+
return cfg[action] if cfg[action]
|
|
43
89
|
end
|
|
44
90
|
|
|
45
91
|
# No action_config, so try singular/plural config if explicitly instructed to via @many.
|
|
46
92
|
return self.plural_config if @many == true && self.plural_config
|
|
47
93
|
return self.singular_config if @many == false && self.singular_config
|
|
48
94
|
|
|
49
|
-
# Lastly, try returning the default config
|
|
50
|
-
self.config
|
|
95
|
+
# Lastly, try returning the default config.
|
|
96
|
+
self.config
|
|
51
97
|
end
|
|
52
98
|
|
|
53
99
|
# Get a native serializer configuration from the controller.
|
|
@@ -63,102 +109,9 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
|
|
|
63
109
|
controller_serializer || @controller.class.native_serializer_config
|
|
64
110
|
end
|
|
65
111
|
|
|
66
|
-
# Filter a single subconfig for specific keys. By default, keys from `fields` are removed from the
|
|
67
|
-
# provided `subcfg`. There are two (mutually exclusive) options to adjust the behavior:
|
|
68
|
-
#
|
|
69
|
-
# `add`: Add any `fields` to the `subcfg` which aren't already in the `subcfg`.
|
|
70
|
-
# `only`: Remove any values found in the `subcfg` not in `fields`.
|
|
71
|
-
def self.filter_subcfg(subcfg, fields:, add: false, only: false)
|
|
72
|
-
raise "`add` and `only` conflict with one another" if add && only
|
|
73
|
-
|
|
74
|
-
# Don't process nil `subcfg`s.
|
|
75
|
-
return subcfg unless subcfg
|
|
76
|
-
|
|
77
|
-
if subcfg.is_a?(Array)
|
|
78
|
-
subcfg = subcfg.map(&:to_sym)
|
|
79
|
-
|
|
80
|
-
if add
|
|
81
|
-
# Only add fields which are not already included.
|
|
82
|
-
subcfg += fields - subcfg
|
|
83
|
-
elsif only
|
|
84
|
-
subcfg.select! { |c| c.in?(fields) }
|
|
85
|
-
else
|
|
86
|
-
subcfg -= fields
|
|
87
|
-
end
|
|
88
|
-
elsif subcfg.is_a?(Hash)
|
|
89
|
-
subcfg = subcfg.symbolize_keys
|
|
90
|
-
|
|
91
|
-
if add
|
|
92
|
-
# Add doesn't make sense in a hash context since we wouldn't know the values.
|
|
93
|
-
elsif only
|
|
94
|
-
subcfg.select! { |k, _v| k.in?(fields) }
|
|
95
|
-
else
|
|
96
|
-
subcfg.reject! { |k, _v| k.in?(fields) }
|
|
97
|
-
end
|
|
98
|
-
else # Subcfg is a single element (assume string/symbol).
|
|
99
|
-
subcfg = subcfg.to_sym
|
|
100
|
-
|
|
101
|
-
if add
|
|
102
|
-
subcfg = subcfg.in?(fields) ? fields : [ subcfg, *fields ]
|
|
103
|
-
elsif only
|
|
104
|
-
subcfg = subcfg.in?(fields) ? subcfg : []
|
|
105
|
-
else
|
|
106
|
-
subcfg = subcfg.in?(fields) ? [] : subcfg
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
subcfg
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Filter out configuration properties based on the :except/:only query parameters.
|
|
114
|
-
def filter_from_request(cfg)
|
|
115
|
-
return cfg unless @controller
|
|
116
|
-
|
|
117
|
-
except_param = @controller.class.native_serializer_except_query_param
|
|
118
|
-
only_param = @controller.class.native_serializer_only_query_param
|
|
119
|
-
if except_param && except = @controller.request&.query_parameters&.[](except_param).presence
|
|
120
|
-
if except = except.split(",").map(&:strip).map(&:to_sym).presence
|
|
121
|
-
# Filter `only`, `except` (additive), `include`, `methods`, and `serializer_methods`.
|
|
122
|
-
if cfg[:only]
|
|
123
|
-
cfg[:only] = self.class.filter_subcfg(cfg[:only], fields: except)
|
|
124
|
-
elsif cfg[:except]
|
|
125
|
-
cfg[:except] = self.class.filter_subcfg(cfg[:except], fields: except, add: true)
|
|
126
|
-
else
|
|
127
|
-
cfg[:except] = except
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
cfg[:include] = self.class.filter_subcfg(cfg[:include], fields: except)
|
|
131
|
-
cfg[:methods] = self.class.filter_subcfg(cfg[:methods], fields: except)
|
|
132
|
-
cfg[:serializer_methods] = self.class.filter_subcfg(
|
|
133
|
-
cfg[:serializer_methods], fields: except
|
|
134
|
-
)
|
|
135
|
-
cfg[:includes_map] = self.class.filter_subcfg(cfg[:includes_map], fields: except)
|
|
136
|
-
end
|
|
137
|
-
elsif only_param && only = @controller.request&.query_parameters&.[](only_param).presence
|
|
138
|
-
if only = only.split(",").map(&:strip).map(&:to_sym).presence
|
|
139
|
-
# Filter `only`, `include`, and `methods`. Adding anything to `except` is not needed,
|
|
140
|
-
# because any configuration there takes precedence over `only`.
|
|
141
|
-
if cfg[:only]
|
|
142
|
-
cfg[:only] = self.class.filter_subcfg(cfg[:only], fields: only, only: true)
|
|
143
|
-
else
|
|
144
|
-
cfg[:only] = only
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
cfg[:include] = self.class.filter_subcfg(cfg[:include], fields: only, only: true)
|
|
148
|
-
cfg[:methods] = self.class.filter_subcfg(cfg[:methods], fields: only, only: true)
|
|
149
|
-
cfg[:serializer_methods] = self.class.filter_subcfg(
|
|
150
|
-
cfg[:serializer_methods], fields: only, only: true
|
|
151
|
-
)
|
|
152
|
-
cfg[:includes_map] = self.class.filter_subcfg(cfg[:includes_map], fields: only, only: true)
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
cfg
|
|
157
|
-
end
|
|
158
|
-
|
|
159
112
|
# Get the associations limit from the controller.
|
|
160
|
-
def
|
|
161
|
-
return @
|
|
113
|
+
def _associations_limit
|
|
114
|
+
return @_associations_limit if defined?(@_associations_limit)
|
|
162
115
|
|
|
163
116
|
limit = @controller&.class&.native_serializer_associations_limit
|
|
164
117
|
|
|
@@ -174,11 +127,11 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
|
|
|
174
127
|
end
|
|
175
128
|
end
|
|
176
129
|
|
|
177
|
-
@
|
|
130
|
+
@_associations_limit = limit
|
|
178
131
|
end
|
|
179
132
|
|
|
180
133
|
# Get a serializer configuration from the controller. `@controller` and `@model` must be set.
|
|
181
|
-
def _get_controller_serializer_config
|
|
134
|
+
def _get_controller_serializer_config
|
|
182
135
|
columns = []
|
|
183
136
|
includes = {}
|
|
184
137
|
methods = []
|
|
@@ -194,7 +147,7 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
|
|
|
194
147
|
reflections = @model.reflections
|
|
195
148
|
attachment_reflections = @model.attachment_reflections
|
|
196
149
|
|
|
197
|
-
fields.each do |f|
|
|
150
|
+
self.fields.each do |f|
|
|
198
151
|
field_config = @controller.class.field_configuration[f]
|
|
199
152
|
next if field_config[:write_only]
|
|
200
153
|
|
|
@@ -216,7 +169,7 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
|
|
|
216
169
|
if ref.collection?
|
|
217
170
|
# If we need to limit the number of serialized association records, then dynamically add a
|
|
218
171
|
# serializer method to do so.
|
|
219
|
-
if limit = self.
|
|
172
|
+
if limit = self._associations_limit
|
|
220
173
|
serializer_methods[f] = f
|
|
221
174
|
self.define_singleton_method(f) do |record|
|
|
222
175
|
next record.send(f).limit(limit).as_json(**sub_config)
|
|
@@ -312,17 +265,17 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
|
|
|
312
265
|
end
|
|
313
266
|
|
|
314
267
|
# If the config wasn't determined, build a serializer config from controller fields.
|
|
315
|
-
if @model && fields
|
|
316
|
-
return self._get_controller_serializer_config
|
|
268
|
+
if @model && self.fields
|
|
269
|
+
return self._get_controller_serializer_config
|
|
317
270
|
end
|
|
318
271
|
|
|
319
272
|
# By default, pass an empty configuration, using the default Rails serializer.
|
|
320
273
|
{}
|
|
321
274
|
end
|
|
322
275
|
|
|
323
|
-
# Get a configuration passable to `serializable_hash` for the object
|
|
276
|
+
# Get a configuration passable to `serializable_hash` for the object.
|
|
324
277
|
def get_serializer_config
|
|
325
|
-
self.
|
|
278
|
+
self.get_raw_serializer_config
|
|
326
279
|
end
|
|
327
280
|
|
|
328
281
|
# Serialize a single record and merge results of `serializer_methods`.
|
data/lib/rest_framework/utils.rb
CHANGED
|
@@ -41,10 +41,11 @@ module RESTFramework::Utils
|
|
|
41
41
|
}.to_h
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
def self.get_skipped_builtin_actions(controller_class)
|
|
44
|
+
def self.get_skipped_builtin_actions(controller_class, singular)
|
|
46
45
|
(
|
|
47
|
-
|
|
46
|
+
(
|
|
47
|
+
RESTFramework::BUILTIN_ACTIONS.keys - (singular ? [ :index ] : [])
|
|
48
|
+
) + RESTFramework::BUILTIN_MEMBER_ACTIONS.keys
|
|
48
49
|
).reject do |action|
|
|
49
50
|
controller_class.method_defined?(action)
|
|
50
51
|
end
|
|
@@ -154,7 +155,7 @@ module RESTFramework::Utils
|
|
|
154
155
|
|
|
155
156
|
# Warn for any unknown keys.
|
|
156
157
|
(h.keys - [ :only, :except, :include, :exclude ]).each do |k|
|
|
157
|
-
Rails.logger.warn("RRF: Unknown key in fields hash: #{k}")
|
|
158
|
+
Rails.logger.warn("RRF: Unknown key in fields hash: #{k}.")
|
|
158
159
|
end
|
|
159
160
|
|
|
160
161
|
# We should always return strings, not symbols.
|
|
@@ -203,7 +204,7 @@ module RESTFramework::Utils
|
|
|
203
204
|
sub_fields = [ model.primary_key ].flatten.compact
|
|
204
205
|
label_fields = RESTFramework.config.label_fields
|
|
205
206
|
|
|
206
|
-
#
|
|
207
|
+
# Preferably find a database column to use as label.
|
|
207
208
|
if match = label_fields.find { |f| f.in?(model.column_names) }
|
|
208
209
|
return sub_fields + [ match ]
|
|
209
210
|
end
|