serega 0.6.1 → 0.7.0

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/serega/attribute.rb +2 -2
  4. data/lib/serega/config.rb +23 -1
  5. data/lib/serega/errors.rb +8 -5
  6. data/lib/serega/helpers/serializer_class_helper.rb +5 -0
  7. data/lib/serega/json/adapter.rb +6 -0
  8. data/lib/serega/json/json.rb +20 -0
  9. data/lib/serega/json/oj.rb +20 -0
  10. data/lib/serega/map.rb +17 -0
  11. data/lib/serega/map_point.rb +36 -1
  12. data/lib/serega/object_serializer.rb +17 -4
  13. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +66 -18
  14. data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +100 -40
  15. data/lib/serega/plugins/batch/batch.rb +136 -10
  16. data/lib/serega/plugins/batch/lib/loader.rb +33 -1
  17. data/lib/serega/plugins/batch/lib/loaders.rb +15 -2
  18. data/lib/serega/plugins/batch/lib/plugins_extensions.rb +23 -1
  19. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb +12 -0
  20. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +12 -0
  21. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +12 -0
  22. data/lib/serega/plugins/context_metadata/context_metadata.rb +95 -13
  23. data/lib/serega/plugins/formatters/formatters.rb +94 -8
  24. data/lib/serega/plugins/hide_nil/hide_nil.rb +33 -7
  25. data/lib/serega/plugins/metadata/metadata.rb +108 -35
  26. data/lib/serega/plugins/metadata/validations/check_block.rb +3 -0
  27. data/lib/serega/plugins/metadata/validations/check_opt_hide_empty.rb +4 -1
  28. data/lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb +4 -1
  29. data/lib/serega/plugins/metadata/validations/check_opts.rb +3 -0
  30. data/lib/serega/plugins/metadata/validations/check_path.rb +3 -0
  31. data/lib/serega/plugins/preloads/lib/enum_deep_freeze.rb +10 -1
  32. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +13 -4
  33. data/lib/serega/plugins/preloads/lib/main_preload_path.rb +16 -3
  34. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +4 -6
  35. data/lib/serega/plugins/preloads/preloads.rb +145 -12
  36. data/lib/serega/plugins/preloads/validations/check_opt_preload.rb +11 -0
  37. data/lib/serega/plugins/preloads/validations/check_opt_preload_path.rb +12 -0
  38. data/lib/serega/plugins/presenter/presenter.rb +20 -11
  39. data/lib/serega/plugins/root/root.rb +131 -19
  40. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +42 -12
  41. data/lib/serega/plugins/string_modifiers/string_modifiers.rb +14 -0
  42. data/lib/serega/plugins.rb +7 -1
  43. data/lib/serega/utils/enum_deep_dup.rb +5 -0
  44. data/lib/serega/utils/to_hash.rb +21 -3
  45. data/lib/serega/validations/attribute/check_block.rb +7 -2
  46. data/lib/serega/validations/attribute/check_name.rb +6 -0
  47. data/lib/serega/validations/attribute/check_opt_const.rb +12 -9
  48. data/lib/serega/validations/attribute/check_opt_delegate.rb +13 -10
  49. data/lib/serega/validations/attribute/check_opt_hide.rb +3 -0
  50. data/lib/serega/validations/attribute/check_opt_key.rb +12 -9
  51. data/lib/serega/validations/attribute/check_opt_many.rb +3 -0
  52. data/lib/serega/validations/attribute/check_opt_serializer.rb +3 -0
  53. data/lib/serega/validations/attribute/check_opt_value.rb +12 -9
  54. data/lib/serega/validations/check_attribute_params.rb +29 -1
  55. data/lib/serega/validations/check_initiate_params.rb +17 -0
  56. data/lib/serega/validations/check_serialize_params.rb +16 -0
  57. data/lib/serega/validations/initiate/check_modifiers.rb +15 -0
  58. data/lib/serega/validations/utils/check_allowed_keys.rb +14 -0
  59. data/lib/serega/validations/utils/check_opt_is_bool.rb +11 -0
  60. data/lib/serega/validations/utils/check_opt_is_hash.rb +11 -0
  61. data/lib/serega/validations/utils/check_opt_is_string_or_symbol.rb +11 -0
  62. data/lib/serega/version.rb +4 -0
  63. data/lib/serega.rb +83 -24
  64. metadata +2 -3
  65. data/lib/serega/serializer.rb +0 -32
@@ -3,9 +3,18 @@
3
3
  class Serega
