serega 0.10.0 → 0.11.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +319 -141
  3. data/VERSION +1 -1
  4. data/lib/serega/attribute.rb +37 -105
  5. data/lib/serega/attribute_normalizer.rb +176 -0
  6. data/lib/serega/config.rb +26 -9
  7. data/lib/serega/object_serializer.rb +11 -10
  8. data/lib/serega/plan.rb +128 -0
  9. data/lib/serega/plan_point.rb +94 -0
  10. data/lib/serega/plugins/batch/batch.rb +187 -41
  11. data/lib/serega/plugins/batch/lib/loader.rb +4 -4
  12. data/lib/serega/plugins/batch/lib/loaders.rb +3 -3
  13. data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +2 -2
  14. data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +19 -1
  15. data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +6 -4
  16. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +9 -5
  17. data/lib/serega/plugins/formatters/formatters.rb +28 -31
  18. data/lib/serega/plugins/if/if.rb +24 -6
  19. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +11 -13
  20. data/lib/serega/plugins/preloads/lib/main_preload_path.rb +2 -2
  21. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +21 -15
  22. data/lib/serega/plugins/preloads/preloads.rb +72 -40
  23. data/lib/serega/plugins/presenter/presenter.rb +0 -9
  24. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +11 -1
  25. data/lib/serega/plugins.rb +3 -3
  26. data/lib/serega/utils/enum_deep_dup.rb +18 -18
  27. data/lib/serega/utils/enum_deep_freeze.rb +35 -0
  28. data/lib/serega/utils/to_hash.rb +17 -9
  29. data/lib/serega.rb +22 -15
  30. metadata +6 -6
  31. data/lib/serega/map.rb +0 -91
  32. data/lib/serega/map_point.rb +0 -66
  33. data/lib/serega/plugins/batch/lib/batch_option_model.rb +0 -73
  34. data/lib/serega/plugins/preloads/lib/enum_deep_freeze.rb +0 -26
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.0
1
+ 0.11.0
@@ -9,98 +9,62 @@ class Serega
9
9
  # Attribute instance methods
10
10
  #
11
11
  module AttributeInstanceMethods
12
- # Returns attribute name
12
+ # Attribute initial params
13
+ # @return [Hash] Attribute initial params
14
+ attr_reader :initials
15
+
16
+ # Attribute name
13
17
  # @return [Symbol] Attribute name
14
18
  attr_reader :name
15
19
 
16
- # Returns attribute options
17
- # @return [Hash] Attribute options
18
- attr_reader :opts
20
+ # Attribute :many option
21
+ # @return [Boolean, nil] Attribute :many option
22
+ attr_reader :many
19
23
 
20
- # Returns attribute block
21
- # @return [Proc] Attribute originally added block
22
- attr_reader :block
24
+ # Attribute :hide option
25
+ # @return [Boolean nil] Attribute :hide option
26
+ attr_reader :hide
23
27
 
24
28
  #
25
29
  # Initializes new attribute
26
30
  #
27
31
  # @param name [Symbol, String] Name of attribute
28
- #
29
32
  # @param opts [Hash] Attribute options
30
- # @option opts [Symbol] :key Object instance method name to get attribute value
31
- # @option opts [Boolean] :exposed Configures if we should serialize this attribute by default.
32
- # (by default is true for regular attributes and false for relationships)
33
+ # @option opts [Symbol] :key Object method name to fetch attribute value
34
+ # @option opts [Hash] :delegate Allows to fetch value from nested object
35
+ # @option opts [Boolean] :hide Specify `true` to not serialize this attribute by default
33
36
  # @option opts [Boolean] :many Specifies has_many relationship. By default is detected via object.is_a?(Enumerable)
34
- # @option opts [Serega, Proc] :serializer Relationship serializer class. Use `proc { MySerializer }` if serializers have cross references.
35
- #
36
- # @param block [Proc] Proc that receives object and context and finds attribute value
37
+ # @option opts [Proc, #call] :value Custom block or callable to find attribute value
38
+ # @option opts [Serega, Proc] :serializer Relationship serializer class. Use `proc { MySerializer }` if serializers have cross references
39
+ # @param block [Proc] Custom block to find attribute value
37
40
  #
38
41
  def initialize(name:, opts: {}, block: nil)
