serega 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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