4
4
  module SeregaPlugins
5
5
  module Preloads
6
- # Freezes nested enumerable data
6
+ #
7
+ # Utility to freeze nested hashes and arrays
8
+ #
7
9
  class EnumDeepFreeze
8
10
  class << self
11
+ #
12
+ # Freezes nested enumerable data
13
+ #
14
+ # @param data[Hash, Array] data to freeze
15
+ #
16
+ # @return [Hash, Array] same deeply frozen data
17
+ #
9
18
  def call(data)
10
19
  data.each_entry { |entry| call(entry) } if data.is_a?(Enumerable)
11
20
  data.freeze
@@ -3,7 +3,9 @@
3
3
  class Serega
4
4
  module SeregaPlugins
5
5
  module Preloads
6
- # Transforms user provided preloads to hash
6
+ #
7
+ # Utility that helps to transform user provided preloads to hash
8
+ #
7
9
  class FormatUserPreloads
8
10
  METHODS = {
9
11
  Array => :array_to_hash,
@@ -14,7 +16,16 @@ class Serega
14
16
  Symbol => :symbol_to_hash
15
17
  }.freeze
16
18
 
17
- module ClassMethods
19
+ private_constant :METHODS
20
+
21
+ class << self
22
+ #
23
+ # Transforms user provided preloads to hash
24
+ #
25
+ # @param value [Array,Hash,String,Symbol,nil,false] preloads
26
+ #
27
+ # @return [Hash] preloads transformed to hash
28
+ #
18
29
  def call(value)
19
30
  send(METHODS.fetch(value.class), value)
20
31
  end
@@ -45,8 +56,6 @@ class Serega
45
56
  {value => {}}
46
57
  end
47
58
  end
48
-
49
- extend ClassMethods
50
59
  end
51
60
  end
52
61
  end
@@ -3,9 +3,24 @@
3
3
  class Serega
4
4
  module SeregaPlugins
5
5
  module Preloads
6
+ #
7
+ # Class that constructs main preloads path.
8
+ #
9
+ # When we have nested preloads we will use this path to dig to `main` element and
10
+ # assign nested preloads to it.
11
+ #
12
+ # By default its a path to latest provided preload
13
+ #
14
+ # @example
15
+ # MainPreloadPath.(a: { b: { c: {} }, d: {} }) # => [:a, :d]
16
+ #
6
17
  class MainPreloadPath
7
- module ClassMethods
18
+ class << self
19
+ # Finds default preload path
20
+ #
8
21
  # @param preloads [Hash] Formatted user provided preloads hash
22
+ #
23
+ # @return [Array<Symbol>] Preloads path to `main` element
9
24
  def call(preloads)
10
25
  return FROZEN_EMPTY_ARRAY if preloads.empty?
11
26
 
@@ -32,8 +47,6 @@ class Serega
32
47
  path
33
48
  end
34
49
  end
35
-
36
- extend ClassMethods
37
50
  end
38
51
  end
39
52
  end
@@ -4,14 +4,14 @@ class Serega
4
4
  module SeregaPlugins
5
5
  module Preloads
6
6
  #
7
- # Finds relations to preload for provided serializer
7
+ # Finds preloads for provided attributes map
8
8
  #
9
9
  class PreloadsConstructor
10
- module ClassMethods
10
+ class << self
11
11
  #
12
- # Constructs preloads hash for given serializer
12
+ # Constructs preloads hash for given attributes map
13
13
  #
14
- # @param serializer [Serega] Instance of Serega serializer
14
+ # @param map [Array<Serega::MapPoint>] Serialization map
15
15
  #
16
16
  # @return [Hash]
17
17
  #
@@ -54,8 +54,6 @@ class Serega
54
54
  (!path || path.empty?) ? preloads : preloads.dig(*path)
55
55
  end
56
56
  end
57
-
58
- extend ClassMethods
59
57
  end
60
58
  end
61
59
  end
@@ -3,8 +3,59 @@
3
3
  class Serega
4
4
  module SeregaPlugins
5
5
  #
6
- # Plugin adds `.preloads` method to find relations that must be preloaded
6
+ # Plugin `:preloads`
7
7
  #
