rest_framework 0.9.7 → 0.9.8

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.
@@ -1,386 +1,7 @@
1
- # The base serializer defines the interface for all REST Framework serializers.
2
- class RESTFramework::BaseSerializer
3
- # Add `object` accessor to be compatible with `ActiveModel::Serializer`.
4
- attr_accessor :object
5
-
6
- # Accept/ignore `*args` to be compatible with the `ActiveModel::Serializer#initialize` signature.
7
- def initialize(object=nil, *args, controller: nil, **kwargs)
8
- @object = object
9
- @controller = controller
10
- end
11
-
12
- # The primary interface for extracting a native Ruby types. This works both for records and
13
- # collections. We accept and ignore `*args` for compatibility with `active_model_serializers`.
14
- def serialize(*args)
15
- raise NotImplementedError
16
- end
17
-
18
- # Synonym for `serialize` for compatibility with `active_model_serializers`.
19
- def serializable_hash(*args)
20
- return self.serialize(*args)
21
- end
22
-
23
- # For compatibility with `active_model_serializers`.
24
- def self.cache_enabled?
25
- return false
26
- end
27
-
28
- # For compatibility with `active_model_serializers`.
29
- def self.fragment_cache_enabled?
30
- return false
31
- end
32
-
33
- # For compatibility with `active_model_serializers`.
34
- def associations(*args, **kwargs)
35
- return []
36
- end
1
+ module RESTFramework::Serializers
37
2
  end
38
3
 
