serega 0.9.0 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b6c3672a4fc40557f4fef3e650de56c84bf23d96defabd3f3f04aa5f273aa90
4
- data.tar.gz: 79bcc2c3526b0ae141de69003e0834c94f9b7bade028cbc85bed20c3f3ba1757
3
+ metadata.gz: 6a94d74dd45c74e499cd1a3abec082d8776019538b67bf24fc840512271adb6b
4
+ data.tar.gz: a22bc547e6ac94b0988e2cfef2af31ad659beb53058a90159e7f46f6470c3390
5
5
  SHA512:
6
- metadata.gz: edcc6856f212848b721af517bb0f77695e6e7f2adbc7bb687e4573a62efe84e02266301cc3e70647b7cd8385b96901436ce1ded9446e9712f3884d8d6348c855
7
- data.tar.gz: e0fec25bc4471ebbf3b6fbfb1967101af7f8cd7e703cb56eba935fd51bc6de0efe87d802be8257a950648afc633ed3ceb3bfc78f67b3905197ffa0a6e39ada32
6
+ metadata.gz: d89c6180d4af0273eee6dd6179a0ac6f0157409f07ae6c18985941ecd8971bd17eb8b547a764d97afd12cdc08e565cb8749e9d9e0a70d8ab8add5dbad3e349db
7
+ data.tar.gz: 4cd808491f38a469ac705f6884007a3d71cead7f12d8e9730ec8c15ee1da97cb2d0d7503aafc0925a5bf41c7b660f7aa71a62da6fda295e713b62114ee83842a
data/README.md CHANGED
@@ -111,6 +111,23 @@ class UserSerializer < Serega
111
111
  end
112
112
  ```
113
113
 
114
+ ---
115
+
116
+ ⚠️ Attribute names are checked to include only "a-z", "A-Z", "0-9", "_", "-", "~" characters.
117
+ We have this check as:
118
+ - Attributes names can be used in URL without encoding.
119
+ - Plugin [string_modifiers][string_modifiers] already uses "," and "()" as attribute names delimeters.
120
+ - We are protected from errors when added some non-english character looking as english.
121
+
122
+ This names check can be disabled globally or per-serializer via:
123
+ ```ruby
124
+ Serega.config.check_attribute_name = false
125
+
126
+ class SomeSerializer < Serega
127
+ config.check_attribute_name = false
128
+ end
129
+ ```
130
+
114
131
  ### Serializing
115
132
 
116
133
  We can serialize objects using class methods `.to_h`, `.to_json`, `.as_json` and same instance methods `#to_h`, `#to_json`, `#as_json`.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.0
1
+ 0.10.0
@@ -51,6 +51,10 @@ class Serega
51
51
  end
52
52
 
53
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
+ #
54
58
  # @return [Boolean, nil] Attribute :hide option value
55
59
  def hide
56
60
  opts[:hide]
@@ -82,7 +86,11 @@ class Serega
82
86
  end
83
87
  end
84
88
 
85
- # Returns final block that will be used to find attribute value
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
+ #
86
94
  # @return [Proc] Proc to find attribute value
87
95
  def value_block
88
96
  return @value_block if instance_variable_defined?(:@value_block)
data/lib/serega/config.rb CHANGED
@@ -17,6 +17,7 @@ class Serega
17
17
  initiate_keys: %i[only with except check_initiate_params].freeze,
18
18
  attribute_keys: %i[key value serializer many hide const delegate].freeze,
19
19
  serialize_keys: %i[context many].freeze,
20
+ check_attribute_name: true,
20
21
  check_initiate_params: true,
21
22
  max_cached_map_per_serializer_count: 0,
22
23
  to_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjDump : SeregaJSON::JSONDump,
@@ -102,6 +103,21 @@ class Serega
102
103
  opts[:max_cached_map_per_serializer_count] = value
103
104
  end
104
105
 
106
+ # Returns whether attributes names check is disabled
107
+ def check_attribute_name
108
+ opts.fetch(:check_attribute_name)
109
+ end
110
+
111
+ # Sets :check_attribute_name config option
112
+ #
113
+ # @param value [Boolean] Set :check_attribute_name config option
114
+ #
115
+ # @return [Boolean] New :check_attribute_name config option
116
+ def check_attribute_name=(value)
117
+ raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
118
+ opts[:check_attribute_name] = value
119
+ end
120
+
105
121
  # Returns current `to_json` adapter