8
+ # Allows to define `:preloads` to attributes and then allows to merge preloads
9
+ # from serialized attributes and return single associations hash.
10
+ #
11
+ # Plugin accepts options:
12
+ # - `auto_preload_attributes_with_delegate` - default false
13
+ # - `auto_preload_attributes_with_serializer` - default false
14
+ # - `auto_hide_attributes_with_preload` - default false
15
+ #
16
+ # This options are very handy if you want to forget about finding preloads manually.
17
+ #
18
+ # Preloads can be disabled with `preload: false` attribute option option.
19
+ # Also automatically added preloads can be overwritten with manually specified `preload: :another_value`.
20
+ #
21
+ # Some examples, **please read comments in the code below**
22
+ #
23
+ # @example
24
+ # class AppSerializer < Serega
25
+ # plugin :preloads,
26
+ # auto_preload_attributes_with_delegate: true,
27
+ # auto_preload_attributes_with_serializer: true,
28
+ # auto_hide_attributes_with_preload: true
29
+ # end
30
+ #
31
+ # class UserSerializer < AppSerializer
32
+ # # No preloads
33
+ # attribute :username
34
+ #
35
+ # # Specify `preload: :user_stats` manually
36
+ # attribute :followers_count, preload: :user_stats, value: proc { |user| user.user_stats.followers_count }
37
+ #
38
+ # # Automatically `preloads: :user_stats` as `auto_preload_attributes_with_delegate` option is true
39
+ # attribute :comments_count, delegate: { to: :user_stats }
40
+ #
41
+ # # Automatically `preloads: :albums` as `auto_preload_attributes_with_serializer` option is true
42
+ # attribute :albums, serializer: 'AlbumSerializer'
43
+ # end
44
+ #
45
+ # class AlbumSerializer < AppSerializer
46
+ # attribute :images_count, delegate: { to: :album_stats }
47
+ # end
48
+ #
49
+ # # By default preloads are empty, as we specify `auto_hide_attributes_with_preload = true`,
50
+ # # and attributes with preloads will be not serialized
51
+ # UserSerializer.new.preloads # => {}
52
+ # UserSerializer.new.to_h(OpenStruct.new(username: 'foo')) # => {:username=>"foo"}
53
+ #
54
+ # UserSerializer.new(with: :followers_count).preloads # => {:user_stats=>{}}
55
+ # UserSerializer.new(with: %i[followers_count comments_count]).preloads # => {:user_stats=>{}}
56
+ # UserSerializer.new(with: [:followers_count, :comments_count, { albums: :images_count }]).preloads # => {:user_stats=>{}, :albums=>{:album_stats=>{}}}
57
+ #
58
+
8
59
  module Preloads
9
60
  DEFAULT_CONFIG = {
10
61
  auto_preload_attributes_with_delegate: false,
@@ -12,24 +63,26 @@ class Serega
12
63
  auto_hide_attributes_with_preload: false
13
64
  }.freeze
14
65
 
15
- # @return [Symbol] plugin name
66
+ private_constant :DEFAULT_CONFIG
67
+
68
+ # @return [Symbol] Plugin name
16
69
  def self.plugin_name
17
70
  :preloads
18
71
  end
19
72
 
20
73
  #
21
- # Includes plugin modules to current serializer
74
+ # Applies plugin code to specific serializer
22
75
  #
23
- # @param serializer_class [Class] current serializer class
24
- # @param _opts [Hash] plugin opts
76
+ # @param serializer_class [Class<Serega>] Current serializer class
77
+ # @param _opts [Hash] Loaded plugins options
25
78
  #
26
79
  # @return [void]
27
80
  #
28
81
  def self.load_plugin(serializer_class, **_opts)
29
82
  serializer_class.include(InstanceMethods)
30
- serializer_class::SeregaAttribute.include(AttributeMethods)
83
+ serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
31
84
  serializer_class::SeregaConfig.include(ConfigInstanceMethods)
32
- serializer_class::SeregaMapPoint.include(MapPointMethods)
85
+ serializer_class::SeregaMapPoint.include(MapPointInstanceMethods)
33
86
 
34
87
  serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
35
88
 
@@ -41,6 +94,14 @@ class Serega
41
94
  require_relative "./validations/check_opt_preload_path"
42
95
  end
43
96
 
97
+ #
98
+ # Adds config options and runs other callbacks after plugin was loaded
99
+ #
100
+ # @param serializer_class [Class<Serega>] Current serializer class
101
+ # @param opts [Hash] loaded plugins opts
102
+ #
103
+ # @return [void]
104
+ #
44
105
  def self.after_load_plugin(serializer_class, **opts)
45
106
  config = serializer_class.config
46
107
  config.attribute_keys << :preload << :preload_path
