rest_framework 0.5.6 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4a0533297190fd34ec007578f495d67ef19e3e02b7b495b8be004dd2180f6b2
4
- data.tar.gz: 07f5478177e47bae6476c42587b10b698a862bbcfa762497a7bd11ebb5cf324e
3
+ metadata.gz: 33c45fc367d9899fa2e47ea4c042a46ed381986e9210bf4574b61d1301c60fcc
4
+ data.tar.gz: 225adc8d9b5380e7e403765d7ec21331a61c9a8c562013781571dc84037560d0
5
5
  SHA512:
6
- metadata.gz: d971192a3c797b69e9e7982f2f7e381b7fa2784db39e592c56f3640683985424e40a52e28a0ff503f0c22ad2b0cedbd828ba5b4461e354abe36d5d9e8c01c370
7
- data.tar.gz: 22f0d3779328a0c5dccb382674d44dfea4a5cffea92b96614366d9cbfa570ce436ea5774c13a16f524b995c54074d5ad5d979e82d081d2f012f89923f55339f6
6
+ metadata.gz: 7178418a52918caf797f2a96bd1d39a5e8291d154ba335122b590e5534cc02124f5203feb24dade1f5dcb84ac45b281a7e4fb30274be943025e2f7850bd1cfca
7
+ data.tar.gz: e4195747856948df06aa0c396812d6ea3bbd3d374318b3c9f7a60c55747ae62db25ef76fc59e0705fd1531822304c62a416151772d29877aa4b723852015f2db
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.6
1
+ 0.6.1
@@ -45,6 +45,7 @@ module RESTFramework::BaseControllerMixin
45
45
  page_query_param: "page",
46
46
  page_size_query_param: "page_size",
47
47
  max_page_size: nil,
48
+ rescue_unknown_format_with: :json,
48
49
  serializer_class: nil,
49
50
  serialize_to_json: true,
50
51
  serialize_to_xml: true,
@@ -145,42 +146,55 @@ module RESTFramework::BaseControllerMixin
145
146
  raise RESTFramework::NilPassedToAPIResponseError
146
147
  end
147
148
 
148
- respond_to do |format|
149
- if payload == ""
150
- format.json { head(:no_content) } if self.class.serialize_to_json
151
- format.xml { head(:no_content) } if self.class.serialize_to_xml
152
- else
153
- format.json {
154
- jkwargs = kwargs.merge(json_kwargs)
155
- render(json: payload, layout: false, **jkwargs)
156
- } if self.class.serialize_to_json
157
- format.xml {
158
- xkwargs = kwargs.merge(xml_kwargs)
159
- render(xml: payload, layout: false, **xkwargs)
160
- } if self.class.serialize_to_xml
161
- # TODO: possibly support more formats here if supported?
162
- end
163
- format.html {
164
- @payload = payload
149
+ # Flag to track if we had to rescue unknown format.
150
+ already_rescued_unknown_format = false
151
+
152
+ begin
153
+ respond_to do |format|
165
154
  if payload == ""
166
- @json_payload = "" if self.class.serialize_to_json
167
- @xml_payload = "" if self.class.serialize_to_xml
155
+ format.json { head(:no_content) } if self.class.serialize_to_json
156
+ format.xml { head(:no_content) } if self.class.serialize_to_xml
168
157
  else
169
- @json_payload = payload.to_json if self.class.serialize_to_json
170
- @xml_payload = payload.to_xml if self.class.serialize_to_xml
171
- end
172
- @template_logo_text ||= "Rails REST Framework"
173
- @title ||= self.controller_name.camelize
174
- @route_groups ||= RESTFramework::Utils.get_routes(Rails.application.routes, request)
175
- hkwargs = kwargs.merge(html_kwargs)
176
- begin
177
- render(**hkwargs)
178
- rescue ActionView::MissingTemplate # fallback to rest_framework layout
179
- hkwargs[:layout] = "rest_framework"
180
- hkwargs[:html] = ""
181
- render(**hkwargs)
158
+ format.json {
159
+ jkwargs = kwargs.merge(json_kwargs)
160
+ render(json: payload, layout: false, **jkwargs)
161
+ } if self.class.serialize_to_json
162
+ format.xml {
163
+ xkwargs = kwargs.merge(xml_kwargs)
164
+ render(xml: payload, layout: false, **xkwargs)
165
+ } if self.class.serialize_to_xml
166
+ # TODO: possibly support more formats here if supported?
182
167
  end
183
- }
168
+ format.html {
169
+ @payload = payload
170
+ if payload == ""
171
+ @json_payload = "" if self.class.serialize_to_json
172
+ @xml_payload = "" if self.class.serialize_to_xml
173
+ else
174
+ @json_payload = payload.to_json if self.class.serialize_to_json
175
+ @xml_payload = payload.to_xml if self.class.serialize_to_xml
176
+ end
177
+ @template_logo_text ||= "Rails REST Framework"
178
+ @title ||= self.controller_name.camelize
179
+ @route_groups ||= RESTFramework::Utils.get_routes(Rails.application.routes, request)
180
+ hkwargs = kwargs.merge(html_kwargs)
181
+ begin
182
+ render(**hkwargs)
183
+ rescue ActionView::MissingTemplate # fallback to rest_framework layout
184
+ hkwargs[:layout] = "rest_framework"
185
+ hkwargs[:html] = ""
186
+ render(**hkwargs)
187
+ end
188
+ }
189
+ end
190
+ rescue ActionController::UnknownFormat
191
+ if !already_rescued_unknown_format && rescue_format = self.class.rescue_unknown_format_with
192
+ request.format = rescue_format
193
+ already_rescued_unknown_format = true
194
+ retry
195
+ else
196
+ raise
197
+ end
184
198
  end
