serega 0.18.0 → 0.20.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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.0
1
+ 0.20.0
@@ -21,6 +21,10 @@ class Serega
21
21
  # @return [Boolean, nil] Attribute :many option
22
22
  attr_reader :many
23
23
 
24
+ # Attribute :default option
25
+ # @return [Object, nil] Attribute :default option
26
+ attr_reader :default
27
+
24
28
  # Attribute :hide option
25
29
  # @return [Boolean, nil] Attribute :hide option
26
30
  attr_reader :hide
@@ -108,9 +112,10 @@ class Serega
108
112
 
109
113
  def set_normalized_vars(normalizer)
110
114
  @name = normalizer.name
115
+ @many = normalizer.many
116
+ @default = normalizer.default
111
117
  @value_block = normalizer.value_block
112
118
  @hide = normalizer.hide
113
- @many = normalizer.many
114
119
  @serializer = normalizer.serializer
115
120
  end
116
121
  end
@@ -86,6 +86,19 @@ class Serega
86
86
  @serializer = prepare_serializer
87
87
  end
88
88
 
89
+ #
90
+ # Shows the default attribute value. It is a value that replaces found nils.
91
+ #
92
+ # When custom :default is not specified, we set empty array as default when `many: true` specified
93
+ #
94
+ # @return [Object] Attribute default value
95
+ #
96
+ def default
97
+ return @default if instance_variable_defined?(:@default)
98
+
99
+ @default = prepare_default
100
+ end
101
+
89
102
  private
90
103
 
91
104
  def prepare_name
@@ -97,11 +110,14 @@ class Serega
97
110
  # - plugin :formatters (wraps resulted block in formatter block and formats :const values)
98
111
  #
99
112
  def prepare_value_block
100
- prepare_init_block ||
113
+ value_block =
114
+ prepare_init_block ||
101
115
  prepare_value_option_block ||
102
116
  prepare_const_block ||
103
117
  prepare_delegate_block ||
104
118
  prepare_keyword_block
119
+
120
+ prepare_value_block_with_default(value_block)
105
121
  end
106
122
 
107
123
  #
@@ -158,6 +174,20 @@ class Serega
158
174
  end
159
175
  end
160
176
 
177
+ def prepare_value_block_with_default(callable)
178
+ default_value = default
179
+ return callable if default_value.nil?
180
+
181
+ proc { |obj, ctx|
182
+ res = callable.call(obj, ctx)
183
+ res.nil? ? default_value : res
184
+ }
185
+ end
186
+
187
+ def prepare_default
188
+ init_opts.fetch(:default) { many ? FROZEN_EMPTY_ARRAY : nil }
189
+ end
190
+
161
191
  def prepare_delegate_block
162
192
  delegate = init_opts[:delegate]
163
193
  return unless delegate
data/lib/serega/config.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "forwardable"
4
-
5
3
  class Serega
6
4
  #
7
5
  # Stores serialization config