@@ -53,21 +114,57 @@ class Serega
53
114
  preloads_config.auto_hide_attributes_with_preload = preloads_opts[:auto_hide_attributes_with_preload]
54
115
  end
55
116
 
56
- # Adds #preloads instance method
117
+ #
118
+ # Serega additional/patched instance methods
119
+ #
120
+ # @see Serega
121
+ #
57
122
  module InstanceMethods
58
- # @return [Hash] relations that can be preloaded to omit N+1
123
+ # @return [Hash] merged preloads of all serialized attributes
59
124
  def preloads
60
125
  @preloads ||= PreloadsConstructor.call(map)
61
126
  end
62
127
  end
63
128
 
129
+ #
130
+ # Config for `preloads` plugin
131
+ #
64
132
  class PreloadsConfig
133
+ # @return [Hash] preloads plugin options
65
134
  attr_reader :opts
66
135
 
136
+ #
137
+ # Initializes context_metadata config object
138
+ #
139
+ # @param opts [Hash] options
140
+ #
141
+ # @return [Serega::SeregaPlugins::Metadata::MetadataConfig]
142
+ #
67
143
  def initialize(opts)
68
144
  @opts = opts
69
145
  end
70
146
 
147
+ # @!method auto_preload_attributes_with_delegate
148
+ # @return [Boolean, nil] option value
149
+ #
150
+ # @!method auto_preload_attributes_with_delegate=(value)
151
+ # @param value [Boolean] New option value
152
+ # @return [Boolean] New option value
153
+ #
154
+ # @!method auto_preload_attributes_with_serializer
155
+ # @return [Boolean, nil] option value
156
+ #
157
+ # @!method auto_preload_attributes_with_serializer=(value)
158
+ # @param value [Boolean] New option value
159
+ # @return [Boolean] New option value
160
+ #
161
+ # @!method auto_hide_attributes_with_preload
162
+ # @return [Boolean, nil] option value
163
+ #
164
+ # @!method auto_hide_attributes_with_preload=(value)
165
+ # @param value [Boolean] New option value
166
+ # @return [Boolean] New option value
167
+ #
71
168
  %i[
72
169
  auto_preload_attributes_with_delegate
73
170
  auto_preload_attributes_with_serializer
@@ -84,26 +181,46 @@ class Serega
84
181
  end
85
182
  end
86
183
 
184
+ #
185
+ # Config class additional/patched instance methods
186
+ #
187
+ # @see Serega::SeregaConfig
188
+ #
87
189
  module ConfigInstanceMethods
190
+ # @return [Serega::SeregaPlugins::Preloads::PreloadsConfig] `preloads` plugin config
88
191
  def preloads
89
192
  @preloads ||= PreloadsConfig.new(opts.fetch(:preloads))
90
193
  end
91
194
  end
92
195
 
93
- # Adds #preloads and #preloads_path Attribute instance method
94
- module AttributeMethods
196
+ #
197
+ # Serega::SeregaAttribute additional/patched instance methods
198
+ #
199
+ # @see Serega::SeregaAttribute::AttributeInstanceMethods
200
+ #
201
+ module AttributeInstanceMethods
202
+ # @return [Hash,nil] formatted preloads of current attribute
95
203
  def preloads
96
204
  return @preloads if defined?(@preloads)
97
205
 
98
206
  @preloads = get_preloads
99
207
  end
100
208
 
209
+ # @return [Array] formatted preloads_path of current attribute
101
210
  def preloads_path
102
211
  return @preloads_path if defined?(@preloads_path)
103
212
 
104
213
  @preloads_path = get_preloads_path
105
214
  end
106
215
 
216
+ # Patch for original `hide` method
217
+ #
218
+ # Marks attribute hidden if auto_hide_attribute_with_preloads option was set and attribute has preloads
219
+ #
220
+ # @return [Boolean, nil] if attribute is hidden
221
+ #
222
+ # @see Serega::SeregaAttribute::AttributeInstanceMethods#hide
223
+ #
107
224
  def hide
108
225
  res = super
109
226
  return res unless res.nil?
@@ -143,16 +260,32 @@ class Serega
143
260
  end
144
261
  end
145
262
 
146
- module MapPointMethods
263
+ #
264
+ # Serega::SeregaMapPoint additional/patched instance methods
265
+ #
266
+ # @see Serega::SeregaMapPoint::InstanceMethods
267
+ #
268
+ module MapPointInstanceMethods
269
+ #
270
+ # @return [Hash] preloads for nested attributes
271
+ #
147
272
  def preloads
148
273
  @preloads ||= PreloadsConstructor.call(nested_points)
149
274
  end
150
275
 
276
+ #
277
+ # @return [Array<Symbol>] preloads path for current attribute
278
+ #
151
279
  def preloads_path
152
280
  attribute.preloads_path
153
281
  end
154
282
  end
155
283
 
284
+ #
285
+ # Serega::SeregaValidations::CheckAttributeParams additional/patched class methods
286
+ #
287
+ # @see Serega::SeregaValidations::CheckAttributeParams
288
+ #
156
289
  module CheckAttributeParamsInstanceMethods
157
290
  private
158
291
 
@@ -3,8 +3,19 @@
3
3
  class Serega
4
4
  module SeregaPlugins
5
5
  module Preloads
6
+ #
7
+ # Validator for attribute :preload option
8
+ #
6
9
  class CheckOptPreload
7
10
  class << self
11
+ #
12
+ # Checks :preload option
13
+ #
14
+ # @param opts [Hash] Attribute options
15
+ #
16
+ # @raise [SeregaError] validation error
17
+ #
18
+ # @return [void]
8
19
  def call(opts)
9
20
  return unless opts.key?(:preload)
10
21
 
@@ -3,8 +3,20 @@
3
3
  class Serega
4
4
  module SeregaPlugins
5
5
  module Preloads
6
+ #
7
+ # Validator for attribute :preload_path option
8
+ #
6
9
  class CheckOptPreloadPath
7
10
  class << self
11
+ #
12
+ # Checks preload_path option
13
+ #
14
+ # @param opts [Hash] Attribute options
15
+ #
16
+ # @raise [SeregaError] validation error
17
+ #
18
+ # @return [void]
19
+ #
8
20
  def call(opts)
9
21
  return unless opts.key?(:preload_path)
10
22
 
@@ -20,13 +20,13 @@ class Serega
20
20
  # end
21
21
  # end
22
22
  module Presenter
23
- # @return [Symbol] plugin name
23
+ # @return [Symbol] Plugin name
24
24
  def self.plugin_name
25
25
  :presenter
26
26
  end
27
27
 
28
28
  #
29
- # Loads plugin
29
+ # Applies plugin code to specific serializer
30
30
  #
31
31
  # @param serializer_class [Class<Serega>] Current serializer class
32
32
  # @param _opts [Hash] Loaded plugins options
@@ -35,14 +35,14 @@ class Serega
35
35
  #
36
36
  def self.load_plugin(serializer_class, **_opts)
37
37
  serializer_class.extend(ClassMethods)
38
- serializer_class::SeregaSerializer.include(SeregaSerializerInstanceMethods)
38
+ serializer_class::SeregaObjectSerializer.include(SeregaObjectSerializerInstanceMethods)
39
39
  end
40
40
 
41
41
  #
42
- # Adds Presenter to current serializer
42
+ # Runs callbacks after plugin was attached
43
43
  #
44
44
  # @param serializer_class [Class<Serega>] Current serializer class
45
- # @param _opts [Hash] Loaded plugins options
45
+ # @param _opts [Hash] loaded plugins opts
46
46
  #
47
47
  # @return [void]
48
48
  #
@@ -74,7 +74,11 @@ class Serega
74
74
  include InstanceMethods
75
75
  end
76
76
 
77
- # Overrides class methods of included class
77
+ #
78
+ # Serega additional/patched class methods
79
+ #
80
+ # @see Serega
81
+ #
78
82
  module ClassMethods
79
83
  private def inherited(subclass)
80
84
  super
@@ -94,14 +98,19 @@ class Serega
94
98
  end
95
99
  end
96
100
 
97
- # Includes methods to override SeregaSerializer class
98
- module SeregaSerializerInstanceMethods
101
+ #
102
+ # SeregaObjectSerializer additional/patched class methods
103
+ #
104
+ # @see Serega::SeregaObjectSerializer
105
+ #
106
+ module SeregaObjectSerializerInstanceMethods
107
+ private
108
+
99
109
  #
100
110
  # Replaces serialized object with Presenter.new(object)
101
111
  #
102
- def serialize(object)
103
- presenter_class = points.first.class.serializer_class::Presenter
104
- object = presenter_class.new(object)
112
+ def serialize_object(object)
113
+ object = self.class.serializer_class::Presenter.new(object)
105
114
  super
106
115
  end
107
116
  end