106
122
  # @return [#call] Callable that used to construct JSON
107
123
  def to_json
@@ -41,22 +41,30 @@ class Serega
41
41
  object.map { |obj| serialize_object(obj) }
42
42
  end
43
43
 
44
+ # Patched in:
45
+ # - plugin :presenter (makes presenter_object and serializes it)
44
46
  def serialize_object(object)
45
47
  points.each_with_object({}) do |point, container|
46
48
  serialize_point(object, point, container)
47
49
  end
48
50
  end
49
51
 
52
+ # Patched in:
53
+ # - plugin :if (conditionally skips serializing this point)
50
54
  def serialize_point(object, point, container)
51
55
  attach_value(object, point, container)
52
56
  end
53
57
 
58
+ # Patched in:
59
+ # - plugin :batch (remembers key for batch loading values instead of attaching)
54
60
  def attach_value(object, point, container)
55
61
  value = point.value(object, context)
56
62
  final_value = final_value(value, point)
57
63
  attach_final_value(final_value, point, container)
58
64
  end
59
65
 
66
+ # Patched in:
67
+ # - plugin :if (conditionally skips attaching)
60
68
  def attach_final_value(final_value, point, container)
61
69
  container[point.name] = final_value
62
70
  end
@@ -262,12 +262,9 @@ class Serega
262
262
 
263
263
  def attach_value(object, point, container)
264
264
  batch = point.batch
265
+ return super unless batch
265
266
 
266
- if batch
267
- remember_key_for_batch_loading(batch, object, point, container)
268
- else
269
- super
270
- end
267
+ remember_key_for_batch_loading(batch, object, point, container)
271
268
  end
272
269
 
273
270
  def remember_key_for_batch_loading(batch, object, point, container)
@@ -68,6 +68,9 @@ class Serega
68
68
  end
69
69
  end
70
70
 
71
+ # Patched in:
72
+ # - plugin batch (extension :activerecord_preloads - preloads data to found values)
73
+ # - plugin batch (extension :formatters - formats values)
71
74
  def keys_values
72
75
  ids = keys.keys
73
76
 
@@ -66,19 +66,6 @@ class Serega
66
66
  serializer_class::SeregaObjectSerializer.include(SeregaObjectSerializerInstanceMethods)
67
67
  end
68
68
 
69
- # Checks requirements and loads additional plugins
70
- #
71
- # @param serializer_class [Class<Serega>] Current serializer class
72
- # @param opts [Hash] loaded plugins opts
73
- #
74
- # @return [void]
75
- #
76
- def self.before_load_plugin(serializer_class, **opts)
77
- if serializer_class.plugin_used?(:batch)
78
- raise SeregaError, "Plugin `#{plugin_name}` must be loaded before `batch`"
79
- end
80
- end
81
-
82
69
  #
83
70
  # Adds config options and runs other callbacks after plugin was loaded
84
71
  #
@@ -60,10 +60,18 @@ class Serega
60
60
  private
61
61
 
62
62
  def check(path, opts, block)
63
- CheckPath.call(path)
64
- CheckOpts.call(opts, self.class.serializer_class.config.metadata.attribute_keys)
63
+ CheckPath.call(path) if check_attribute_name
64
+ CheckOpts.call(opts, attribute_keys)
65
65
  CheckBlock.call(block)
66
66
  end
67
+
68
+ def attribute_keys
69
+ self.class.serializer_class.config.metadata.attribute_keys
70
+ end
71
+
72
+ def check_attribute_name
73
+ self.class.serializer_class.config.check_attribute_name
74
+ end
67
75
  end
68
76
 
69
77
  extend Serega::SeregaHelpers::SerializerClassHelper
@@ -130,19 +130,6 @@ class Serega
130
130
  # @see Serega::SeregaConfig
131
131
  #
132
132
  module ClassMethods