185
199
  end
186
200
  end
@@ -59,7 +59,7 @@ module RESTFramework::BaseModelControllerMixin
59
59
  end
60
60
 
61
61
  def _get_specific_action_config(action_config_key, generic_config_key)
62
- action_config = self.class.send(action_config_key) || {}
62
+ action_config = self.class.send(action_config_key)&.with_indifferent_access || {}
63
63
  action = self.action_name&.to_sym
64
64
 
65
65
  # Index action should use :list serializer if :index is not provided.
@@ -68,38 +68,45 @@ module RESTFramework::BaseModelControllerMixin
68
68
  return (action_config[action] if action) || self.class.send(generic_config_key)
69
69
  end
70
70
 
71
- # Get a list of fields for the current action.
72
- def get_fields
73
- return (
74
- _get_specific_action_config(:action_fields, :fields)&.map(&:to_s) ||
75
- self.get_model&.column_names ||
76
- []
77
- )
71
+ # Get a list of fields for the current action. Returning `nil` indicates that anything should be
72
+ # accepted unless `fallback` is true, in which case we should fallback to this controller's model
73
+ # columns, or en empty array.
74
+ def get_fields(fallback: false)
75
+ fields = _get_specific_action_config(:action_fields, :fields)
76
+
77
+ if fallback
78
+ fields ||= self.get_model&.column_names || []
79
+ end
80
+
81
+ return fields
78
82
  end
79
83
 
80
84
  # Get a list of find_by fields for the current action.
81
85
  def get_find_by_fields
82
- return self.class.find_by_fields&.map(&:to_s) || self.get_fields
86
+ return self.class.find_by_fields || self.get_fields
83
87
  end
84
88
 
85
- # Get a list of find_by fields for the current action.
89
+ # Get a list of find_by fields for the current action. Default to the model column names.
86
90
  def get_filterset_fields
87
- return self.class.filterset_fields&.map(&:to_s) || self.get_fields
91
+ return self.class.filterset_fields || self.get_fields(fallback: true)
88
92
  end
89
93
 
90
94
  # Get a list of ordering fields for the current action.
91
95
  def get_ordering_fields
92
- return self.class.ordering_fields&.map(&:to_s) || self.get_fields
96
+ return self.class.ordering_fields || self.get_fields
93
97
  end
94
98
 
95
- # Get a list of search fields for the current action.
99
+ # Get a list of search fields for the current action. Default to the model column names.
96
100
  def get_search_fields
97
- return self.class.search_fields&.map(&:to_s) || self.get_fields
101
+ return self.class.search_fields || self.get_fields(fallback: true)
98
102
  end
99
103
 
100
104
  # Get a list of parameters allowed for the current action.
101
105
  def get_allowed_parameters
102
- return _get_specific_action_config(:allowed_action_parameters, :allowed_parameters)&.map(&:to_s)
106
+ return _get_specific_action_config(
107
+ :allowed_action_parameters,
108
+ :allowed_parameters,
109
+ ) || self.fields
103
110
  end
104
111
 
105
112
  # Helper to get the configured serializer class, or `NativeSerializer` as a default.
@@ -117,14 +124,18 @@ module RESTFramework::BaseModelControllerMixin
117
124
  # Filter the request body for keys in current action's allowed_parameters/fields config.
118
125
  def get_body_params