39
- # This serializer uses `.serializable_hash` to convert objects to Ruby primitives (with the
40
- # top-level being either an array or a hash).
41
- class RESTFramework::NativeSerializer < RESTFramework::BaseSerializer
42
- class_attribute :config
43
- class_attribute :singular_config
44
- class_attribute :plural_config
45
- class_attribute :action_config
46
-
47
- # Accept/ignore `*args` to be compatible with the `ActiveModel::Serializer#initialize` signature.
48
- def initialize(object=nil, *args, many: nil, model: nil, **kwargs)
49
- super(object, *args, **kwargs)
50
-
51
- if many.nil?
52
- # Determine if we are dealing with many objects or just one.
53
- @many = @object.is_a?(Enumerable)
54
- else
55
- @many = many
56
- end
57
-
58
- # Determine model either explicitly, or by inspecting @object or @controller.
59
- @model = model
60
- @model ||= @object.class if @object.is_a?(ActiveRecord::Base)
61
- @model ||= @object[0].class if
62
- @many && @object.is_a?(Enumerable) && @object.is_a?(ActiveRecord::Base)
63
-
64
- @model ||= @controller.class.get_model if @controller
65
- end
66
-
67
- # Get controller action, if possible.
68
- def get_action
69
- return @controller&.action_name&.to_sym
70
- end
71
-
72
- # Get a locally defined native serializer configuration, if one is defined.
73
- def get_local_native_serializer_config
74
- action = self.get_action
75
-
76
- if action && self.action_config
77
- # Index action should use :list serializer config if :index is not provided.
78
- action = :list if action == :index && !self.action_config.key?(:index)
79
-
80
- return self.action_config[action] if self.action_config[action]
81
- end
82
-
83
- # No action_config, so try singular/plural config if explicitly instructed to via @many.
84
- return self.plural_config if @many == true && self.plural_config
85
- return self.singular_config if @many == false && self.singular_config
86
-
87
- # Lastly, try returning the default config, or singular/plural config in that order.
88
- return self.config || self.singular_config || self.plural_config
89
- end
90
-
91
- # Get a native serializer configuration from the controller.
92
- def get_controller_native_serializer_config
93
- return nil unless @controller
94
-
95
- if @many == true
96
- controller_serializer = @controller.try(:native_serializer_plural_config)
97
- elsif @many == false
98
- controller_serializer = @controller.try(:native_serializer_singular_config)
99
- end
100
-
101
- return controller_serializer || @controller.try(:native_serializer_config)
102
- end
103
-
104
- # Filter a single subconfig for specific keys. By default, keys from `fields` are removed from the
105
- # provided `subcfg`. There are two (mutually exclusive) options to adjust the behavior:
106
- #
107
- # `add`: Add any `fields` to the `subcfg` which aren't already in the `subcfg`.
108
- # `only`: Remove any values found in the `subcfg` not in `fields`.
109
- def self.filter_subcfg(subcfg, fields:, add: false, only: false)
110
- raise "`add` and `only` conflict with one another" if add && only
111
-
112
- # Don't process nil `subcfg`s.
113
- return subcfg unless subcfg
114
-
115
- if subcfg.is_a?(Array)
116
- subcfg = subcfg.map(&:to_sym)
117
-
118
- if add
119
- # Only add fields which are not already included.
120
- subcfg += fields - subcfg
121
- elsif only
122
- subcfg.select! { |c| c.in?(fields) }
123
- else
124
- subcfg -= fields
125
- end
126
- elsif subcfg.is_a?(Hash)
127
- subcfg = subcfg.symbolize_keys
128
-
129
- if add
130
- # Add doesn't make sense in a hash context since we wouldn't know the values.
131
- elsif only
132
- subcfg.select! { |k, _v| k.in?(fields) }
133
- else
134
- subcfg.reject! { |k, _v| k.in?(fields) }
135
- end
136
- else # Subcfg is a single element (assume string/symbol).
137
- subcfg = subcfg.to_sym
138
-
139
- if add
140
- subcfg = subcfg.in?(fields) ? fields : [subcfg, *fields]
141
- elsif only
142
- subcfg = subcfg.in?(fields) ? subcfg : []
143
- else
144
- subcfg = subcfg.in?(fields) ? [] : subcfg
145
- end
146
- end
147
-
148
- return subcfg
149
- end
150
-
151
- # Filter out configuration properties based on the :except/:only query parameters.
152
- def filter_from_request(cfg)
153
- return cfg unless @controller
154
-
155
- except_param = @controller.try(:native_serializer_except_query_param)
156
- only_param = @controller.try(:native_serializer_only_query_param)
157
- if except_param && except = @controller.request&.query_parameters&.[](except_param).presence
158
- if except = except.split(",").map(&:strip).map(&:to_sym).presence
159
- # Filter `only`, `except` (additive), `include`, `methods`, and `serializer_methods`.
160
- if cfg[:only]
161
- cfg[:only] = self.class.filter_subcfg(cfg[:only], fields: except)
162
- elsif cfg[:except]
163
- cfg[:except] = self.class.filter_subcfg(cfg[:except], fields: except, add: true)
164
- else
165
- cfg[:except] = except
166
- end
167
-
168
- cfg[:include] = self.class.filter_subcfg(cfg[:include], fields: except)
169
- cfg[:methods] = self.class.filter_subcfg(cfg[:methods], fields: except)
170
- cfg[:serializer_methods] = self.class.filter_subcfg(
171
- cfg[:serializer_methods], fields: except
172
- )
173
- end
174
- elsif only_param && only = @controller.request&.query_parameters&.[](only_param).presence
175
- if only = only.split(",").map(&:strip).map(&:to_sym).presence
176
- # Filter `only`, `include`, and `methods`. Adding anything to `except` is not needed,
177
- # because any configuration there takes precedence over `only`.
178
- if cfg[:only]
179
- cfg[:only] = self.class.filter_subcfg(cfg[:only], fields: only, only: true)
180
- else
181
- cfg[:only] = only
182
- end
183
-
184
- cfg[:include] = self.class.filter_subcfg(cfg[:include], fields: only, only: true)
185
- cfg[:methods] = self.class.filter_subcfg(cfg[:methods], fields: only, only: true)
186
- cfg[:serializer_methods] = self.class.filter_subcfg(
187
- cfg[:serializer_methods], fields: only, only: true
188
- )
189
- end
190
- end
4
+ require_relative "serializers/base_serializer"
191
5
 