133
- private def inherited(subclass)
134
- super
135
-
136
- meta_attribute_class = Class.new(self::MetaAttribute)
137
- meta_attribute_class.serializer_class = subclass
138
- subclass.const_set(:MetaAttribute, meta_attribute_class)
139
-
140
- # Assign same metadata attributes
141
- meta_attributes.each_value do |attr|
142
- subclass.meta_attribute(*attr.path, **attr.opts, &attr.block)
143
- end
144
- end
145
-
146
133
  #
147
134
  # List of added metadata attributes
148
135
  #
@@ -165,6 +152,21 @@ class Serega
165
152
  attribute = self::MetaAttribute.new(path: path, opts: opts, block: block)
166
153
  meta_attributes[attribute.name] = attribute
167
154
  end
155
+
156
+ private
157
+
158
+ def inherited(subclass)
159
+ super
160
+
161
+ meta_attribute_class = Class.new(self::MetaAttribute)
162
+ meta_attribute_class.serializer_class = subclass
163
+ subclass.const_set(:MetaAttribute, meta_attribute_class)
164
+
165
+ # Assign same metadata attributes
166
+ meta_attributes.each_value do |attr|
167
+ subclass.meta_attribute(*attr.path, **attr.opts, &attr.block)
168
+ end
169
+ end
168
170
  end
169
171
 
170
172
  #
@@ -8,16 +8,15 @@ class Serega
8
8
  # Validator for meta_attribute :path parameter
9
9
  #
10
10
  class CheckPath
11
- FORMAT_ONE_CHAR = /\A[a-zA-Z0-9]\z/
12
- FORMAT_MANY_CHARS = /\A[a-zA-Z0-9][a-zA-Z0-9_-]*?[a-zA-Z0-9]\z/ # allow '-' and '_' in the middle
11
+ # Regexp for valid path
12
+ FORMAT = /\A[\w~-]+\z/
13
13
 
14
- private_constant :FORMAT_ONE_CHAR, :FORMAT_MANY_CHARS
14
+ private_constant :FORMAT
15
15
 
16
16
  class << self
17
17
  #
18
- # Checks allowed characters in specified metadata path parts.
19
- # Globally allowed characters: "a-z", "A-Z", "0-9".
20
- # Minus and low line "-", "_" also allowed except as the first or last character.
18
+ # Checks allowed characters.
19
+ # Allowed characters: "a-z", "A-Z", "0-9", "_", "-", "~".
21
20
  #
22
21
  # @param path [Array<String, Symbol>] Metadata attribute path names
23
22
  #
@@ -33,20 +32,14 @@ class Serega
33
32
  def check_name(name)
34
33
  name = name.to_s
35
34
 
36
- valid =
37
- case name.size
38
- when 0 then false
39
- when 1 then name.match?(FORMAT_ONE_CHAR)
40
- else name.match?(FORMAT_MANY_CHARS)
41
- end
42
-
43
- return if valid
44
-
45
- raise SeregaError, message(name)
35
+ raise SeregaError, message(name) unless FORMAT.match?(name)
46
36
  end
47
37
 
48
38
  def message(name)
