rest_framework 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"