@@ -15,7 +13,7 @@ class Serega
15
13
  DEFAULTS = {
16
14
  plugins: [],
17
15
  initiate_keys: %i[only with except check_initiate_params].freeze,
18
- attribute_keys: %i[method value serializer many hide const delegate].freeze,
16
+ attribute_keys: %i[method value serializer many hide const delegate default].freeze,
19
17
  serialize_keys: %i[context many].freeze,
20
18
  check_attribute_name: true,
21
19
  check_initiate_params: true,
data/lib/serega/errors.rb CHANGED
@@ -11,5 +11,13 @@ class Serega
11
11
  # @example
12
12
  # Serega.new(only: 'FOO')
13
13
  # # => Attribute 'FOO' not exists (Serega::AttributeNotExist)
14
- class AttributeNotExist < SeregaError; end
14
+ class AttributeNotExist < SeregaError
15
+ attr_reader :serializer, :attributes
16
+
17
+ def initialize(message = nil, serializer = nil, attributes = nil)
18
+ super(message)
19
+ @serializer = serializer
20
+ @attributes = attributes
21
+ end
22
+ end
15
23
  end
@@ -38,8 +38,8 @@ class Serega
38
38
 
39
39
  private
40
40
 
41
- def serialize_array(object)
42
- object.map { |obj| serialize_object(obj) }
41
+ def serialize_array(objects)
42
+ objects.map { |object| serialize_object(object) }
43
43
  end
44
44
 
45
45
  # Patched in:
@@ -79,11 +79,16 @@ class Serega
79
79
  end
80
80
 
81
81
  def relation_value(value, point)
82
- child_plan = point.child_plan
83
- child_serializer = point.child_object_serializer
84
- child_many = point.many
85
- serializer = child_serializer.new(context: context, plan: child_plan, many: child_many, **opts)
86
- serializer.serialize(value)
82
+ child_serializer(point).serialize(value)
83
+ end
84
+
85
+ def child_serializer(point)
86
+ point.child_object_serializer.new(
87
+ context: context,
88
+ plan: point.child_plan,
89
+ many: point.many,
90
+ **opts
91
+ )
87
92
  end
88
93
 
89
94
  def array?(object, many)
@@ -9,8 +9,6 @@ class Serega
9
9
  # SeregaPlanPoint instance methods
10
10
  #
11
11
  module InstanceMethods
12
- extend Forwardable
13
-
14
12
  # Link to current plan this point belongs to
15
13
  # @return [SeregaAttribute] Current plan
16
14
  attr_reader :plan
@@ -27,20 +25,6 @@ class Serega
27
25
  # @return [Hash] Attributes to serialize
28
26
  attr_reader :modifiers
29
27
 
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
28
  #
45
29
  # Initializes plan point
46
30
  #
@@ -60,6 +44,30 @@ class Serega
60
44
  set_normalized_vars
61
45
  end
62
46
 
47
+ # Attribute `value`
48
+ # @see SeregaAttribute::AttributeInstanceMethods#value
49
+ def value(obj, ctx)
50
+ attribute.value(obj, ctx)
51
+ end
52
+
53
+ # Attribute `name`
54
+ # @see SeregaAttribute::AttributeInstanceMethods#value
55
+ def name
56
+ attribute.name
57
+ end
58
+
59
+ # Attribute `many` option
60
+ # @see SeregaAttribute::AttributeInstanceMethods#many
61
+ def many
62
+ attribute.many
63
+ end
64
+
65
+ # Attribute `serializer` option
66
+ # @see SeregaAttribute::AttributeInstanceMethods#serializer
67
+ def serializer
68
+ attribute.serializer
69
+ end
70
+
63
71
  #
64
72
  # @return [SeregaObjectSerializer] object serializer for child plan
65
73
  #
@@ -54,17 +54,21 @@ class Serega
54
54
  # Checks requirements to load plugin
55
55
  #
56
56
  # @param serializer_class [Class<Serega>] Current serializer class
57
- # @param _opts [Hash] plugins options
57
+ # @param opts [Hash] plugin options
58
58
  #
59
59
  # @return [void]
60
60
  #
61
- def self.before_load_plugin(serializer_class, **_opts)
61
+ def self.before_load_plugin(serializer_class, **opts)
62
+ opts.each_key do |key|
63
+ raise SeregaError, "Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. No options are allowed"
64
+ end
65
+
62
66
  unless serializer_class.plugin_used?(:preloads)
63
- raise SeregaError, "Please load `plugin :preloads` first"
67
+ raise SeregaError, "Plugin #{plugin_name.inspect} must be loaded after the :preloads plugin. Please load the :preloads plugin first"
64
68
  end
65
69
 
66
70
  if serializer_class.plugin_used?(:batch)
67
- raise SeregaError, "Plugin `activerecord_preloads` must be loaded before `batch`"
71
+ raise SeregaError, "Plugin #{plugin_name.inspect} must be loaded before the :batch plugin"
68
72
  end
69
73
  end
70
74
 
@@ -72,7 +76,7 @@ class Serega
72
76
  # Applies plugin code to specific serializer
73
77
  #
74
78
  # @param serializer_class [Class<Serega>] Current serializer class
75
- # @param _opts [Hash] Loaded plugins options
79
+ # @param _opts [Hash] Plugin options
76
80
  #
77
81
  # @return [void]
78
82
  #
@@ -14,7 +14,7 @@ class Serega
14
14
  # end
15
15
  #
16
16
  # class UserSerializer < AppSerializer
17
- # attribute :comments_count, batch: { loader: CommentsCountBatchLoader, default: 0 }
17
+ # attribute :comments_count, batch: { loader: CommentsCountBatchLoader }, default: 0
18
18
  # attribute :company, serializer: CompanySerializer, batch: { loader: UserCompanyBatchLoader }
19
19
  # end
20
20
  #
@@ -25,11 +25,30 @@ class Serega
25
25
  :batch
26
26
  end
27
27
 
28
+ # Checks requirements to load plugin
29
+ #
30
+ # @param serializer_class [Class<Serega>] Current serializer class
31
+ # @param opts [Hash] plugin options
32
+ #
33
+ # @return [void]
34
+ #
35
+ def self.before_load_plugin(serializer_class, **opts)
36
+ allowed_keys = %i[auto_hide id_method]
37
+ opts.each_key do |key|
38
+ next if allowed_keys.include?(key)
39
+
40
+ raise SeregaError,
41
+ "Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. Allowed options:\n" \
42
+ " - :auto_hide [Boolean] - Marks attribute as hidden when it has :batch loader specified\n" \
43
+ " - :id_method [Symbol, #call] - Specified the default method to use to find object identifier"
44
+ end
45
+ end
46
+
28
47
  #
29
48
  # Applies plugin code to specific serializer
30
49
  #
31
50
  # @param serializer_class [Class<Serega>] Current serializer class
32
- # @param _opts [Hash] Loaded plugins options
51
+ # @param _opts [Hash] Plugin options
33
52
  #
34
53
  # @return [void]
35
54
  #
@@ -60,7 +79,7 @@ class Serega
60
79
  # Runs callbacks after plugin was attached
61
80
  #
62
81
  # @param serializer_class [Class<Serega>] Current serializer class
63
- # @param opts [Hash] loaded plugins opts
82
+ # @param opts [Hash] Plugin options
64
83
  #
65
84
  # @return [void]
66
85
  #
@@ -47,8 +47,6 @@ class Serega
47
47
  id_method = batch[:id_method] || self.class.serializer_class.config.batch.id_method
48
48
  id_method = prepare_batch_id_method(id_method)
49
49
 
50
- default = batch.fetch(:default) { many ? FROZEN_EMPTY_ARRAY : nil }
51
-
52
50
  {loader: loader, id_method: id_method, default: default}
53
51
  end
54
52
 
@@ -23,7 +23,7 @@ class Serega
23
23
  SeregaValidations::Utils::CheckOptIsHash.call(opts, :batch)
24
24
 
25
25
  batch = opts[:batch]
26
- SeregaValidations::Utils::CheckAllowedKeys.call(batch, %i[id_method loader default], :batch)
26
+ SeregaValidations::Utils::CheckAllowedKeys.call(batch, %i[id_method loader], :batch)
27
27
 
28
28
  check_batch_opt_id_method(batch, serializer_class)
29
29
  check_batch_opt_loader(batch, serializer_class)
@@ -48,11 +48,29 @@ class Serega
48
48
  :camel_case
49
49
  end
50
50
 
51
+ # Checks requirements to load plugin
52
+ #
53
+ # @param serializer_class [Class<Serega>] Current serializer class
54
+ # @param opts [Hash] plugin options
55
+ #
56
+ # @return [void]
57
+ #
58
+ def self.before_load_plugin(serializer_class, **opts)
59
+ allowed_keys = %i[transform]
60
+ opts.each_key do |key|
61
+ next if allowed_keys.include?(key)
62
+
63
+ raise SeregaError,
64
+ "Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. Allowed options:\n" \
65
+ " - :transform [#call] - Custom transformation"
66
+ end
67
+ end
68
+
51
69
  #
52
70
  # Applies plugin code to specific serializer
53
71
  #
54
72
  # @param serializer_class [Class<Serega>] Current serializer class
55
- # @param _opts [Hash] Loaded plugins options
73
+ # @param _opts [Hash] Plugin options
56
74
  #
57
75
  # @return [void]
58
76
  #
@@ -66,7 +84,7 @@ class Serega
66
84
  # Adds config options and runs other callbacks after plugin was loaded
67
85
  #
68
86
  # @param serializer_class [Class<Serega>] Current serializer class
69
- # @param opts [Hash] loaded plugins opts
87
+ # @param opts [Hash] Plugin options
70
88
  #
71
89
  # @return [void]
72
90
  #
@@ -30,13 +30,22 @@ class Serega
30
30
  # Checks requirements and loads additional plugins
31
31
  #
32
32
  # @param serializer_class [Class<Serega>] Current serializer class
33
- # @param opts [Hash] loaded plugins opts
33
+ # @param opts [Hash] Plugin options
34
34
  #
35
35
  # @return [void]
36
36
  #
37
37
  def self.before_load_plugin(serializer_class, **opts)
38
+ allowed_keys = %i[context_metadata_key]
39
+ opts.each_key do |key|
40
+ next if allowed_keys.include?(key)
41
+
42
+ raise SeregaError,
43
+ "Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. Allowed options:\n" \
44
+ " - :context_metadata_key [Symbol] - The key name that must be used to add metadata. Default is :meta."
45
+ end
46
+
38
47
  unless serializer_class.plugin_used?(:root)
39
- raise SeregaError, "Please load :root plugin first so we can wrap serialization response into top-level hash to add metadata there"
48
+ raise SeregaError, "Plugin #{plugin_name.inspect} must be loaded after the :root plugin. Please load the :root plugin first"
40
49
  end
41
50
  end
42
51
 
@@ -44,7 +53,7 @@ class Serega
44
53
  # Applies plugin code to specific serializer
45
54
  #
46
55
  # @param serializer_class [Class<Serega>] Current serializer class
47
- # @param _opts [Hash] Loaded plugins options
56
+ # @param _opts [Hash] Plugin options
48
57
  #
49
58
  # @return [void]
50
59
  #
@@ -58,7 +67,7 @@ class Serega
58
67
  # Adds config options and runs other callbacks after plugin was loaded
59
68
  #
60
69
  # @param serializer_class [Class<Serega>] Current serializer class
61
- # @param opts [Hash] loaded plugins opts
70
+ # @param opts [Hash] Plugin options
62
71
  #
63
72
  # @return [void]
64
73
  #
@@ -41,11 +41,29 @@ class Serega
41
41
  :depth_limit
42
42
  end
43
43
 
44
+ # Checks requirements
45
+ #
46
+ # @param serializer_class [Class<Serega>] Current serializer class
47
+ # @param opts [Hash] Plugin options
48
+ #
49
+ # @return [void]
50
+ #
51
+ def self.before_load_plugin(serializer_class, **opts)
52
+ allowed_keys = %i[limit]
53
+ opts.each_key do |key|
54
+ next if allowed_keys.include?(key)
55
+
56
+ raise SeregaError,
57
+ "Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. Allowed options:\n" \
58
+ " - :limit [Integer] - Maximum serialization depth."
59
+ end
60
+ end
61
+
44
62
  #
45
63
  # Applies plugin code to specific serializer
46
64
  #
47
65
  # @param serializer_class [Class<Serega>] Current serializer class
48
- # @param _opts [Hash] Loaded plugins options
66
+ # @param _opts [Hash] Plugin options
49
67
  #
50
68
  # @return [void]
51
69
  #
@@ -58,7 +76,7 @@ class Serega
58
76
  # Adds config options and runs other callbacks after plugin was loaded
59
77
  #
60
78
  # @param serializer_class [Class<Serega>] Current serializer class
61
- # @param opts [Hash] loaded plugins opts
79
+ # @param opts [Hash] Plugin options
62
80
  #
63
81
  # @return [void]
64
82
  #
@@ -35,7 +35,7 @@ class Serega
35
35
  # Applies plugin code to specific serializer
36
36
  #
37
37
  # @param serializer_class [Class<Serega>] Current serializer class
38
- # @param _opts [Hash] Loaded plugins options
38
+ # @param _opts [Hash] Plugin options
39
39
  #
40
40
  # @return [void]
41
41
  #
@@ -51,13 +51,22 @@ class Serega
51
51
  # Checks requirements and loads additional plugins
52
52
  #
53
53
  # @param serializer_class [Class<Serega>] Current serializer class
54
- # @param opts [Hash] loaded plugins opts
54
+ # @param opts [Hash] Plugin options
55
55
  #
56
56
  # @return [void]
57
57
  #
58
58
  def self.before_load_plugin(serializer_class, **opts)
59
+ allowed_keys = %i[formatters]
60
+ opts.each_key do |key|
61
+ next if allowed_keys.include?(key)
62
+
63
+ raise SeregaError,
64
+ "Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. Allowed options:\n" \
65
+ " - :formatters [Hash<Symbol, #call>] - Formatters (names and according callable values)"
66
+ end
67
+
59
68
  if serializer_class.plugin_used?(:batch)
60
- raise SeregaError, "Plugin `formatters` must be loaded before `batch`"
69
+ raise SeregaError, "Plugin #{plugin_name.inspect} must be loaded before the :batch plugin"
61
70
  end
62
71
  end
63
72
 
@@ -65,7 +74,7 @@ class Serega
65
74
  # Applies plugin code to specific serializer
66
75
  #
67
76
  # @param serializer_class [Class<Serega>] Current serializer class
68
- # @param _opts [Hash] Loaded plugins options
77
+ # @param _opts [Hash] Plugin options
69
78
  #
70
79
  # @return [void]
71
80
  #
@@ -79,7 +88,7 @@ class Serega
79
88
  # Adds config options and runs other callbacks after plugin was loaded
80
89
  #
81
90
  # @param serializer_class [Class<Serega>] Current serializer class
82
- # @param opts [Hash] loaded plugins opts
91
+ # @param opts [Hash] Plugin options
83
92
  #
84
93
  # @return [void]
85
94
  #
@@ -51,7 +51,7 @@ class Serega
51
51
  # Applies plugin code to specific serializer
52
52
  #
53
53
  # @param serializer_class [Class<Serega>] Current serializer class
54
- # @param _opts [Hash] Loaded plugins options
54
+ # @param _opts [Hash] Plugin options
55
55
  #
56
56
  # @return [void]
57
57
  #
@@ -72,7 +72,7 @@ class Serega
72
72
  # Adds config options and runs other callbacks after plugin was loaded
73
73
  #
74
74
  # @param serializer_class [Class<Serega>] Current serializer class
75
- # @param opts [Hash] loaded plugins opts
75
+ # @param opts [Hash] Plugin options
76
76
  #
77
77
  # @return [void]
78
78
  #
@@ -65,7 +65,7 @@ class Serega
65
65
  def normalize_block(value, const, block)
66
66
  return proc { const } if const
67
67
 
68
- callable = (value || block)
68
+ callable = value || block
69
69
  params_count = SeregaUtils::ParamsCount.call(callable, max_count: 2)
70
70
 
71
71
  case params_count
@@ -46,13 +46,13 @@ class Serega
46
46
  # Checks requirements and loads additional plugins
47
47
  #
48
48
  # @param serializer_class [Class<Serega>] Current serializer class
49
- # @param _opts [Hash] loaded plugins opts
49
+ # @param _opts [Hash] Plugin options
50
50
  #
51
51
  # @return [void]
52
52
  #
53
53
  def self.before_load_plugin(serializer_class, **_opts)
54
54
  unless serializer_class.plugin_used?(:root)
55
- raise SeregaError, "Please load :root plugin first so we can wrap serialization response into top-level hash to add metadata there"
55
+ raise SeregaError, "Plugin #{plugin_name.inspect} must be loaded after the :root plugin. Please load the :root plugin first"
56
56
  end
57
57
  end
58
58
 
@@ -60,7 +60,7 @@ class Serega
60
60
  # Applies plugin code to specific serializer
61
61
  #
62
62
  # @param serializer_class [Class<Serega>] Current serializer class
63
- # @param _opts [Hash] Loaded plugins options
63
+ # @param _opts [Hash] Plugin options
64
64
  #
65
65
  # @return [void]
66
66
  #
@@ -87,7 +87,7 @@ class Serega
87
87
  # Adds config options and runs other callbacks after plugin was loaded
88
88
  #
89
89
  # @param serializer_class [Class<Serega>] Current serializer class
90
- # @param _opts [Hash] loaded plugins opts
90
+ # @param _opts [Hash] Plugin options
91
91
  #
92
92
  # @return [void]
93
93
  #
@@ -51,7 +51,7 @@ class Serega
51
51
  opts.fetch(method_name)
52
52
  end
53
53
 
54
- define_method("#{method_name}=") do |value|
54
+ define_method(:"#{method_name}=") do |value|
55
55
  raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
56
56
  opts[method_name] = value
57
57
  end
@@ -70,11 +70,31 @@ class Serega
70
70
  :preloads
71
71
  end
72
72
 
73
+ # Checks requirements to load plugin
74
+ #
75
+ # @param serializer_class [Class<Serega>] Current serializer class
76
+ # @param opts [Hash] plugin options
77
+ #
78
+ # @return [void]
79
+ #
80
+ def self.before_load_plugin(serializer_class, **opts)
81
+ allowed_keys = DEFAULT_CONFIG.keys
82
+ opts.each_key do |key|
83
+ next if allowed_keys.include?(key)
84
+
85
+ raise SeregaError,
86
+ "Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. Allowed options:\n" \
87
+ " - :auto_preload_attributes_with_delegate [Boolean] - Automatically adds `preload: <delegate_to>` option to attributes with :delegate option specified\n" \
88
+ " - :auto_preload_attributes_with_serializer [Boolean] - Automatically adds `preload: <attribute_name>` option to attributes with :serializer option specified\n" \
89
+ " - :auto_hide_attributes_with_preload [Boolean] - Automatically adds `hide: true` option to attributes with :preload option (specified manually or added automatically)"
90
+ end
91
+ end
92
+
73
93
  #
74
94
  # Applies plugin code to specific serializer
75
95
  #
76
96
  # @param serializer_class [Class<Serega>] Current serializer class
77
- # @param _opts [Hash] Loaded plugins options
97
+ # @param _opts [Hash] Plugin options
78
98
  #
79
99
  # @return [void]
80
100
  #
@@ -104,7 +124,7 @@ class Serega
104
124
  # Adds config options and runs other callbacks after plugin was loaded
105
125
  #
106
126
  # @param serializer_class [Class<Serega>] Current serializer class
107
- # @param opts [Hash] loaded plugins opts
127
+ # @param opts [Hash] Plugin options
108
128
  #
109
129
  # @return [void]
110
130
  #
@@ -29,7 +29,7 @@ class Serega
29
29
  # Applies plugin code to specific serializer
30
30
  #
31
31
  # @param serializer_class [Class<Serega>] Current serializer class
32
- # @param _opts [Hash] Loaded plugins options
32
+ # @param _opts [Hash] Plugin options
33
33
  #
34
34
  # @return [void]
35
35
  #
@@ -42,7 +42,7 @@ class Serega
42
42
  # Runs callbacks after plugin was attached
43
43
  #
44
44
  # @param serializer_class [Class<Serega>] Current serializer class
45
- # @param _opts [Hash] loaded plugins opts
45
+ # @param _opts [Hash] Plugin options
46
46
  #
47
47
  # @return [void]
48
48
  #