49
- %(Invalid metadata path #{name.inspect}, globally allowed characters: "a-z", "A-Z", "0-9". Minus and low line "-", "_" also allowed except as the first or last character)
39
+ <<~MESSAGE.tr("\n", "")
40
+ Invalid metadata path #{name.inspect}.
41
+ Allowed characters: "a-z", "A-Z", "0-9", "_", "-", "~"
42
+ MESSAGE
50
43
  end
51
44
  end
52
45
  end
@@ -235,6 +235,8 @@ class Serega
235
235
  @auto_hide_attribute_with_preloads = auto && !preloads.nil? && (preloads != false) && (preloads != {})
236
236
  end
237
237
 
238
+ # Patched in:
239
+ # - plugin :batch (extension :preloads - skips auto preloads when batch option provided)
238
240
  def get_preloads
239
241
  preloads_provided = opts.key?(:preload)
240
242
  preloads =
@@ -80,14 +80,6 @@ class Serega
80
80
  # @see Serega
81
81
  #
82
82
  module ClassMethods
83
- private def inherited(subclass)
84
- super
85
-
86
- presenter_class = Class.new(self::Presenter)
87
- presenter_class.serializer_class = subclass
88
- subclass.const_set(:Presenter, presenter_class)
89
- end
90
-
91
83
  # Overrides {Serega::ClassMethods#attribute} method, additionally adds method
92
84
  # to Presenter to not hit {Serega::SeregaPlugins::Presenter::Presenter#method_missing}
93
85
  # @see Serega::ClassMethods#attribute
@@ -96,6 +88,16 @@ class Serega
96
88
  self::Presenter.def_delegator(:__getobj__, attribute.key) unless attribute.block
97
89
  end
98
90
  end
91
+
92
+ private
93
+
94
+ def inherited(subclass)
95
+ super
96
+
97
+ presenter_class = Class.new(self::Presenter)
98
+ presenter_class.serializer_class = subclass
99
+ subclass.const_set(:Presenter, presenter_class)
100
+ end
99
101
  end
100
102
 
101
103
  #
@@ -25,72 +25,61 @@ class Serega
25
25
  # Modifiers parser
26
26
  #
27
27
  class ParseStringModifiers
28
- #
29
- # Parses provided fields
30
- #
31
- # @param fields [String,Hash,Array,nil]
32
- #
33
- # @return [Hash] parsed modifiers in form of nested hash
34
- #
35
- def self.call(fields)
36
- return fields unless fields.is_a?(String)
28
+ class << self
29
+ #
30
+ # Parses string modifiers
31
+ #
32
+ # @param fields [String]
33
+ #
34
+ # @return [Hash] parsed modifiers in form of nested hash
35
+ #
36
+ # @example
37
+ # parse("user") => { user: {} }
38
+ # parse("user(id)") => { user: { id: {} } }
39
+ # parse("user(id,name)") => { user: { id: {}, name: {} } }
40
+ # parse("user,comments") => { user: {}, comments: {} }
41
+ # parse("user(comments(text))") => { user: { comments: { text: {} } } }
42
+ def parse(fields)
43
+ res = {}
44
+ attribute = +""
45
+ char = +""
46
+ path_stack = nil
47
+ fields = StringIO.new(fields)
37
48
 
38
- new.parse(fields)
39
- end
40
-
41
- #
42
- # Parses string modifiers
43
- #
44
- # @param fields [String]
45
- #
46
- # @return [Hash] parsed modifiers in form of nested hash
47
- #
48
- # @example
49
- # parse("user") => { user: {} }
50
- # parse("user(id)") => { user: { id: {} } }
51
- # parse("user(id,name)") => { user: { id: {}, name: {} } }
52
- # parse("user,comments") => { user: {}, comments: {} }
53
- # parse("user(comments(text))") => { user: { comments: { text: {} } } }
54
- def parse(fields)
55
- res = {}
56
- attribute = +""
57
- char = +""
58
- path_stack = nil
59
- fields = StringIO.new(fields)
60
-
61
- while fields.read(1, char)
62
- case char
63
- when ","
64
- add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
65
- when ")"
66
- add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
67
- path_stack&.pop
68
- when "("
69
- name = add_attribute(res, path_stack, attribute, {})
70
- (path_stack ||= []).push(name) if name
71
- else
72
- attribute.insert(-1, char)
49
+ while fields.read(1, char)
50
+ case char
51
+ when ","
52
+ add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
53
+ when ")"
54
+ add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
55
+ path_stack&.pop
56
+ when "("
57
+ name = add_attribute(res, path_stack, attribute, {})
58
+ (path_stack ||= []).push(name) if name
59
+ else
60
+ attribute.insert(-1, char)
61
+ end
73
62
  end
74
- end
75
63
 
76
- add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
64
+ add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
77
65
 
78
- res
79
- end
66
+ res
67
+ end
80
68
 
81
- private
69
+ private
82
70
 
83
- def add_attribute(res, path_stack, attribute, nested_attributes = FROZEN_EMPTY_HASH)
84
- attribute.strip!
85
- return if attribute.empty?
71
+ def add_attribute(res, path_stack, attribute, nested_attributes = FROZEN_EMPTY_HASH)
72
+ attribute.strip!
73
+ return if attribute.empty?
86
74
 
87
- name = attribute.to_sym
88
- attribute.clear
75
+ name = attribute.to_sym
76
+ attribute.clear
89
77
 
90
- current_attrs = (!path_stack || path_stack.empty?) ? res : res.dig(*path_stack)
91
- current_attrs[name] = nested_attributes
78
+ current_attrs = (!path_stack || path_stack.empty?) ? res : res.dig(*path_stack)
79
+ current_attrs[name] = nested_attributes
92
80
 
93
- name
81
+ name
82
+ end
94
83
  end
95
84
  end
96
85
  end
@@ -29,14 +29,10 @@ class Serega
29
29
  module InstanceMethods
30
30
  private
31
31
 
32
- def prepare_modifiers(opts)
33
- parsed_opts =
34
- opts.each_with_object({}) do |(key, value), obj|
35
- value = ParseStringModifiers.call(value) if (key == :only) || (key == :except) || (key == :with)
36
- obj[key] = value
37
- end
32
+ def parse_modifier(value)
33
+ return ParseStringModifiers.parse(value) if value.is_a?(String)
38
34
 
39
- super(parsed_opts)
35
+ super
40
36
  end
41
37
  end
42
38
  end
@@ -38,8 +38,8 @@ class Serega
38
38
  end
39
39
 
40
40
  def dup_array_values(duplicate_data)
41
- duplicate_data.each_with_index do |value, index|
42
- duplicate_data[index] = call(value) if value.is_a?(Enumerable)
41
+ duplicate_data.map! do |value|
42
+ value.is_a?(Enumerable) ? call(value) : value
43
43
  end
44
44
  end
45
45
  end
@@ -7,19 +7,13 @@ class Serega
7
7
  # Attribute `name` parameter validator
8
8
  #
9
9
  class CheckName
10
- # Regexp for valid one-char attribute name
11
- FORMAT_ONE_CHAR = /\A[a-zA-Z0-9]\z/
12
-
13
- # Regexp for valid multi-chars attribute name
14
- FORMAT_MANY_CHARS = /\A[a-zA-Z0-9][a-zA-Z0-9_-]*?[a-zA-Z0-9]\z/ # allow '-' and '_' in the middle
15
-
16
- private_constant :FORMAT_ONE_CHAR, :FORMAT_MANY_CHARS
10
+ # Regexp for valid attribute name
11
+ FORMAT = /\A[\w~-]+\z/
17
12
 
18
13
  class << self
19
14
  #
20
15
  # Checks allowed characters.
21
- # Globally allowed characters: "a-z", "A-Z", "0-9".
22
- # Minus and low line "-", "_" also allowed except as the first or last character.
16
+ # Allowed characters: "a-z", "A-Z", "0-9", "_", "-", "~".
23
17
  #
24
18
  # @param name [String, Symbol] Attribute name
25
19
  #
@@ -28,23 +22,16 @@ class Serega
28
22
  #
29
23
  def call(name)
30
24
  name = SeregaUtils::SymbolName.call(name)
31
-
32
- valid =
33
- case name.size
34
- when 0 then false
35
- when 1 then name.match?(FORMAT_ONE_CHAR)
36
- else name.match?(FORMAT_MANY_CHARS)
37
- end
38
-
39
- return if valid
40
-
41
- raise SeregaError, message(name)
25
+ raise SeregaError, message(name) unless FORMAT.match?(name)
42
26
  end
43
27
 
44
28
  private
45
29
 
46
30
  def message(name)
47
- %(Invalid attribute name = #{name.inspect}. Globally allowed characters: "a-z", "A-Z", "0-9". Minus and low line "-", "_" also allowed except as the first or last character)
31
+ <<~MESSAGE.tr("\n", "")
32
+ Invalid attribute name = #{name.inspect}.
33
+ Allowed characters: "a-z", "A-Z", "0-9", "_", "-", "~"
34
+ MESSAGE
48
35
  end
49
36
  end
50
37
  end
@@ -41,7 +41,7 @@ class Serega
41
41
  # Validates attribute params
42
42
  #
43
43
  def validate
44
- check_name
44
+ check_name if check_attribute_name
45
45
  check_opts
46
46
  check_block
47
47
  end
@@ -52,6 +52,11 @@ class Serega
52
52
  Attribute::CheckName.call(name)
53
53
  end
54
54
 
55
+ # Patched in:
56
+ # - plugin :batch (checks :batch option)
57
+ # - plugin :context_metadata (checks context metadata option which is :meta by default)
58
+ # - plugin :if (checks :if, :if_value, :unless, :unless_value options)
59
+ # - plugin :preloads (checks :preload option)
55
60
  def check_opts
56
61
  Utils::CheckAllowedKeys.call(opts, allowed_opts_keys)
57
62
 
@@ -71,6 +76,10 @@ class Serega
71
76
  def allowed_opts_keys
72
77
  self.class.serializer_class.config.attribute_keys
73
78
  end
79
+
80
+ def check_attribute_name
81
+ self.class.serializer_class.config.check_attribute_name
82
+ end
74
83
  end
75
84
 
76
85
  include InstanceMethods
data/lib/serega.rb CHANGED
@@ -70,49 +70,6 @@ class Serega
70
70
  # Returns current config
71
71
  # @return [SeregaConfig] current serializer config
72
72
  attr_reader :config
73
-
74
- private def inherited(subclass)
75
- config_class = Class.new(self::SeregaConfig)
76
- config_class.serializer_class = subclass
77
- subclass.const_set(:SeregaConfig, config_class)
78
- subclass.instance_variable_set(:@config, subclass::SeregaConfig.new(config.opts))
79
-
80
- attribute_class = Class.new(self::SeregaAttribute)
81
- attribute_class.serializer_class = subclass
82
- subclass.const_set(:SeregaAttribute, attribute_class)
83
-
84
- map_class = Class.new(self::SeregaMap)
85
- map_class.serializer_class = subclass
86
- subclass.const_set(:SeregaMap, map_class)
87
-
88
- map_point_class = Class.new(self::SeregaMapPoint)
89
- map_point_class.serializer_class = subclass
90
- subclass.const_set(:SeregaMapPoint, map_point_class)
91
-
92
- object_serializer_class = Class.new(self::SeregaObjectSerializer)
93
- object_serializer_class.serializer_class = subclass
94
- subclass.const_set(:SeregaObjectSerializer, object_serializer_class)
95
-
96
- check_attribute_params_class = Class.new(self::CheckAttributeParams)
97
- check_attribute_params_class.serializer_class = subclass
98
- subclass.const_set(:CheckAttributeParams, check_attribute_params_class)
99
-
100
- check_initiate_params_class = Class.new(self::CheckInitiateParams)
101
- check_initiate_params_class.serializer_class = subclass
102
- subclass.const_set(:CheckInitiateParams, check_initiate_params_class)
103
-
104
- check_serialize_params_class = Class.new(self::CheckSerializeParams)
105
- check_serialize_params_class.serializer_class = subclass
106
- subclass.const_set(:CheckSerializeParams, check_serialize_params_class)
107
-
108
- # Assign same attributes
109
- attributes.each_value do |attr|
110
- subclass.attribute(attr.name, **attr.opts, &attr.block)
111
- end
112
-
113
- super
114
- end
115
-
116
73
  #
117
74
  # Enables plugin for current serializer
118
75
  #
@@ -170,6 +127,9 @@ class Serega
170
127
  #
171
128
  # Adds attribute
172
129
  #
130
+ # Patched in:
131
+ # - plugin :presenter (additionally adds method in Presenter class)
132
+ #
173
133
  # @param name [Symbol] Attribute name. Attribute value will be found by executing `object.<name>`
174
134
  # @param opts [Hash] Options to serialize attribute
175
135
  # @param block [Proc] Custom block to find attribute value. Accepts object and context.
@@ -250,6 +210,54 @@ class Serega
250
210
  def as_json(object, opts = nil)
251
211
  config.from_json.call(to_json(object, opts))
252
212
  end
213
+
214
+ private
215
+
216
+ # Patched in:
217
+ # - plugin :batch (defines SeregaBatchLoaders, SeregaBatchLoader)
218
+ # - plugin :metadata (defines MetaAttribute and copies meta_attributes to subclasses)
219
+ # - plugin :presenter (defines Presenter)
220
+ def inherited(subclass)
221
+ config_class = Class.new(self::SeregaConfig)
222
+ config_class.serializer_class = subclass
223
+ subclass.const_set(:SeregaConfig, config_class)
224
+ subclass.instance_variable_set(:@config, subclass::SeregaConfig.new(config.opts))
225
+
226
+ attribute_class = Class.new(self::SeregaAttribute)
227
+ attribute_class.serializer_class = subclass
228
+ subclass.const_set(:SeregaAttribute, attribute_class)
229
+
230
+ map_class = Class.new(self::SeregaMap)
231
+ map_class.serializer_class = subclass
232
+ subclass.const_set(:SeregaMap, map_class)
233
+
234
+ map_point_class = Class.new(self::SeregaMapPoint)
235
+ map_point_class.serializer_class = subclass
236
+ subclass.const_set(:SeregaMapPoint, map_point_class)
237
+
238
+ object_serializer_class = Class.new(self::SeregaObjectSerializer)
239
+ object_serializer_class.serializer_class = subclass
240
+ subclass.const_set(:SeregaObjectSerializer, object_serializer_class)
241
+
242
+ check_attribute_params_class = Class.new(self::CheckAttributeParams)
243
+ check_attribute_params_class.serializer_class = subclass
244
+ subclass.const_set(:CheckAttributeParams, check_attribute_params_class)
245
+
246
+ check_initiate_params_class = Class.new(self::CheckInitiateParams)
247
+ check_initiate_params_class.serializer_class = subclass
248
+ subclass.const_set(:CheckInitiateParams, check_initiate_params_class)
249
+
250
+ check_serialize_params_class = Class.new(self::CheckSerializeParams)
251
+ check_serialize_params_class.serializer_class = subclass
252
+ subclass.const_set(:CheckSerializeParams, check_serialize_params_class)
253
+
254
+ # Assign same attributes
255
+ attributes.each_value do |attr|
256
+ subclass.attribute(attr.name, **attr.opts, &attr.block)
257
+ end
258
+
259
+ super
260
+ end
253
261
  end
254
262
 
255
263
  #
@@ -266,7 +274,7 @@ class Serega
266
274
  # @option opts [Boolean] :validate Validates provided modifiers (Default is true)
267
275
  #
268
276
  def initialize(opts = nil)
269
- @opts = (opts.nil? || opts.empty?) ? FROZEN_EMPTY_HASH : prepare_modifiers(opts)
277
+ @opts = (opts.nil? || opts.empty?) ? FROZEN_EMPTY_HASH : parse_modifiers(opts)
270
278
  self.class::CheckInitiateParams.new(@opts).validate if opts&.fetch(:check_initiate_params) { config.check_initiate_params }
271
279
  end
272
280
 
@@ -340,13 +348,29 @@ class Serega
340
348
  self.class.config
341
349
  end
342
350
 
343
- def prepare_modifiers(opts)
344
- opts.each_with_object({}) do |(key, value), obj|
345
- value = SeregaUtils::ToHash.call(value) if (key == :only) || (key == :except) || (key == :with)
346
- obj[key] = value
351
+ def parse_modifiers(opts)
352
+ result = {}
353
+
354
+ opts.each do |key, value|
355
+ value = parse_modifier(value) if (key == :only) || (key == :except) || (key == :with)
356
+ result[key] = value
347
357
  end
358
+
359
+ result
360
+ end
361
+
362
+ # Patched in:
363
+ # - plugin :string_modifiers (parses string modifiers differently)
364
+ def parse_modifier(value)
365
+ SeregaUtils::ToHash.call(value)
348
366
  end
349
367
 
368
+ # Patched in:
369
+ # - plugin :activerecord_preloads (loads defined :preloads to object)
370
+ # - plugin :batch (runs serialization of collected batches)
371
+ # - plugin :root (wraps result `{ root => result }`)
372
+ # - plugin :context_metadata (adds context metadata to final result)
373
+ # - plugin :metadata (adds metadata to final result)
350
374
  def serialize(object, opts)
351
375
  self.class::SeregaObjectSerializer
352
376
  .new(**opts, points: map)
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.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Glushkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-22 00:00:00.000000000 Z
11
+ date: 2023-03-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  JSON Serializer