192
- return cfg
193
- end
194
-
195
- # Get the associations limit from the controller.
196
- def _get_associations_limit
197
- return @_get_associations_limit if defined?(@_get_associations_limit)
198
-
199
- limit = @controller&.native_serializer_associations_limit
200
-
201
- # Extract the limit from the query parameters if it's set.
202
- if query_param = @controller&.native_serializer_associations_limit_query_param
203
- if @controller.request.query_parameters.key?(query_param)
204
- query_limit = @controller.request.query_parameters[query_param].to_i
205
- if query_limit > 0
206
- limit = query_limit
207
- else
208
- limit = nil
209
- end
210
- end
211
- end
212
-
213
- return @_get_associations_limit = limit
214
- end
215
-
216
- # Get a serializer configuration from the controller. `@controller` and `@model` must be set.
217
- def _get_controller_serializer_config(fields)
218
- columns = []
219
- includes = {}
220
- methods = []
221
- serializer_methods = {}
222
-
223
- column_names = @model.column_names
224
- reflections = @model.reflections
225
- attachment_reflections = @model.attachment_reflections
226
-
227
- fields.each do |f|
228
- field_config = @controller.class.get_field_config(f)
229
- next if field_config[:write_only]
230
-
231
- if f.in?(column_names)
232
- columns << f
233
- elsif ref = reflections[f]
234
- sub_columns = []
235
- sub_methods = []
236
- field_config[:sub_fields].each do |sf|
237
- if !ref.polymorphic? && sf.in?(ref.klass.column_names)
238
- sub_columns << sf
239
- else
240
- sub_methods << sf
241
- end
242
- end
243
- sub_config = {only: sub_columns, methods: sub_methods}
244
-
245
- # Apply certain rules regarding collection associations.
246
- if ref.collection?
247
- # If we need to limit the number of serialized association records, then dynamically add a
248
- # serializer method to do so.
249
- if limit = self._get_associations_limit
250
- serializer_methods[f] = f
251
- self.define_singleton_method(f) do |record|
252
- next record.send(f).limit(limit).as_json(**sub_config)
253
- end
254
- else
255
- includes[f] = sub_config
256
- end
257
-
258
- # If we need to include the association count, then add it here.
259
- if @controller.native_serializer_include_associations_count
260
- method_name = "#{f}.count"
261
- serializer_methods[method_name] = method_name
262
- self.define_singleton_method(method_name) do |record|
263
- next record.send(f).count
264
- end
265
- end
266
- else
267
- includes[f] = sub_config
268
- end
269
- elsif ref = reflections["rich_text_#{f}"]
270
- # ActionText Integration: Define rich text serializer method.
271
- serializer_methods[f] = f
272
- self.define_singleton_method(f) do |record|
273
- next record.send(f).to_s
274
- end
275
- elsif ref = attachment_reflections[f]
276
- # ActiveStorage Integration: Define attachment serializer method.
277
- serializer_methods[f] = f
278
- if ref.macro == :has_one_attached
279
- self.define_singleton_method(f) do |record|
280
- next record.send(f).attachment&.url
281
- end
282
- else
283
- self.define_singleton_method(f) do |record|
284
- next record.send(f).map(&:url)
285
- end
286
- end
287
- elsif @model.method_defined?(f)
288
- methods << f
289
- end
290
- end
291
-
292
- return {
293
- only: columns, include: includes, methods: methods, serializer_methods: serializer_methods
294
- }
295
- end
296
-
297
- # Get the raw serializer config, prior to any adjustments from the request.
298
- #
299
- # Use `deep_dup` on any class mutables (array, hash, etc) to avoid mutating class state.
300
- def get_raw_serializer_config
301
- # Return a locally defined serializer config if one is defined.
302
- if local_config = self.get_local_native_serializer_config
303
- return local_config.deep_dup
304
- end
305
-
306
- # Return a serializer config if one is defined on the controller.
307
- if serializer_config = self.get_controller_native_serializer_config
308
- return serializer_config.deep_dup
309
- end
310
-
311
- # If the config wasn't determined, build a serializer config from controller fields.
312
- if @model && fields = @controller&.get_fields
313
- return self._get_controller_serializer_config(fields.deep_dup)
314
- end
315
-
316
- # By default, pass an empty configuration, using the default Rails serializer.
317
- return {}
318
- end
319
-
320
- # Get a configuration passable to `serializable_hash` for the object, filtered if required.
321
- def get_serializer_config
322
- return filter_from_request(self.get_raw_serializer_config)
323
- end
324
-
325
- # Serialize a single record and merge results of `serializer_methods`.
326
- def _serialize(record, config, serializer_methods)
327
- # Ensure serializer_methods is either falsy, or a hash.
328
- if serializer_methods && !serializer_methods.is_a?(Hash)
329
- serializer_methods = [serializer_methods].flatten.map { |m| [m, m] }.to_h
330
- end
331
-
332
- # Merge serialized record with any serializer method results.
333
- return record.serializable_hash(config).merge(
334
- serializer_methods&.map { |m, k| [k.to_sym, self.send(m, record)] }.to_h,
335
- )
336
- end
337
-
338
- def serialize(*args)
339
- config = self.get_serializer_config
340
- serializer_methods = config.delete(:serializer_methods)
341
-
342
- if @object.respond_to?(:to_ary)
343
- return @object.map { |r| self._serialize(r, config, serializer_methods) }
344
- end
345
-
346
- return self._serialize(@object, config, serializer_methods)
347
- end
348
-
349
- # Allow a serializer instance to be used as a hash directly in a nested serializer config.
350
- def [](key)
351
- @_nested_config ||= self.get_serializer_config
352
- return @_nested_config[key]
353
- end
354
-
355
- def []=(key, value)
356
- @_nested_config ||= self.get_serializer_config
357
- return @_nested_config[key] = value
358
- end
359
-
360
- # Allow a serializer class to be used as a hash directly in a nested serializer config.
361
- def self.[](key)
362
- @_nested_config ||= self.new.get_serializer_config
363
- return @_nested_config[key]
364
- end
365
-
366
- def self.[]=(key, value)
367
- @_nested_config ||= self.new.get_serializer_config
368
- return @_nested_config[key] = value
369
- end
370
- end
371
-
372
- # This is a helper factory to wrap an ActiveModelSerializer to provide a `serialize` method which
373
- # accepts both collections and individual records. Use `.for` to build adapters.
374
- class RESTFramework::ActiveModelSerializerAdapterFactory
375
- def self.for(active_model_serializer)
376
- return Class.new(active_model_serializer) do
377
- def serialize
378
- if self.object.respond_to?(:to_ary)
379
- return self.object.map { |r| self.class.superclass.new(r).serializable_hash }
380
- end
381
-
382
- return self.serializable_hash
383
- end
384
- end
385
- end
386
- end
6
+ require_relative "serializers/active_model_serializer_adapter_factory"
7
+ require_relative "serializers/native_serializer"
@@ -179,11 +179,11 @@ module RESTFramework
179
179
  end