39
- self.class.serializer_class::CheckAttributeParams.new(name, opts, block).validate
40
-
41
- @name = name.to_sym
42
- @opts = SeregaUtils::EnumDeepDup.call(opts)
43
- @block = block
44
- end
42
+ serializer_class = self.class.serializer_class
43
+ serializer_class::CheckAttributeParams.new(name, opts, block).validate
45
44
 
46
- # Method name that will be used to get attribute value
47
- #
48
- # @return [Symbol] key
49
- def key
50
- @key ||= opts.key?(:key) ? opts[:key].to_sym : name
51
- end
52
-
53
- # Shows current opts[:hide] option
54
- #
55
- # Patched in:
56
- # - plugin :preloads (returns true by default if config option auto_hide_attribute_with_preloads is enabled)
57
- #
58
- # @return [Boolean, nil] Attribute :hide option value
59
- def hide
60
- opts[:hide]
61
- end
45
+ @initials = SeregaUtils::EnumDeepFreeze.call(
46
+ name: name,
47
+ opts: SeregaUtils::EnumDeepDup.call(opts),
48
+ block: block
49
+ )
62
50
 
63
- # Shows current opts[:many] option
64
- # @return [Boolean, nil] Attribute :many option value
65
- def many
66
- opts[:many]
51
+ normalizer = serializer_class::SeregaAttributeNormalizer.new(initials)
52
+ set_normalized_vars(normalizer)
67
53
  end
68
54
 
69
55
  # Shows whether attribute has specified serializer
70
56
  # @return [Boolean] Checks if attribute is relationship (if :serializer option exists)
71
57
  def relation?
72
- !opts[:serializer].nil?
58
+ !@serializer.nil?
73
59
  end
74
60
 
75
61
  # Shows specified serializer class
76
62
  # @return [Serega, nil] Attribute serializer if exists
77
63
  def serializer
78
- return @serializer if instance_variable_defined?(:@serializer)
79
-
80
- serializer = opts[:serializer]
81
- @serializer =
82
- case serializer
83
- when String then Object.const_get(serializer, false)
84
- when Proc then serializer.call
85
- else serializer
86
- end
87
- end
64
+ ser = @serializer
65
+ return ser if (ser.is_a?(Class) && (ser < Serega)) || !ser
88
66
 
89
- # Returns final block used to find attribute value
90
- #
91
- # Patched in:
92
- # - plugin :formatters (wraps resulted block in formatter block and formats :const values)
93
- #
94
- # @return [Proc] Proc to find attribute value
95
- def value_block
96
- return @value_block if instance_variable_defined?(:@value_block)
97
-
98
- @value_block =
99
- block ||
100
- opts[:value] ||
101
- const_block ||
102
- delegate_block ||
103
- keyword_block
67
+ @serializer = ser.is_a?(String) ? Object.const_get(ser, false) : ser.call
104
68
  end
105
69
 
106
70
  #
@@ -135,46 +99,14 @@ class Serega
135
99
 
136
100
  private
137
101
 
138
- def const_block
139
- return unless opts.key?(:const)
140
-
141
- const = opts[:const]
142
- proc { const }
143
- end
144
-
145
- def keyword_block
146
- key_method_name = key
147
- proc do |object|
148
- handle_no_method_error { object.public_send(key_method_name) }
149
- end
150
- end
151
-
152
- def delegate_block
153
- delegate = opts[:delegate]
154
- return unless delegate
155
-
156
- key_method_name = delegate[:key] || key
157
- delegate_to = delegate[:to]
158
-
159
- if delegate[:allow_nil]
160
- proc do |object|
161
- handle_no_method_error do
162
- object.public_send(delegate_to)&.public_send(key_method_name)
163
- end
164
- end
165
- else
166
- proc do |object|
167
- handle_no_method_error do
168
- object.public_send(delegate_to).public_send(key_method_name)
169
- end
170
- end
171
- end
172
- end
102
+ attr_reader :value_block
173
103
 
174
- def handle_no_method_error
175
- yield
176
- rescue NoMethodError => error
177
- raise error, "NoMethodError when serializing '#{name}' attribute in #{self.class.serializer_class}\n\n#{error.message}", error.backtrace
104
+ def set_normalized_vars(normalizer)
105
+ @name = normalizer.name
106
+ @value_block = normalizer.value_block
107
+ @hide = normalizer.hide
108
+ @many = normalizer.many
109
+ @serializer = normalizer.serializer
178
110
  end
