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.
- checksums.yaml +4 -4
- data/README.md +319 -141
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +37 -105
- data/lib/serega/attribute_normalizer.rb +176 -0
- data/lib/serega/config.rb +26 -9
- data/lib/serega/object_serializer.rb +11 -10
- data/lib/serega/plan.rb +128 -0
- data/lib/serega/plan_point.rb +94 -0
- data/lib/serega/plugins/batch/batch.rb +187 -41
- data/lib/serega/plugins/batch/lib/loader.rb +4 -4
- data/lib/serega/plugins/batch/lib/loaders.rb +3 -3
- data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +2 -2
- data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +19 -1
- data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +6 -4
- data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +9 -5
- data/lib/serega/plugins/formatters/formatters.rb +28 -31
- data/lib/serega/plugins/if/if.rb +24 -6
- data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +11 -13
- data/lib/serega/plugins/preloads/lib/main_preload_path.rb +2 -2
- data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +21 -15
- data/lib/serega/plugins/preloads/preloads.rb +72 -40
- data/lib/serega/plugins/presenter/presenter.rb +0 -9
- data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +11 -1
- data/lib/serega/plugins.rb +3 -3
- data/lib/serega/utils/enum_deep_dup.rb +18 -18
- data/lib/serega/utils/enum_deep_freeze.rb +35 -0
- data/lib/serega/utils/to_hash.rb +17 -9
- data/lib/serega.rb +22 -15
- metadata +6 -6
- data/lib/serega/map.rb +0 -91
- data/lib/serega/map_point.rb +0 -66
- data/lib/serega/plugins/batch/lib/batch_option_model.rb +0 -73
- data/lib/serega/plugins/preloads/lib/enum_deep_freeze.rb +0 -26
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.11.0
|
data/lib/serega/attribute.rb
CHANGED
@@ -9,98 +9,62 @@ class Serega
|
|
9
9
|
# Attribute instance methods
|
10
10
|
#
|
11
11
|
module AttributeInstanceMethods
|
12
|
-
#
|
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
|
-
#
|
17
|
-
# @return [
|
18
|
-
attr_reader :
|
20
|
+
# Attribute :many option
|
21
|
+
# @return [Boolean, nil] Attribute :many option
|
22
|
+
attr_reader :many
|
19
23
|
|
20
|
-
#
|
21
|
-
# @return [
|
22
|
-
attr_reader :
|
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
|
31
|
-
# @option opts [
|
32
|
-
#
|
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 [
|
35
|
-
#
|
36
|
-
# @param block [Proc]
|
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
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
64
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
175
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
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 :
|
91
|
-
# @return [Boolean] Current :
|
92
|
-
def
|
93
|
-
opts.fetch(:
|
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 :
|
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 :
|
101
|
-
def
|
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[:
|
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, :
|
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
|
18
|
+
# @param opts [Hash] Any custom options
|
18
19
|
#
|
19
20
|
# @return [SeregaObjectSerializer] New SeregaObjectSerializer
|
20
|
-
def initialize(context:,
|
21
|
+
def initialize(context:, plan:, many: nil, **opts)
|
21
22
|
@context = context
|
22
|
-
@
|
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.
|
74
|
+
point.child_plan ? relation_value(value, point) : value
|
74
75
|
end
|
75
76
|
|
76
77
|
def relation_value(value, point)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
serializer =
|
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
|
|
data/lib/serega/plan.rb
ADDED
@@ -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
|