rest_framework 0.5.6 → 0.6.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 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