serega 0.8.0 → 0.8.2

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: b2ebcd147c1714ebb2b4e923ee179c788158b225ac76791817229a82975926ec
4
- data.tar.gz: eef2621b3d6b9ac6ee1da3de29d3006ff99a0c2c91082cdc7795e82665ee92e5
3
+ metadata.gz: 0d467016ac66fcd328919f3d7068dd556ac5a7768f657adb30948c2c316480a1
4
+ data.tar.gz: 3ae0c46dc6760cdf94299c0edc34b41a8b195cea92dff13b91a4017bb21653f7
5
5
  SHA512:
6
- metadata.gz: 7891e792121105b7ee566b926347a63d82fb9fae186b7cf97766d4450141538d81dbb3bf7ac571b2ff7ed12bf8567f35d3e769a68e8979e47aad5c1c99be0300
7
- data.tar.gz: 3dd972104c307eff809f5c41af005fb75fdd7caa6d6a494b90a8c63e89a0e25dc89de1547b762c6ad1f7ab3aa5e5f5811b470a6f20adaed55c7155c17fc00207
6
+ metadata.gz: 9f2a9f714be636a26f4ca2de8a5f692455080354f495dc5cc2d69dd874d207ddd0bc3286b47ea600ebf5a0ba2ddfd774d6876da67e82906d35319f4a54614caa
7
+ data.tar.gz: 766931dbb8aaa415dcc008a477258ba6f63ca35b664267278ee31ab0becc91f124e16eb1f57bd5968df03e333bb59c98a8396f796be84c254a76e674a472d6e1
data/README.md CHANGED
@@ -150,7 +150,7 @@ UserSerializer.as_json([user]) # => [{"username":"serega"}]
150
150
 
151
151
  Modifiers can be provided as Hash, Array, String, Symbol or their combinations.
152
152
 
153
- With plugin [string_modifiers][string_modifiers] we can provide modifiers as single `String` with attributes split by comma `,` and nested values inside brackets `()`, like: `username,enemies(username,email)`. This can be very useful to accept list of field in **GET** requests.
153
+ With plugin [string_modifiers][string_modifiers] we can provide modifiers as single `String` with attributes split by comma `,` and nested values inside brackets `()`, like: `username,enemies(username,email)`. This can be very useful to accept list of fields in **GET** requests.
154
154
 
155
155
  When provided non-existing attribute, `Serega::AttributeNotExist` error will be raised. This error can be muted with `check_initiate_params: false` parameter.
156
156
 
@@ -258,9 +258,9 @@ Allows to define `:preloads` to attributes and then allows to merge preloads
258
258
  from serialized attributes and return single associations hash.
259
259
 
260
260
  Plugin accepts options:
261
- - `auto_preload_attributes_with_delegate` - default false
262
- - `auto_preload_attributes_with_serializer` - default false
263
- - `auto_hide_attributes_with_preload` - default false
261
+ - `auto_preload_attributes_with_delegate` - default `false`
262
+ - `auto_preload_attributes_with_serializer` - default `false`
263
+ - `auto_hide_attributes_with_preload` - default `false`
264
264
 
265
265
  This options are very handy if you want to forget about finding preloads manually.
266
266
 
@@ -373,13 +373,12 @@ Added new `:batch` attribute option, example:
373
373
  attribute :name, batch: { key: :id, loader: :name_loader, default: '' }