179
111
  end
180
112
 
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ #
5
+ # Prepares provided attribute options
6
+ #
7
+ class SeregaAttributeNormalizer
8
+ #
9
+ # AttributeNormalizer instance methods
10
+ #
11
+ module AttributeNormalizerInstanceMethods
12
+ # Attribute initial params
13
+ # @return [Hash] Attribute initial params
14
+ attr_reader :init_name, :init_opts, :init_block
15
+
16
+ #
17
+ # Instantiates attribute options normalizer
18
+ #
19
+ # @param initials [Hash] new attribute options
20
+ #
21
+ # @return [SeregaAttributeNormalizer] Instantiated attribute options normalizer
22
+ #
23
+ def initialize(initials)
24
+ @init_name = initials[:name]
25
+ @init_opts = initials[:opts]
26
+ @init_block = initials[:block]
27
+ end
28
+
29
+ #
30
+ # Symbolized initial attribute name
31
+ #
32
+ # @return [Symbol] Attribute normalized name
33
+ #
34
+ def name
35
+ @name ||= prepare_name
36
+ end
37
+
38
+ #
39
+ # Symbolized initial attribute key or attribute name if key is empty
40
+ #
41
+ # @return [Symbol] Attribute normalized name
42
+ #
43
+ def key
44
+ @key ||= prepare_key
45
+ end
46
+
47
+ #
48
+ # Combines all options to return single block that will be used to find
49
+ # attribute value during serialization
50
+ #
51
+ # @return [#call] Attribute normalized callable value block
52
+ #
53
+ def value_block
54
+ @value_block ||= prepare_value_block
55
+ end
56
+
57
+ #
58
+ # Shows if attribute is specified to be hidden
59
+ #
60
+ # @return [Boolean, nil] if attribute must be hidden by default
61
+ #
62
+ def hide
63
+ return @hide if instance_variable_defined?(:@hide)
64
+
65
+ @hide = prepare_hide
66
+ end
67
+
68
+ #
69
+ # Shows if attribute is specified to be a one-to-many relationship
70
+ #
71
+ # @return [Boolean, nil] if attribute is specified to be a one-to-many relationship
72
+ #
73
+ def many
74
+ return @many if instance_variable_defined?(:@many)
75
+
76
+ @many = prepare_many
77
+ end
78
+
79
+ #
80
+ # Shows specified attribute serializer
81
+ # @return [Serega, String, #callable, nil] specified serializer
82
+ #
83
+ def serializer
84
+ return @serializer if instance_variable_defined?(:@serializer)
85
+
86
+ @serializer = prepare_serializer
87
+ end
88
+
89
+ private
90
+
91
+ def prepare_name
92
+ init_name.to_sym
93
+ end
94
+
95
+ #
96
+ # Patched in:
97
+ # - plugin :formatters (wraps resulted block in formatter block and formats :const values)
98
+ #
99
+ def prepare_value_block
100
+ init_block ||
101
+ init_opts[:value] ||
102
+ prepare_const_block ||
103
+ prepare_delegate_block ||
104
+ prepare_keyword_block
105
+ end
106
+
107
+ #
108
+ # Patched in:
109
+ # - plugin :preloads (returns true by default if config option auto_hide_attribute_with_preloads is enabled)
110
+ #
111
+ def prepare_hide
112
+ init_opts[:hide]
113
+ end
114
+
115
+ def prepare_many
116
+ init_opts[:many]
117
+ end
118
+
119
+ def prepare_serializer
120
+ init_opts[:serializer]
121
+ end
122
+
123
+ def prepare_key
124
+ key = init_opts[:key]
125
+ key ? key.to_sym : name
126
+ end
127
+
128
+ def prepare_const_block
129
+ return unless init_opts.key?(:const)
130
+
131
+ const = init_opts[:const]
132
+ proc { const }
133
+ end
134
+
135
+ def prepare_keyword_block
136
+ key_method_name = key
137
+ proc do |object|
138
+ handle_no_method_error { object.public_send(key_method_name) }
139
+ end
140
+ end
141
+
142
+ def prepare_delegate_block
143
+ delegate = init_opts[:delegate]
144
+ return unless delegate
145
+
146
+ key_method_name = delegate[:key] || key
147
+ delegate_to = delegate[:to]
148
+
149
+ allow_nil = delegate.fetch(:allow_nil) { self.class.serializer_class.config.delegate_default_allow_nil }
150
+
151
+ if allow_nil
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
157
+ else
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
163
+ end
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
171
+ end
172
+
173
+ extend Serega::SeregaHelpers::SerializerClassHelper
174
+ include AttributeNormalizerInstanceMethods
175
+ end
176
+ end
data/lib/serega/config.rb CHANGED
@@ -19,7 +19,8 @@ class Serega
19
19
  serialize_keys: %i[context many].freeze,