180
180
  end
181
181
 
182
- require_relative "rest_framework/controller_mixins"
183
182
  require_relative "rest_framework/engine"
184
183
  require_relative "rest_framework/errors"
185
184
  require_relative "rest_framework/filters"
186
185
  require_relative "rest_framework/generators"
186
+ require_relative "rest_framework/mixins"
187
187
  require_relative "rest_framework/paginators"
188
188
  require_relative "rest_framework/routers"
189
189
  require_relative "rest_framework/serializers"
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.9.7
4
+ version: 0.9.8
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: 2023-07-06 00:00:00.000000000 Z
11
+ date: 2023-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -31,6 +31,7 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
+ - ".yardopts"
34
35
  - LICENSE
35
36
  - README.md
36
37
  - VERSION
@@ -50,23 +51,28 @@ files:
50
51
  - app/views/rest_framework/routes_and_forms/_routes.html.erb
51
52
  - app/views/rest_framework/routes_and_forms/routes/_route.html.erb
52
53
  - lib/rest_framework.rb
53
- - lib/rest_framework/controller_mixins.rb
54
- - lib/rest_framework/controller_mixins/base.rb
55
- - lib/rest_framework/controller_mixins/bulk.rb
56
- - lib/rest_framework/controller_mixins/models.rb
57
54
  - lib/rest_framework/engine.rb
58
55
  - lib/rest_framework/errors.rb
59
56
  - lib/rest_framework/filters.rb
60
- - lib/rest_framework/filters/base.rb
61
- - lib/rest_framework/filters/model_ordering.rb
62
- - lib/rest_framework/filters/model_query.rb
63
- - lib/rest_framework/filters/model_search.rb
64
- - lib/rest_framework/filters/ransack.rb
57
+ - lib/rest_framework/filters/base_filter.rb
58
+ - lib/rest_framework/filters/model_ordering_filter.rb
59
+ - lib/rest_framework/filters/model_query_filter.rb
60
+ - lib/rest_framework/filters/model_search_filter.rb
61
+ - lib/rest_framework/filters/ransack_filter.rb
65
62
  - lib/rest_framework/generators.rb
66
63
  - lib/rest_framework/generators/controller_generator.rb
64
+ - lib/rest_framework/mixins.rb
65
+ - lib/rest_framework/mixins/base_controller_mixin.rb
66
+ - lib/rest_framework/mixins/bulk_model_controller_mixin.rb
67
+ - lib/rest_framework/mixins/model_controller_mixin.rb
67
68
  - lib/rest_framework/paginators.rb
69
+ - lib/rest_framework/paginators/base_paginator.rb
70
+ - lib/rest_framework/paginators/page_number_paginator.rb
68
71
  - lib/rest_framework/routers.rb
69
72
  - lib/rest_framework/serializers.rb
73
+ - lib/rest_framework/serializers/active_model_serializer_adapter_factory.rb
74
+ - lib/rest_framework/serializers/base_serializer.rb
75
+ - lib/rest_framework/serializers/native_serializer.rb
70
76
  - lib/rest_framework/utils.rb
71
77
  - lib/rest_framework/version.rb
72
78
  - vendor/assets/javascripts/rest_framework/external.min.js
@@ -83,7 +89,6 @@ post_install_message:
83
89
  rdoc_options: []
84
90
  require_paths:
85
91
  - lib
86
- - app
87
92
  required_ruby_version: !ruby/object:Gem::Requirement
88
93
  requirements:
89
94
  - - ">="
@@ -1,7 +0,0 @@
1
- module RESTFramework::ControllerMixins
2
- end
3
-
4
- require_relative "controller_mixins/base"
5
-
6
- require_relative "controller_mixins/bulk"
7
- require_relative "controller_mixins/models"