374
374
  ```
375
375
 
376
- `:batch` option must be a hash with this keys:
377
- - :key (required) [Symbol, Proc, callable] - Defines identifier of current object
378
- - :loader (required) [Symbol, Proc, callable] - Defines how to fetch values for batch of keys. Accepts 3 parameters: keys, context, point.
379
- - :default (optional) - Default value used when loader does not return value for current key. By default it is `nil` or `[]` when attribute has additional option `many: true` (`attribute :name, many: true, batch: { ... }`).
376
+ Attribute `:batch` option must be a hash with this keys:
377
+ - `key` (required) [Symbol, Proc, callable] - Defines identifier of current object
378
+ - `loader` (required) [Symbol, Proc, callable] - Defines how to fetch values for batch of keys. Accepts 3 parameters: keys, context, point.
379
+ - `default` (optional) - Default value used when loader does not return value for current key. By default it is `nil` or `[]` when attribute has additional option `many: true` (ex: `attribute :name, many: true, batch: { ... }`).
380
380
 
381
- If `:loader` was defined via Symbol then batch loader must be defined using `config.batch_loaders.define(:loader_name) { ... }` method.
382
- Result of this block must be a Hash where keys are - provided keys, and values are - batch loaded values for according keys.
381
+ If `:loader` was defined using name (as Symbol) then batch loader must be defined using `config.batch_loaders.define(:loader_name) { ... }` method. Result of this block must be a Hash where keys are - provided keys, and values are - batch loaded values for according keys.
383
382
 
384
383
  Batch loader works well with [`activerecord_preloads`][activerecord_preloads] plugin.
385
384
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.0
1
+ 0.8.2
@@ -136,7 +136,9 @@ class Serega
136
136
 
137
137
  def keyword_block
138
138
  key_method_name = key
139
- proc { |object| object.public_send(key_method_name) }
139
+ proc do |object|
140
+ handle_no_method_error { object.public_send(key_method_name) }
141
+ end
140
142
  end
141
143
 
142
144
  def delegate_block
@@ -147,11 +149,25 @@ class Serega
147
149
  delegate_to = delegate[:to]
148
150
 
149
151
  if delegate[:allow_nil]
150
- proc { |object| object.public_send(delegate_to)&.public_send(key_method_name) }
152
+ proc do |object|
153
+ handle_no_method_error do
154
+ object.public_send(delegate_to)&.public_send(key_method_name)
155
+ end
156
+ end
151
157
  else
152
- proc { |object| object.public_send(delegate_to).public_send(key_method_name) }
158
+ proc do |object|
159
+ handle_no_method_error do
160
+ object.public_send(delegate_to).public_send(key_method_name)
161
+ end
162
+ end
153
163
  end
154
164
  end
165
+
166
+ def handle_no_method_error
167
+ yield
168
+ rescue NoMethodError => error
169
+ raise error, "NoMethodError when serializing '#{name}' attribute in #{self.class.serializer_class}\n\n#{error.message}", error.backtrace
170
+ end
155
171
  end
156
172
 
157
173
  extend Serega::SeregaHelpers::SerializerClassHelper
@@ -50,6 +50,7 @@ class Serega
50
50
  # @return [void]
51
51
  #
52
52
  def self.load_plugin(serializer_class, **_opts)
53
+ require_relative "./lib/batch_option_model"
53
54
  require_relative "./lib/loader"
54
55
  require_relative "./lib/loaders"
55
56
  require_relative "./lib/validations/check_batch_opt_key"
@@ -87,14 +88,19 @@ class Serega
87
88
  serializer_class.const_set(:SeregaBatchLoader, batch_loader_class)
88
89
 
89
90
  if serializer_class.plugin_used?(:activerecord_preloads)
90
- require_relative "./lib/plugins_extensions"
91
+ require_relative "./lib/plugins_extensions/activerecord_preloads"
91
92
  serializer_class::SeregaBatchLoader.include(PluginsExtensions::ActiveRecordPreloads::BatchLoaderInstanceMethods)
92
93
  end
93
94
 
94
95
  if serializer_class.plugin_used?(:formatters)
95
- require_relative "./lib/plugins_extensions"
96
+ require_relative "./lib/plugins_extensions/formatters"
96
97
  serializer_class::SeregaBatchLoader.include(PluginsExtensions::Formatters::BatchLoaderInstanceMethods)
97
98
  end
99
+
100
+ if serializer_class.plugin_used?(:preloads)
101
+ require_relative "./lib/plugins_extensions/preloads"
102
+ serializer_class::SeregaAttribute.include(PluginsExtensions::Preloads::AttributeInstanceMethods)
103
+ end
98
104
  end
99
105
 
100
106
  #
@@ -140,56 +146,6 @@ class Serega
140
146
  end
141
147
  end
142
148
 
143
- #
144
- # Stores batch config for specific attribute
145
- #
146
- class BatchModel
147
- attr_reader :opts, :loaders, :many
148
-
149
- #
150
- # Initializes batch model
151
- #
152
- # @param opts [Hash] Attribute :batch option
153
- # @param loaders [Array] Array of all loaders defined in serialize class
154
- # @param many [Boolean] Option :many, defined on attribute
155
- #
156
- # @return [void]
157
- def initialize(opts, loaders, many)
158
- @opts = opts
159
- @loaders = loaders
160
- @many = many
161
- end
162
-
163
- # Returns proc that will be used to batch load registered keys values
164
- # @return [#call] batch loader
165
- def loader
166
- @batch_loader ||= begin
167
- loader = opts[:loader]
168
- loader = loaders.fetch(loader) if loader.is_a?(Symbol)
169
- loader
170
- end
171
- end
172
-
173
- # Returns proc that will be used to find batch_key for current attribute.
174
- # @return [Object] key (uid) of batch loaded object
175
- def key
176
- @batch_key ||= begin
177
- key = opts[:key]
178
- key.is_a?(Symbol) ? proc { |object| object.public_send(key) } : key
179
- end
180
- end
181
-
182
- # Returns default value to use if batch loader does not return value for some key
183
- # @return [Object] default value for missing key
184
- def default_value
185
- if opts.key?(:default)
186
- opts[:default]
187
- elsif many
188
- FROZEN_EMPTY_ARRAY
189
- end
190
- end
191
- end
192
-
193
149
  #
194
150
  # Config class additional/patched instance methods
195
151
  #
@@ -263,16 +219,16 @@ class Serega
263
219
  #
264
220
  module MapPointInstanceMethods
265
221
  #
266
- # Returns BatchModel, an object that encapsulates all batch_loader methods for current point
222
+ # Returns BatchOptionModel, an object that combines options and methods needed to load batch
267
223
  #
268
- # @return [BatchModel] batch model that encapsulates everything needed to load current batch
224
+ # @return [BatchOptionModel] Class that combines options and methods needed to load batch.
269
225
  #
270
226
  def batch
271
227
  return @batch if instance_variable_defined?(:@batch)
272
228
 
273
229
  @batch = begin
274
230
  opts = attribute.batch
275
- BatchModel.new(opts, self.class.serializer_class.config.batch_loaders, many) if opts
231
+ BatchOptionModel.new(attribute) if opts
276
232
  end
277
233
  end
278
234
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ #
7
+ # Combines options and methods needed to load batch for specific attribute
8
+ #
9
+ class BatchOptionModel
10
+ attr_reader :attribute, :opts, :loaders, :many
11
+
12
+ #
13
+ # Initializes BatchOptionModel
14
+ #
15
+ # @param map_point [Serega::SeregaMapPoint] Map point for attribute with :batch option
16
+ # @param loaders [Array] Array of all loaders defined in serialize class
17
+ # @param many [Boolean] Option :many, defined on attribute
18
+ #
19
+ # @return [void]
20
+ def initialize(attribute)
21
+ @attribute = attribute
22
+ @opts = attribute.batch
23
+ @loaders = attribute.class.serializer_class.config.batch_loaders
24
+ @many = attribute.many
25
+ end
26
+
27
+ # Returns proc that will be used to batch load registered keys values
28
+ # @return [#call] batch loader
29
+ def loader
30
+ @batch_loader ||= begin
31
+ loader = opts[:loader]
32
+ loader = loaders.fetch(loader) if loader.is_a?(Symbol)
33
+ loader
34
+ end
35
+ end
36
+
37
+ # Returns proc that will be used to find batch_key for current attribute.
38
+ # @return [Object] key (uid) of batch loaded object
39
+ def key
40
+ @batch_key ||= begin
41
+ key = opts[:key]
42
+
43
+ if key.is_a?(Symbol)
44
+ proc do |object|
45
+ handle_no_method_error { object.public_send(key) }
46
+ end
47
+ else
48
+ key
49
+ end
50
+ end
51
+ end
52
+
53
+ # Returns default value to use if batch loader does not return value for some key
54
+ # @return [Object] default value for missing key
55
+ def default_value
56
+ if opts.key?(:default)
57
+ opts[:default]
58
+ elsif many
59
+ FROZEN_EMPTY_ARRAY
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def handle_no_method_error
66
+ yield
67
+ rescue NoMethodError => error
68
+ raise error, "NoMethodError when serializing '#{attribute.name}' attribute in #{attribute.class.serializer_class}\n\n#{error.message}", error.backtrace
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ #
7
+ # Extensions (mini-plugins) that are enabled when :batch plugin used with other plugins
8
+ #
9
+ module PluginsExtensions
10
+ #
11
+ # Extension that is used when :batch plugin used with :active_record_preloads plugin
12
+ #
13
+ module ActiveRecordPreloads
14
+ #
15
+ # BatchLoader additional/patched instance methods
16
+ #
17
+ # @see Serega::SeregaPlugins::Batch::SeregaBatchLoader
18
+ #
19
+ module BatchLoaderInstanceMethods
20
+ private
21
+
22
+ # Preloads required associations to batch-loaded records
23
+ def keys_values(*)
24
+ data = super
25
+
26
+ if point.has_nested_points?
27
+ associations = point.preloads
28
+ return data if associations.empty?
29
+
30
+ records = data.values
31
+ records.flatten!(1)
32
+
33
+ ActiverecordPreloads::Preloader.preload(records, associations)
34
+ end
35
+
36
+ data
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ #
7
+ # Extensions (mini-plugins) that are enabled when :batch plugin used with other plugins
8
+ #
9
+ module PluginsExtensions
10
+ #
11
+ # Extension that is used when batch plugin used with :formatters plugin
12
+ #
13
+ module Formatters
14
+ #
15
+ # BatchLoader additional/patched instance methods
16
+ #
17
+ # @see Serega::SeregaPlugins::Batch::SeregaBatchLoader
18
+ #
19
+ module BatchLoaderInstanceMethods
20
+ private
21
+
22
+ # Format values after they are prepared
23
+ def keys_values(*)
24
+ data = super
25
+
26
+ formatter = point.attribute.formatter
27
+ data.transform_values! { |value| formatter.call(value) } if formatter
28
+
29
+ data
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ #
7
+ # Extensions (mini-plugins) that are enabled when :batch plugin used with other plugins
8
+ #
9
+ module PluginsExtensions
10
+ #
11
+ # Extension that is used when :preloads plugin is loaded
12
+ #
13
+ module Preloads
14
+ #
15
+ # Attribute additional/patched instance methods
16
+ #
17
+ # @see Serega::SeregaPlugins::Preloads::AttributeInstanceMethods
18
+ #
19
+ module AttributeInstanceMethods
20
+ private
21
+
22
+ # Do not add any preloads automatically when batch option provided
23
+ def get_preloads
24
+ return if batch && !opts.key?(:preload)
25
+ super
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -39,9 +39,7 @@ class Serega
39
39
  end
40
40
 
41
41
  def check_modifiers
42
- Initiate::CheckModifiers.call(serializer_class, opts[:only])
43
- Initiate::CheckModifiers.call(serializer_class, opts[:except])
44
- Initiate::CheckModifiers.call(serializer_class, opts[:with])
42
+ Initiate::CheckModifiers.new.call(serializer_class, opts[:only], opts[:with], opts[:except])
45
43
  end
46
44
 
47
45
  def serializer_class
@@ -10,55 +10,72 @@ class Serega
10
10
  # Modifiers validation
11
11
  #
12
12
  class CheckModifiers
13
- class << self
14
- # Validates provided fields names are existing attributes
15
- #
16
- # @param serializer_class [Serega]
17
- # @param fields [Hash] validated fields
18
- #
19
- # @raise [Serega::AttributeNotExist] when modifier not exist as attribute
20
- #
21
- # @return [void]
22
- #
23
- def call(serializer_class, fields)
24
- return unless fields
13
+ #
14
+ # Validates provided fields names are existing attributes
15
+ #
16
+ # @param serializer_class [Serega]
17
+ # @param only [Hash, nil] `only` modifier
18
+ # @param with [Hash, nil] `with` modifier
19
+ # @param except [Hash, nil] `except` modifier
20
+ #
21
+ # @raise [Serega::AttributeNotExist] when some checked modifier has not existing attribute
22
+ #
23
+ # @return [void]
24
+ #
25
+ def call(serializer_class, only, with, except)
26
+ validate(serializer_class, only) if only
27
+ validate(serializer_class, with) if with
28
+ validate(serializer_class, except) if except
25
29
 
26
- validate(serializer_class, fields, [])
27
- end
30
+ raise_errors if any_error?
31
+ end
28
32
 
29
- private
33
+ private
30
34
 
31
- def validate(serializer_class, fields, prev_names)
32
- fields.each do |name, nested_fields|
33
- attribute = serializer_class.attributes[name]
35
+ def validate(serializer_class, fields)
36
+ fields.each do |name, nested_fields|
37
+ attribute = serializer_class && serializer_class.attributes[name]
38
+
39
+ # Save error when no attribute with checked name exists
40
+ unless attribute
41
+ save_error(name)
42
+ next
43
+ end
34
44
 
35
- raise_error(name, prev_names) unless attribute
36
- next if nested_fields.empty?
45
+ # Return when attribute has no nested fields
46
+ next if nested_fields.empty?
37
47
 
38
- raise_nested_error(name, prev_names, nested_fields) unless attribute.relation?
39
- nested_serializer = attribute.serializer
40
- validate(nested_serializer, nested_fields, prev_names + [name])
48
+ with_parent_name(name) do
49
+ validate(attribute.serializer, nested_fields)
41
50
  end
42
51
  end
52
+ end
43
53
 
44
- def raise_error(name, prev_names)
45
- field_name = field_name(name, prev_names)
54
+ def parents_names
55
+ @parents_names ||= []
56
+ end
46
57
 
47
- raise Serega::AttributeNotExist, "Attribute #{field_name} not exists"
48
- end
58
+ def with_parent_name(name)
59
+ parents_names << name
60
+ yield
61
+ parents_names.pop
62
+ end
49
63
 
50
- def raise_nested_error(name, prev_names, nested_fields)
51
- field_name = field_name(name, prev_names)
52
- first_nested = nested_fields.keys.first
64
+ def error_attributes
65
+ @error_attributes ||= []
66
+ end
53
67
 
54
- raise Serega::AttributeNotExist, "Attribute #{field_name} has no :serializer option specified to add nested '#{first_nested}' attribute"
55
- end
68
+ def save_error(name)
69
+ full_attribute_name = [*parents_names, name].join(".")
70
+ error_attributes << full_attribute_name
71
+ end
56
72
 
57
- def field_name(name, prev_names)
58
- res = "'#{name}'"
59
- res += " ('#{prev_names.join(".")}.#{name}')" if prev_names.any?
60
- res
61
- end
73
+ def raise_errors
74
+ raise Serega::AttributeNotExist, "Not existing attributes: #{error_attributes.join(", ")}"
75
+ end
76
+
77
+ def any_error?
78
+ defined?(@error_attributes)
62
79
  end
63
80
  end
64
81
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serega
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Glushkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-08 00:00:00.000000000 Z
11
+ date: 2022-12-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  JSON Serializer
@@ -42,9 +42,12 @@ files:
42
42
  - lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb
43
43
  - lib/serega/plugins/activerecord_preloads/lib/preloader.rb
44
44
  - lib/serega/plugins/batch/batch.rb
45
+ - lib/serega/plugins/batch/lib/batch_option_model.rb
45
46
  - lib/serega/plugins/batch/lib/loader.rb
46
47
  - lib/serega/plugins/batch/lib/loaders.rb
47
- - lib/serega/plugins/batch/lib/plugins_extensions.rb
48
+ - lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb
49
+ - lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb
50
+ - lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb
48
51
  - lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb
49
52
  - lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb
50
53
  - lib/serega/plugins/batch/lib/validations/check_opt_batch.rb
@@ -93,8 +96,8 @@ homepage: https://github.com/aglushkov/serega
93
96
  licenses:
94
97
  - MIT
95
98
  metadata:
96
- homepage_uri: https://github.com/aglushkov/serega
97
99
  source_code_uri: https://github.com/aglushkov/serega
100
+ documentation_uri: https://www.rubydoc.info/gems/serega
98
101
  changelog_uri: https://github.com/aglushkov/serega/blob/master/CHANGELOG.md
99
102
  post_install_message:
100
103
  rdoc_options: []
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- module SeregaPlugins
5
- module Batch
6
- # Extensions (mini-plugins) that are enabled when Batch plugin used with other plugins
7
- module PluginsExtensions
8
- #
9
- # Extension that is used when batch plugin used with :active_record_preloads plugin
10
- #
11
- module ActiveRecordPreloads
12
- #
13
- # BatchLoader additional/patched instance methods
14
- #
15
- # @see Serega::SeregaPlugins::Batch::SeregaBatchLoader
16
- #
17
- module BatchLoaderInstanceMethods
18
- private
19
-
20
- # Preloads required associations to batch-loaded records
21
- def keys_values(*)
22
- data = super
23
-
24
- if point.has_nested_points?
25
- associations = point.preloads
26
- return data if associations.empty?
27
-
28
- ActiverecordPreloads::Preloader.preload(data.values.flatten(1), associations)
29
- end
30
-
31
- data
32
- end
33
- end
34
- end
35
-
36
- #
37
- # Extension that is used when batch plugin used with :formatters plugin
38
- #
39
- module Formatters
40
- #
41
- # BatchLoader additional/patched instance methods
42
- #
43
- # @see Serega::SeregaPlugins::Batch::SeregaBatchLoader
44
- #
45
- module BatchLoaderInstanceMethods
46
- private
47
-
48
- # Format values after they are prepared
49
- def keys_values(*)
50
- data = super
51
-
52
- formatter = point.attribute.formatter
53
- data.transform_values! { |value| formatter.call(value) } if formatter
54
-
55
- data
56
- end
57
- end
58
- end
59
- end
60
- end
61
- end
62
- end