20
20
  check_attribute_name: true,
21
21
  check_initiate_params: true,
22
- max_cached_map_per_serializer_count: 0,
22
+ delegate_default_allow_nil: false,
23
+ max_cached_plans_per_serializer_count: 0,
23
24
  to_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjDump : SeregaJSON::JSONDump,
24
25
  from_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjLoad : SeregaJSON::JSONLoad
25
26
  }.freeze
@@ -87,20 +88,36 @@ class Serega
87
88
  opts[:check_initiate_params] = value
88
89
  end
89
90
 
90
- # Returns :max_cached_map_per_serializer_count config option
91
- # @return [Boolean] Current :max_cached_map_per_serializer_count config option
92
- def max_cached_map_per_serializer_count
93
- opts.fetch(:max_cached_map_per_serializer_count)
91
+ # Returns :delegate_default_allow_nil config option
92
+ # @return [Boolean] Current :delegate_default_allow_nil config option
93
+ def delegate_default_allow_nil
94
+ opts.fetch(:delegate_default_allow_nil)
94
95
  end
95
96
 
96
- # Sets :max_cached_map_per_serializer_count config option
97
+ # Sets :delegate_default_allow_nil config option
98
+ #
99
+ # @param value [Boolean] Set :delegate_default_allow_nil config option
100
+ #
101
+ # @return [Boolean] :delegate_default_allow_nil config option
102
+ def delegate_default_allow_nil=(value)
103
+ raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
104
+ opts[:delegate_default_allow_nil] = value
105
+ end
106
+
107
+ # Returns :max_cached_plans_per_serializer_count config option
108
+ # @return [Boolean] Current :max_cached_plans_per_serializer_count config option
109
+ def max_cached_plans_per_serializer_count
110
+ opts.fetch(:max_cached_plans_per_serializer_count)
111
+ end
112
+
113
+ # Sets :max_cached_plans_per_serializer_count config option
97
114
  #
98
115
  # @param value [Boolean] Set :check_initiate_params config option
99
116
  #
100
- # @return [Boolean] New :max_cached_map_per_serializer_count config option
101
- def max_cached_map_per_serializer_count=(value)
117
+ # @return [Boolean] New :max_cached_plans_per_serializer_count config option
118
+ def max_cached_plans_per_serializer_count=(value)
102
119
  raise SeregaError, "Must have Integer value, #{value.inspect} provided" unless value.is_a?(Integer)
103
- opts[:max_cached_map_per_serializer_count] = value
120
+ opts[:max_cached_plans_per_serializer_count] = value
104
121
  end
105
122
 
106
123
  # Returns whether attributes names check is disabled
@@ -10,16 +10,17 @@ class Serega
10
10
  # SeregaObjectSerializer instance methods
11
11
  #
12
12
  module InstanceMethods
13
- attr_reader :context, :points, :many, :opts
13
+ attr_reader :context, :plan, :many, :opts
14
14
 
15
+ # @param plan [SeregaPlan] Serialization plan
15
16
  # @param context [Hash] Serialization context
16
17
  # @param many [TrueClass|FalseClass] is object is enumerable
17
- # @param points [Array<MapPoint>] Serialization points (attributes)
18
+ # @param opts [Hash] Any custom options
18
19
  #
19
20
  # @return [SeregaObjectSerializer] New SeregaObjectSerializer
20
- def initialize(context:, points:, many: nil, **opts)
21
+ def initialize(context:, plan:, many: nil, **opts)
21
22
  @context = context
22
- @points = points
23
+ @plan = plan
23
24
  @many = many
24
25
  @opts = opts
25
26
  end
@@ -44,7 +45,7 @@ class Serega
44
45
  # Patched in:
45
46
  # - plugin :presenter (makes presenter_object and serializes it)