119
126
  return @_get_body_params ||= begin
120
- fields = self.get_allowed_parameters || self.get_fields
121
-
122
- # Filter the request body.
123
- body_params = request.request_parameters.select { |p| fields.include?(p) }
127
+ # Filter the request body and map to strings. Return all params if we cannot resolve a list of
128
+ # allowed parameters or fields.
129
+ body_params = if allowed_params = self.get_allowed_parameters&.map(&:to_s)
130
+ request.request_parameters.select { |p| allowed_params.include?(p) }
131
+ else
132
+ request.request_parameters
133
+ end
124
134
 
125
- # Add query params in place of missing body params, if configured.
135
+ # Add query params in place of missing body params, if configured. If fields are not defined,
136
+ # fallback to using columns for this particular feature.
126
137
  if self.class.accept_generic_params_as_body_params
127
- (fields - body_params.keys).each do |k|
138
+ (self.get_fields(fallback: true) - body_params.keys).each do |k|
128
139
  if (value = params[k])
129
140
  body_params[k] = value
130
141
  end
@@ -137,7 +148,7 @@ module RESTFramework::BaseModelControllerMixin
137
148
  end
138
149
 
139
150
  # Filter fields in exclude_body_fields.
140
- (self.class.exclude_body_fields || []).each { |f| body_params.delete(f.to_s) }
151
+ (self.class.exclude_body_fields || []).each { |f| body_params.delete(f) }
141
152
 
142
153
  body_params
143
154
  end
@@ -181,13 +192,21 @@ module RESTFramework::BaseModelControllerMixin
181
192
 
182
193
  # Get a single record by primary key or another column, if allowed.
183
194
  def get_record
195
+ # Cache the result.
196
+ return @_get_record if @_get_record
197
+
184
198
  recordset = self.get_recordset
185
- find_by_fields = self.get_find_by_fields
186
199
  find_by_key = self.get_model.primary_key
187
200
 
188
201
  # Find by another column if it's permitted.
189
- if find_by_query_param && params[find_by_query_param].in?(find_by_fields)
190
- find_by_key = params[find_by_query_param]
202
+ if find_by_param = self.class.find_by_query_param.presence
203
+ if find_by = params[find_by_param].presence
204
+ find_by_fields = self.get_find_by_fields&.map(&:to_s)
205
+
206
+ if !find_by_fields || find_by.in?(find_by_fields)
207
+ find_by_key = find_by
208
+ end
209
+ end
191
210
  end
192
211
 
193
212
  # Filter recordset, if configured.
@@ -196,18 +215,18 @@ module RESTFramework::BaseModelControllerMixin
196
215
  end
197
216
 
198
217
  # Return the record. Route key is always :id by Rails convention.
199
- return recordset.find_by!(find_by_key => params[:id])
218
+ return @_get_record = recordset.find_by!(find_by_key => params[:id])
200
219
  end
201
220
  end
202
221
 
203
222
  # Mixin for listing records.
204
223
  module RESTFramework::ListModelMixin
205
224
  def index
206
- api_response(self._index)
225
+ api_response(self.index!)
207
226
  end
208
227
 
209
- def _index
210
- @records = self.get_filtered_data(self.get_recordset)
228
+ def index!
229
+ @records ||= self.get_filtered_data(self.get_recordset)
211
230
 
212
231
  # Handle pagination, if enabled.
213
232
  if self.class.paginator_class
@@ -224,11 +243,11 @@ end
224
243
  # Mixin for showing records.
225
244
  module RESTFramework::ShowModelMixin
226
245
  def show
227
- api_response(self._show)
246
+ api_response(self.show!)
228
247
  end
229
248
 
230
- def _show
231
- @record = self.get_record
249
+ def show!
250
+ @record ||= self.get_record
232
251
  return self.get_serializer_class.new(@record, controller: self).serialize
233
252
  end
234
253
  end
@@ -236,16 +255,16 @@ end
236
255
  # Mixin for creating records.
237
256
  module RESTFramework::CreateModelMixin
238
257
  def create
239
- api_response(self._create)
258
+ api_response(self.create!)
240
259
  end
241
260
 
242
- def _create
261
+ def create!
243
262
  if self.get_recordset.respond_to?(:create!) && self.create_from_recordset
244
263
  # Create with any properties inherited from the recordset.
245
- @record = self.get_recordset.create!(self.get_create_params)
264
+ @record ||= self.get_recordset.create!(self.get_create_params)
246
265
  else
247
266
  # Otherwise, perform a "bare" create.