46
47
  def serialize_object(object)
47
- points.each_with_object({}) do |point, container|
48
+ plan.points.each_with_object({}) do |point, container|
48
49
  serialize_point(object, point, container)
49
50
  end
50
51
  end
@@ -70,14 +71,14 @@ class Serega
70
71
  end
71
72
 
72
73
  def final_value(value, point)
73
- point.has_nested_points? ? relation_value(value, point) : value
74
+ point.child_plan ? relation_value(value, point) : value
74
75
  end
75
76
 
76
77
  def relation_value(value, point)
77
- nested_points = point.nested_points
78
- nested_serializer = point.nested_object_serializer
79
- nested_many = point.many
80
- serializer = nested_serializer.new(context: context, points: nested_points, many: nested_many, **opts)
78
+ child_plan = point.child_plan
79
+ child_serializer = point.child_object_serializer
80
+ child_many = point.many
81
+ serializer = child_serializer.new(context: context, plan: child_plan, many: child_many, **opts)
81
82
  serializer.serialize(value)
82
83
  end
83
84
 
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ #
5
+ # Constructs plan - list of serialized attributes.
6
+ # We will traverse this plan to construct serialized response.
7
+ #
8
+ class SeregaPlan
9
+ #
10
+ # SeregaPlan class methods
11
+ #
12
+ module ClassMethods
13
+ #
14
+ # Constructs plan of attributes that should be serialized.
15
+ #
16
+ # @param opts Serialization parameters
17
+ # @option opts [Hash] :only The only attributes to serialize
18
+ # @option opts [Hash] :except Attributes to hide
19
+ # @option opts [Hash] :with Attributes (usually marked `hide: true`) to serialize additionally
20
+ #
21
+ # @return [SeregaPlan] Serialization plan
22
+ #
23
+ def call(opts)
24
+ max_cache_size = serializer_class.config.max_cached_plans_per_serializer_count
25
+ return plan_for(opts) if max_cache_size.zero?
26
+
27
+ cached_plan_for(opts, max_cache_size)
28
+ end
29
+
30
+ private
31
+
32
+ def plan_for(opts)
33
+ new(**modifiers(opts))
34
+ end
35
+
36
+ def cached_plan_for(opts, max_cache_size)
37
+ @cache ||= {}
38
+ cache_key = construct_cache_key(opts)
39
+
40
+ plan = @cache[cache_key] ||= plan_for(opts)
41
+ @cache.shift if @cache.length > max_cache_size
42
+ plan
43
+ end
44
+
45
+ def modifiers(opts)
46
+ {
47
+ only: opts[:only] || FROZEN_EMPTY_HASH,
48
+ except: opts[:except] || FROZEN_EMPTY_HASH,
49
+ with: opts[:with] || FROZEN_EMPTY_HASH
50
+ }
51
+ end
52
+
53
+ def construct_cache_key(opts, cache_key = nil)
54
+ return nil if opts.empty?
55
+
56
+ cache_key ||= +""
57
+
58
+ opts.each do |key, nested_opts|
59
+ cache_key.insert(-1, SeregaUtils::SymbolName.call(key))
60
+ cache_key.insert(-1, "-")
61
+ construct_cache_key(nested_opts, cache_key)
62
+ end
63
+
64
+ cache_key
65
+ end
66
+ end
67
+
68
+ #
69
+ # SeregaPlan instance methods
70
+ #
71
+ module InstanceMethods
72
+ # Parent plan point, if exists
73
+ # @return [SeregaPlanPoint, nil]
74
+ attr_reader :parent_plan_point
75
+
76
+ # Serialization points
77
+ # @return [Array<SeregaPlanPoint>] points to serialize
78
+ attr_reader :points
79
+
80
+ #
81
+ # Instantiate new serialization plan.
82
+ #
83
+ # @param opts Serialization parameters
84
+ # @option opts [Hash] :only The only attributes to serialize
85
+ # @option opts [Hash] :except Attributes to hide
86
+ # @option opts [Hash] :with Attributes (usually marked hide: true`) to serialize additionally
87
+ # @option opts [Hash] :with Attributes (usually marked hide: true`) to serialize additionally
88
+ #
89
+ # @return [SeregaPlan] Serialization plan
90
+ #
91
+ def initialize(only:, except:, with:, parent_plan_point: nil)
92
+ @parent_plan_point = parent_plan_point
93
+ @points = attributes_points(only: only, except: except, with: with)
94
+ end
95
+
96
+ #
97
+ # Serializer class of current plan
98
+ #
99
+ def serializer_class
100
+ self.class.serializer_class
101
+ end
102
+
103
+ private
104
+
105
+ def attributes_points(only:, except:, with:)
106
+ points = []
107
+
108
+ serializer_class.attributes.each_value do |attribute|
109
+ next unless attribute.visible?(only: only, except: except, with: with)
110
+
111
+ child_fields =
112
+ if attribute.relation?
113
+ name = attribute.name
114
+ {only: only[name], with: with[name], except: except[name]}
115
+ end
116
+
117
+ points << serializer_class::SeregaPlanPoint.new(attribute, self, child_fields)
118
+ end
119
+
120
+ points.freeze
121
+ end
122
+ end
123
+
124
+ extend ClassMethods
125
+ include InstanceMethods
126
+ extend SeregaHelpers::SerializerClassHelper
127
+ end
128
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ #
5
+ # Combines attribute and nested attributes
6
+ #
7
+ class SeregaPlanPoint
8
+ #
9
+ # SeregaPlanPoint instance methods
10
+ #
11
+ module InstanceMethods
12
+ extend Forwardable
13
+
14
+ # Link to current plan this point belongs to
15
+ # @return [SeregaAttribute] Current plan
16
+ attr_reader :plan
17
+
18
+ # Shows current attribute
19
+ # @return [SeregaAttribute] Current attribute
20
+ attr_reader :attribute
21
+
22
+ # Shows child plan if exists
23
+ # @return [SeregaPlan, nil] Attribute serialization plan
24
+ attr_reader :child_plan
25
+
26
+ # Child fields to serialize
27
+ # @return [SeregaPlan, nil] Attribute serialization plan
28
+ attr_reader :child_fields
29
+
30
+ # @!method name
31
+ # Attribute `name`
32
+ # @see SeregaAttribute::AttributeInstanceMethods#name
33
+ # @!method value
34
+ # Attribute `value` block
35
+ # @see SeregaAttribute::AttributeInstanceMethods#value
36
+ # @!method many
37
+ # Attribute `many` option
38
+ # @see SeregaAttribute::AttributeInstanceMethods#many
39
+ # @!method serializer
40
+ # Attribute `serializer` option
41
+ # @see SeregaAttribute::AttributeInstanceMethods#serializer
42
+ def_delegators :@attribute, :name, :value, :many, :serializer
43
+
44
+ #
45
+ # Initializes plan point
46
+ #
47
+ # @param plan [SeregaPlan] Plan where this point belongs to.
48
+ # @param attribute [SeregaAttribute] Attribute to construct plan point
49
+ # @param child_fields [Hash, nil] Child fields (:only, :with, :except)
50
+ #
51
+ # @return [SeregaPlanPoint] New plan point
52
+ #
53
+ def initialize(attribute, plan = nil, child_fields = nil)
54
+ @plan = plan
55
+ @attribute = attribute
56
+ @child_fields = child_fields
57
+ set_normalized_vars
58
+ freeze
59
+ end
60
+
61
+ #
62
+ # @return [SeregaObjectSerializer] object serializer for child plan
63
+ #
64
+ def child_object_serializer
65
+ serializer::SeregaObjectSerializer
66
+ end
67
+
68
+ private
69
+
70
+ # Patched in:
71
+ # - plugin :batch (prepares @batch)
72
+ # - plugin :preloads (prepares @preloads and @preloads_path)
73
+ def set_normalized_vars
74
+ @child_plan = prepare_child_plan
75
+ end
76
+
77
+ def prepare_child_plan
78
+ return unless serializer
79
+
80
+ fields = child_fields || FROZEN_EMPTY_HASH
81
+
82
+ serializer::SeregaPlan.new(
83
+ parent_plan_point: self,
84
+ only: fields[:only] || FROZEN_EMPTY_HASH,
85
+ with: fields[:with] || FROZEN_EMPTY_HASH,
86
+ except: fields[:except] || FROZEN_EMPTY_HASH
87
+ )
88
+ end
89
+ end
90
+
91
+ extend SeregaHelpers::SerializerClassHelper
92
+ include InstanceMethods
93
+ end
94
+ end