248
- @record = self.get_model.create!(self.get_create_params)
267
+ @record ||= self.get_model.create!(self.get_create_params)
249
268
  end
250
269
 
251
270
  return self.get_serializer_class.new(@record, controller: self).serialize
@@ -255,11 +274,11 @@ end
255
274
  # Mixin for updating records.
256
275
  module RESTFramework::UpdateModelMixin
257
276
  def update
258
- api_response(self._update)
277
+ api_response(self.update!)
259
278
  end
260
279
 
261
- def _update
262
- @record = self.get_record
280
+ def update!
281
+ @record ||= self.get_record
263
282
  @record.update!(self.get_update_params)
264
283
  return self.get_serializer_class.new(@record, controller: self).serialize
265
284
  end
@@ -268,12 +287,12 @@ end
268
287
  # Mixin for destroying records.
269
288
  module RESTFramework::DestroyModelMixin
270
289
  def destroy
271
- self._destroy
290
+ self.destroy!
272
291
  api_response("")
273
292
  end
274
293
 
275
- def _destroy
276
- @record = self.get_record
294
+ def destroy!
295
+ @record ||= self.get_record
277
296
  @record.destroy!
278
297
  end
279
298
  end
@@ -13,15 +13,17 @@ end
13
13
  class RESTFramework::ModelFilter < RESTFramework::BaseFilter
14
14
  # Filter params for keys allowed by the current action's filterset_fields/fields config.
15
15
  def _get_filter_params
16
- fields = @controller.get_filterset_fields
17
- return @controller.request.query_parameters.select { |p, _|
18
- fields.include?(p)
19
- }.to_h.symbolize_keys # convert from HashWithIndifferentAccess to Hash w/keys
16
+ # Map filterset fields to strings because query parameter keys are strings.
17
+ if fields = @controller.get_filterset_fields&.map(&:to_s)
18
+ return @controller.request.query_parameters.select { |p, _| fields.include?(p) }
19
+ end
20
+
21
+ return @controller.request.query_parameters.to_h
20
22
  end
21
23
 
22
24
  # Filter data according to the request query parameters.
23
25
  def get_filtered_data(data)
24
- filter_params = self._get_filter_params
26
+ filter_params = self._get_filter_params.symbolize_keys
25
27
  unless filter_params.blank?
26
28
  return data.where(**filter_params)
27
29
  end
@@ -36,11 +38,12 @@ class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
36
38
  def _get_ordering
37
39
  return nil if @controller.class.ordering_query_param.blank?
38
40
 
39
- ordering_fields = @controller.get_ordering_fields
41
+ # Ensure ordering_fields are strings since the split param will be strings.
42
+ ordering_fields = @controller.get_ordering_fields&.map(&:to_s)
40
43
  order_string = @controller.params[@controller.class.ordering_query_param]
41
44
 
42
45
  unless order_string.blank?
43
- ordering = {}
46
+ ordering = {}.with_indifferent_access
44
47
  order_string.split(",").each do |field|
45
48
  if field[0] == "-"
46
49
  column = field[1..-1]
@@ -49,8 +52,8 @@ class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
49
52
  column = field
50
53
  direction = :asc
51
54
  end
52
- if column.in?(ordering_fields)
53
- ordering[column.to_sym] = direction
55
+ if !ordering_fields || column.in?(ordering_fields)
56
+ ordering[column] = direction
54
57
  end
55
58
  end
56
59
  return ordering
@@ -6,7 +6,7 @@ module ActionDispatch::Routing
6
6
  # Internal interface to get the controller class from the name and current scope.
7
7
  def _get_controller_class(name, pluralize: true, fallback_reverse_pluralization: true)
8
8
  # Get class name.
9
- name = name.to_s.camelize # camelize to leave plural names plural
9
+ name = name.to_s.camelize # Camelize to leave plural names plural.
10
10
  name = name.pluralize if pluralize
11
11
  if name == name.pluralize
12
12
  name_reverse = name.singularize
@@ -212,9 +212,8 @@ class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
212
212
  return serializer_config.deep_dup
213
213
  end
214
214
 
215
- # If the config wasn't determined, build a serializer config from model fields.
216
- fields = @controller.get_fields if @controller
217
- if fields
215
+ # If the config wasn't determined, build a serializer config from controller fields.
216
+ if fields = @controller&.get_fields
218
217
  fields = fields.deep_dup
219
218
 
220
219
  if @model
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rest_framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregory N. Schmit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-20 00:00:00.000000000 Z
11
+ date: 2022-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails