serega 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/serega/attribute.rb +2 -2
  4. data/lib/serega/config.rb +23 -1
  5. data/lib/serega/errors.rb +8 -5
  6. data/lib/serega/helpers/serializer_class_helper.rb +5 -0
  7. data/lib/serega/json/adapter.rb +6 -0
  8. data/lib/serega/json/json.rb +20 -0
  9. data/lib/serega/json/oj.rb +20 -0
  10. data/lib/serega/map.rb +17 -0
  11. data/lib/serega/map_point.rb +36 -1
  12. data/lib/serega/object_serializer.rb +17 -4
  13. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +66 -18
  14. data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +100 -40
  15. data/lib/serega/plugins/batch/batch.rb +136 -10
  16. data/lib/serega/plugins/batch/lib/loader.rb +33 -1
  17. data/lib/serega/plugins/batch/lib/loaders.rb +15 -2
  18. data/lib/serega/plugins/batch/lib/plugins_extensions.rb +23 -1
  19. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb +12 -0
  20. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +12 -0
  21. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +12 -0
  22. data/lib/serega/plugins/context_metadata/context_metadata.rb +95 -13
  23. data/lib/serega/plugins/formatters/formatters.rb +94 -8
  24. data/lib/serega/plugins/hide_nil/hide_nil.rb +33 -7
  25. data/lib/serega/plugins/metadata/metadata.rb +108 -35
  26. data/lib/serega/plugins/metadata/validations/check_block.rb +3 -0
  27. data/lib/serega/plugins/metadata/validations/check_opt_hide_empty.rb +4 -1
  28. data/lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb +4 -1
  29. data/lib/serega/plugins/metadata/validations/check_opts.rb +3 -0
  30. data/lib/serega/plugins/metadata/validations/check_path.rb +3 -0
  31. data/lib/serega/plugins/preloads/lib/enum_deep_freeze.rb +10 -1
  32. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +13 -4
  33. data/lib/serega/plugins/preloads/lib/main_preload_path.rb +16 -3
  34. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +4 -6
  35. data/lib/serega/plugins/preloads/preloads.rb +145 -12
  36. data/lib/serega/plugins/preloads/validations/check_opt_preload.rb +11 -0
  37. data/lib/serega/plugins/preloads/validations/check_opt_preload_path.rb +12 -0
  38. data/lib/serega/plugins/presenter/presenter.rb +20 -11
  39. data/lib/serega/plugins/root/root.rb +131 -19
  40. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +42 -12
  41. data/lib/serega/plugins/string_modifiers/string_modifiers.rb +14 -0
  42. data/lib/serega/plugins.rb +7 -1
  43. data/lib/serega/utils/enum_deep_dup.rb +5 -0
  44. data/lib/serega/utils/to_hash.rb +21 -3
  45. data/lib/serega/validations/attribute/check_block.rb +7 -2
  46. data/lib/serega/validations/attribute/check_name.rb +6 -0
  47. data/lib/serega/validations/attribute/check_opt_const.rb +12 -9
  48. data/lib/serega/validations/attribute/check_opt_delegate.rb +13 -10
  49. data/lib/serega/validations/attribute/check_opt_hide.rb +3 -0
  50. data/lib/serega/validations/attribute/check_opt_key.rb +12 -9
  51. data/lib/serega/validations/attribute/check_opt_many.rb +3 -0
  52. data/lib/serega/validations/attribute/check_opt_serializer.rb +3 -0
  53. data/lib/serega/validations/attribute/check_opt_value.rb +12 -9
  54. data/lib/serega/validations/check_attribute_params.rb +29 -1
  55. data/lib/serega/validations/check_initiate_params.rb +17 -0
  56. data/lib/serega/validations/check_serialize_params.rb +16 -0
  57. data/lib/serega/validations/initiate/check_modifiers.rb +15 -0
  58. data/lib/serega/validations/utils/check_allowed_keys.rb +14 -0
  59. data/lib/serega/validations/utils/check_opt_is_bool.rb +11 -0
  60. data/lib/serega/validations/utils/check_opt_is_hash.rb +11 -0
  61. data/lib/serega/validations/utils/check_opt_is_string_or_symbol.rb +11 -0
  62. data/lib/serega/version.rb +4 -0
  63. data/lib/serega.rb +83 -24
  64. metadata +2 -3
  65. data/lib/serega/serializer.rb +0 -32
@@ -2,23 +2,66 @@
2
2
 
3
3
  class Serega
4
4
  module SeregaPlugins
5
+ #
6
+ # Plugin :context_metadata
7
+ #
8
+ # Depends on: `:root` plugin, that must be loaded first
9
+ #
10
+ # Allows to specify metadata to be added to serialized response.
11
+ #
12
+ # @example
13
+ # class UserSerializer < Serega
14
+ # plugin :root, root: :data
15
+ # plugin :context_metadata, context_metadata_key: :meta
16
+ # end
17
+ #
18
+ # UserSerializer.to_h(nil, meta: { version: '1.0.1' })
19
+ # # => {:data=>nil, :version=>"1.0.1"}
20
+ #
5
21
  module ContextMetadata
22
+ # Default context metadata option name
6
23
  DEFAULT_CONTEXT_METADATA_KEY = :meta
7
24
 
25
+ # @return [Symbol] Plugin name
8
26
  def self.plugin_name
9
27
  :context_metadata
10
28
  end
11
29
 
30
+ # Checks requirements and loads additional plugins
31
+ #
32
+ # @param serializer_class [Class<Serega>] Current serializer class
33
+ # @param opts [Hash] loaded plugins opts
34
+ #
35
+ # @return [void]
36
+ #
12
37
  def self.before_load_plugin(serializer_class, **opts)
13
- serializer_class.plugin(:root, **opts) unless serializer_class.plugin_used?(:root)
38
+ 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"
40
+ end
14
41
  end
15
42
 
43
+ #
44
+ # Applies plugin code to specific serializer
45
+ #
46
+ # @param serializer_class [Class<Serega>] Current serializer class
47
+ # @param _opts [Hash] Loaded plugins options
48
+ #
49
+ # @return [void]
50
+ #
16
51
  def self.load_plugin(serializer_class, **_opts)
52
+ serializer_class.include(InstanceMethods)
17
53
  serializer_class::SeregaConfig.include(ConfigInstanceMethods)
18
- serializer_class::SeregaSerializer.include(SeregaSerializerInstanceMethods)
19
54
  serializer_class::CheckSerializeParams.include(CheckSerializeParamsInstanceMethods)
20
55
  end
21
56
 
57
+ #
58
+ # Adds config options and runs other callbacks after plugin was loaded
59
+ #
60
+ # @param serializer_class [Class<Serega>] Current serializer class
61
+ # @param opts [Hash] loaded plugins opts
62
+ #
63
+ # @return [void]
64
+ #
22
65
  def self.after_load_plugin(serializer_class, **opts)
23
66
  config = serializer_class.config
24
67
  meta_key = opts[:context_metadata_key] || DEFAULT_CONTEXT_METADATA_KEY
@@ -26,29 +69,58 @@ class Serega
26
69
  config.serialize_keys << meta_key
27
70
  end
28
71
 
72
+ #
73
+ # Config for `context_metadata` plugin
74
+ #
29
75
  class ContextMetadataConfig
76
+ # @return [Hash] context_metadata options
30
77
  attr_reader :opts
31
78
 
79
+ #
80
+ # Initializes context_metadata config object
81
+ #
82
+ # @param opts [Hash] options
83
+ #
84
+ # @return [Serega::SeregaPlugins::ContextMetadata::ContextMetadataConfig]
32
85
  def initialize(opts)
33
86
  @opts = opts
34
87
  end
35
88
 
89
+ # Key that should be used to define metadata
36
90
  def key
37
91
  opts.fetch(:key)
38
92
  end
39
93
 
40
- def key=(value)
41
- opts[:key] = value
94
+ # Sets key that should be used to define metadata
95
+ #
96
+ # @param new_key [Symbol] New key
97
+ #
98
+ # @return [Symbol] New key
99
+ def key=(new_key)
100
+ opts[:key] = new_key
42
101
  end
43
102
  end
44
103
 
104
+ #
105
+ # Config class additional/patched instance methods
106
+ #
107
+ # @see Serega::SeregaConfig
108
+ #
45
109
  module ConfigInstanceMethods
110
+ # @return [Serega::SeregaPlugins::ContextMetadata::ContextMetadataConfig] context_metadata config
46
111
  def context_metadata
47
112
  @context_metadata ||= ContextMetadataConfig.new(opts.fetch(:context_metadata))
48
113
  end
49
114
  end
50
115
 
116
+ #
117
+ # CheckSerializeParams class additional/patched instance methods
118
+ #
119
+ # @see Serega::SeregaValidations::CheckSerializeParams
120
+ #
51
121
  module CheckSerializeParamsInstanceMethods
122
+ private
123
+
52
124
  def check_opts
53
125
  super
54
126
 
@@ -57,17 +129,27 @@ class Serega
57
129
  end
58
130
  end
59
131
 
60
- module SeregaSerializerInstanceMethods
61
- def serialize(object)
62
- super.tap do |hash|
63
- add_context_metadata(hash)
64
- end
65
- end
66
-
132
+ #
133
+ # Serega additional/patched instance methods
134
+ #
135
+ # @see Serega
136
+ #
137
+ module InstanceMethods
67
138
  private
68
139
 
69
- def add_context_metadata(hash)
70
- context_metadata_key = self.class.serializer_class.config.context_metadata.key
140
+ def serialize(object, opts)
141
+ result = super
142
+ return result unless result.is_a?(Hash) # return earlier if not a hash, so no root was added
143
+
144
+ root = build_root(object, opts)
145
+ return result unless root # return earlier when no root
146
+
147
+ add_context_metadata(result, opts)
148
+ result
149
+ end
150
+
151
+ def add_context_metadata(hash, opts)
152
+ context_metadata_key = self.class.config.context_metadata.key
71
153
  return unless context_metadata_key
72
154
 
73
155
  metadata = opts[context_metadata_key]
@@ -2,22 +2,82 @@
2
2
 
3
3
  class Serega
4
4
  module SeregaPlugins
5
+ #
6
+ # Plugin :formatters
7
+ #
8
+ # Allows to define value formatters one time and apply them on any attributes.
9
+ #
10
+ # Config option `config.formatters.add()` can be used to add formatters.
11
+ #
12
+ # Attribute option `:format` now can be used with name of formatter or with callable instance.
13
+ #
14
+ # @example
15
+ # class AppSerializer < Serega
16
+ # plugin :formatters, formatters: {
17
+ # iso8601: ->(value) { time.iso8601.round(6) },
18
+ # on_off: ->(value) { value ? 'ON' : 'OFF' },
19
+ # money: ->(value) { value.round(2) }
20
+ # }
21
+ # end
22
+ #
23
+ # class UserSerializer < Serega
24
+ # # Additionally we can add formatters via config in subclasses
25
+ # config.formatters.add(
26
+ # iso8601: ->(value) { time.iso8601.round(6) },
27
+ # on_off: ->(value) { value ? 'ON' : 'OFF' },
28
+ # money: ->(value) { value.round(2) }
29
+ # )
30
+ #
31
+ # # Using predefined formatter
32
+ # attribute :commission, format: :money
33
+ # attribute :is_logined, format: :on_off
34
+ # attribute :created_at, format: :iso8601
35
+ # attribute :updated_at, format: :iso8601
36
+ #
37
+ # # Using `callable` formatter
38
+ # attribute :score_percent, format: proc { |percent| "#{percent.round(2)}%" }
39
+ # end
40
+ #
5
41
  module Formatters
42
+ # @return [Symbol] Plugin name
6
43
  def self.plugin_name
7
44
  :formatters
8
45
  end
9
46
 
47
+ # Checks requirements and loads additional plugins
48
+ #
49
+ # @param serializer_class [Class<Serega>] Current serializer class
50
+ # @param opts [Hash] loaded plugins opts
51
+ #
52
+ # @return [void]
53
+ #
10
54
  def self.before_load_plugin(serializer_class, **opts)
11
55
  if serializer_class.plugin_used?(:batch)
12
56
  raise SeregaError, "Plugin `formatters` must be loaded before `batch`"
13
57
  end
14
58
  end
15
59
 
60
+ #
61
+ # Applies plugin code to specific serializer
62
+ #
63
+ # @param serializer_class [Class<Serega>] Current serializer class
64
+ # @param _opts [Hash] Loaded plugins options
65
+ #
66
+ # @return [void]
67
+ #
16
68
  def self.load_plugin(serializer_class, **_opts)
17
69
  serializer_class::SeregaConfig.include(ConfigInstanceMethods)
18
70
  serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
19
71
  end
20
72
 
73
+ #
74
+ # Adds config options and runs other callbacks after plugin was loaded
75
+ #
76
+ # @param serializer_class [Class<Serega>] Current serializer class
77
+ # @param opts [Hash] loaded plugins opts
78
+ #
79
+ # @return [void]
80
+ #
21
81
  def self.after_load_plugin(serializer_class, **opts)
22
82
  config = serializer_class.config
23
83
  config.opts[:formatters] = {}
@@ -25,13 +85,26 @@ class Serega
25
85
  config.attribute_keys << :format
26
86
  end
27
87
 
88
+ # Formatters plugin config
28
89
  class FormattersConfig
29
90
  attr_reader :opts
30
91
 
92
+ #
93
+ # Initializes formatters config object
94
+ #
95
+ # @param opts [Hash] options
96
+ #
97
+ # @return FormattersConfig
31
98
  def initialize(opts)
32
99
  @opts = opts
33
100
  end
34
101
 
102
+ # Adds new formatters
103
+ #
104
+ # @param formatters [Hash<Symbol, #call>] hash key is a formatter name and
105
+ # hash value is a callable instance to format value
106
+ #
107
+ # @return [void]
35
108
  def add(formatters)
36
109
  formatters.each_pair do |key, value|
37
110
  opts[key] = value
@@ -39,27 +112,32 @@ class Serega
39
112
  end
40
113
  end
41
114
 
115
+ #
116
+ # Config class additional/patched instance methods
117
+ #
118
+ # @see Serega::SeregaConfig
119
+ #
42
120
  module ConfigInstanceMethods
121
+ # @return [Serega::SeregaPlugins::Formatters::FormattersConfig] current formatters config
43
122
  def formatters
44
123
  @formatters ||= FormattersConfig.new(opts.fetch(:formatters))
45
124
  end
46
125
  end
47
126
 
127
+ #
128
+ # Attribute class additional/patched instance methods
129
+ #
130
+ # @see Serega::SeregaAttribute
131
+ #
48
132
  module AttributeInstanceMethods
133
+ # @return [#call] callable formatter
49
134
  def formatter
50
135
  return @formatter if instance_variable_defined?(:@formatter)
51
136
 
52
137
  @formatter = formatter_resolved
53
138
  end
54
139
 
55
- def formatter_resolved
56
- formatter = opts[:format]
57
- return unless formatter
58
-
59
- formatter = self.class.serializer_class.config.formatters.opts.fetch(formatter) if formatter.is_a?(Symbol)
60
- formatter
61
- end
62
-
140
+ # @return [#call] callable, that should be used to fetch attribute value
63
141
  def value_block
64
142
  return @value_block if instance_variable_defined?(:@value_block)
65
143
  return @value_block = super unless formatter
@@ -77,6 +155,14 @@ class Serega
77
155
 
78
156
  private
79
157
 
158
+ def formatter_resolved
159
+ formatter = opts[:format]
160
+ return unless formatter
161
+
162
+ formatter = self.class.serializer_class.config.formatters.opts.fetch(formatter) if formatter.is_a?(Symbol)
163
+ formatter
164
+ end
165
+
80
166
  def formatted_block(formatter, original_block)
81
167
  proc do |object, context|
82
168
  value = original_block.call(object, context)
@@ -7,36 +7,54 @@ class Serega
7
7
  # if value is nil
8
8
  #
9
9
  module HideNil
10
- # @return [Symbol] plugin name
10
+ # @return [Symbol] Plugin name
11
11
  def self.plugin_name
12
12
  :hide_nil
13
13
  end
14
14
 
15
15
  #
16
- # Includes plugin modules to current serializer
16
+ # Applies plugin code to specific serializer
17
17
  #
18
- # @param serializer_class [Class] current serializer class
19
- # @param _opts [Hash] plugin opts
18
+ # @param serializer_class [Class<Serega>] Current serializer class
19
+ # @param _opts [Hash] Loaded plugins options
20
20
  #
21
21
  # @return [void]
22
22
  #
23
23
  def self.load_plugin(serializer_class, **_opts)
24
- serializer_class::SeregaAttribute.include(AttributeMethods)
24
+ serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
25
25
  serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
26
26
  serializer_class::SeregaObjectSerializer.include(SeregaObjectSerializerInstanceMethods)
27
27
  end
28
28
 
29
+ #
30
+ # Adds config options and runs other callbacks after plugin was loaded
31
+ #
32
+ # @param serializer_class [Class<Serega>] Current serializer class
33
+ # @param opts [Hash] loaded plugins opts
34
+ #
35
+ # @return [void]
36
+ #
29
37
  def self.after_load_plugin(serializer_class, **opts)
30
38
  serializer_class.config.attribute_keys << :hide_nil
31
39
  end
32
40
 
33
- # Adds #hide_nil? Attribute instance method
34
- module AttributeMethods
41
+ #
42
+ # Serega::SeregaAttribute additional/patched instance methods
43
+ #
44
+ # @see Serega::SeregaValidations::CheckAttributeParams
45
+ #
46
+ module AttributeInstanceMethods
47
+ # Check hide_nil is specified
35
48
  def hide_nil?
36
49
  !!opts[:hide_nil]
37
50
  end
38
51
  end
39
52
 
53
+ #
54
+ # Serega::SeregaValidations::CheckAttributeParams additional/patched class methods
55
+ #
56
+ # @see Serega::SeregaValidations::CheckAttributeParams
57
+ #
40
58
  module CheckAttributeParamsInstanceMethods
41
59
  private
42
60
 
@@ -46,6 +64,9 @@ class Serega
46
64
  end
47
65
  end
48
66
 
67
+ #
68
+ # Validator class for :hide_nil attribute option
69
+ #
49
70
  class CheckOptHideNil
50
71
  #
51
72
  # Checks attribute :hide_nil option
@@ -66,6 +87,11 @@ class Serega
66
87
  end
67
88
  end
68
89
 
90
+ #
91
+ # SeregaObjectSerializer additional/patched class methods
92
+ #
93
+ # @see Serega::SeregaObjectSerializer
94
+ #
69
95
  module SeregaObjectSerializerInstanceMethods
70
96
  private
71
97
 
@@ -2,25 +2,65 @@
2
2
 
3
3
  class Serega
4
4
  module SeregaPlugins
5
+ #
6
+ # Plugin `:metadata`
7
+ #
8
+ # Depends on: `:root` plugin, that must be loaded first
9
+ #
10
+ # Adds ability to describe metadata that must be added to serialized response
11
+ #
12
+ # Added class-level method `:meta_attribute`, to define metadata, it accepts:
13
+ # - *path [Array<Symbol>] - nested hash keys beginning from the root object.
14
+ # - **options [Hash] - defaults are `hide_nil: false, hide_empty: false`
15
+ # - &block [Proc] - describes value for current meta attribute
16
+ #
17
+ # @example
18
+ # class AppSerializer < Serega
19
+ # plugin :root
20
+ # plugin :metadata
21
+ #
22
+ # meta_attribute(:version) { '1.2.3' }
23
+ # meta_attribute(:ab_tests, :names) { %i[foo bar] }
24
+ # meta_attribute(:meta, :paging, hide_nil: true) do |records, ctx|
25
+ # next unless records.respond_to?(:total_count)
26
+ #
27
+ # { page: records.page, per_page: records.per_page, total_count: records.total_count }
28
+ # end
29
+ # end
30
+ #
31
+ # AppSerializer.to_h(nil) # => {:data=>nil, :version=>"1.2.3", :ab_tests=>{:names=>[:foo, :bar]}}
32
+ #
5
33
  module Metadata
34
+ # @return [Symbol] Plugin name
6
35
  def self.plugin_name
7
36
  :metadata
8
37
  end
9
38
 
10
- def self.before_load_plugin(serializer_class, **opts)
11
- if serializer_class.plugin_used?(:root)
12
- root = serializer_class.config.root
13
- root.one = opts[:root_one] if opts.key?(:root_one)
14
- root.many = opts[:root_many] if opts.key?(:root_many)
15
- else
16
- serializer_class.plugin(:root, **opts)
39
+ # Checks requirements and loads additional plugins
40
+ #
41
+ # @param serializer_class [Class<Serega>] Current serializer class
42
+ # @param _opts [Hash] loaded plugins opts
43
+ #
44
+ # @return [void]
45
+ #
46
+ def self.before_load_plugin(serializer_class, **_opts)
47
+ unless serializer_class.plugin_used?(:root)
48
+ raise SeregaError, "Please load :root plugin first so we can wrap serialization response into top-level hash to add metadata there"
17
49
  end
18
50
  end
19
51
 
52
+ #
53
+ # Applies plugin code to specific serializer
54
+ #
55
+ # @param serializer_class [Class<Serega>] Current serializer class
56
+ # @param _opts [Hash] Loaded plugins options
57
+ #
58
+ # @return [void]
59
+ #
20
60
  def self.load_plugin(serializer_class, **_opts)
21
61
  serializer_class.extend(ClassMethods)
62
+ serializer_class.include(InstanceMethods)
22
63
  serializer_class::SeregaConfig.include(ConfigInstanceMethods)
23
- serializer_class::SeregaSerializer.include(SeregaSerializerInstanceMethods)
24
64
 
25
65
  require_relative "./meta_attribute"
26
66
  require_relative "./validations/check_block"
@@ -34,28 +74,61 @@ class Serega
34
74
  serializer_class.const_set(:MetaAttribute, meta_attribute_class)
35
75
  end
36
76
 
77
+ #
78
+ # Adds config options and runs other callbacks after plugin was loaded
79
+ #
80
+ # @param serializer_class [Class<Serega>] Current serializer class
81
+ # @param _opts [Hash] loaded plugins opts
82
+ #
83
+ # @return [void]
84
+ #
37
85
  def self.after_load_plugin(serializer_class, **_opts)
38
86
  serializer_class.config.opts[:metadata] = {attribute_keys: %i[path hide_nil hide_empty]}
39
87
  end
40
88
 
89
+ #
90
+ # Config for `metadata` plugin
91
+ #
41
92
  class MetadataConfig
93
+ # @return [Hash] metadata options
42
94
  attr_reader :opts
43
95
 
96
+ #
97
+ # Initializes context_metadata config object
98
+ #
99
+ # @param opts [Hash] options
100
+ #
101
+ # @return [Serega::SeregaPlugins::Metadata::MetadataConfig]
102
+ #
44
103
  def initialize(opts)
45
104
  @opts = opts
46
105
  end
47
106
 
107
+ #
108
+ # Returns allowed metadata attribute keys
109
+ #
48
110
  def attribute_keys
49
111
  opts.fetch(:attribute_keys)
50
112
  end
51
113
  end
52
114
 
115
+ #
116
+ # Config class additional/patched instance methods
117
+ #
118
+ # @see Serega::SeregaConfig
119
+ #
53
120
  module ConfigInstanceMethods
121
+ # @return [Serega::SeregaPlugins::Metadata::MetadataConfig] metadata config
54
122
  def metadata
55
123
  @metadata ||= MetadataConfig.new(opts.fetch(:metadata))
56
124
  end
57
125
  end
58
126
 
127
+ #
128
+ # Serega class additional/patched class methods
129
+ #
130
+ # @see Serega::SeregaConfig
131
+ #
59
132
  module ClassMethods
60
133
  private def inherited(subclass)
61
134
  super
@@ -73,30 +146,20 @@ class Serega
73
146
  #
74
147
  # List of added metadata attributes
75
148
  #
76
- # @return [Array] Added metadata attributes
149
+ # @return [Hash<Symbol => Serega::SeregaPlugins::Metadata::MetaAttribute>] Added metadata attributes
77
150
  #
78
151
  def meta_attributes
79
152
  @meta_attributes ||= {}
80
153
  end
81
154
 
82
155
  #
83
- # Adds metadata to response
84
- #
85
- # @example
86
- # class AppSerializer < Serega
87
- #
88
- # meta_attribute(:version) { '1.2.3' }
156
+ # Define metadata attribute
89
157
  #
90
- # meta_attribute(:meta, :paging, hide_nil: true, hide_empty: true) do |scope, ctx|
91
- # { page: scope.page, per_page: scope.per_page, total_count: scope.total_count }
92
- # end
93
- # end
158
+ # @param path [String, Symbol] Metadata attribute path keys
159
+ # @param opts [Hash] Metadata attribute options
160
+ # @param block [Proc] Block to fetch metadata attribute value
94
161
  #
95
- # @param *path [Array<String, Symbol>] Metadata attribute path keys
96
- # @param **opts [Hash] Metadata attribute options
97
- # @param &block [Proc] Metadata attribute value
98
- #
99
- # @return [MetadataAttribute] Added metadata attribute
162
+ # @return [Serega::SeregaPlugins::Metadata::MetaAttribute] Added metadata attribute
100
163
  #
101
164
  def meta_attribute(*path, **opts, &block)
102
165
  attribute = self::MetaAttribute.new(path: path, opts: opts, block: block)
@@ -104,25 +167,35 @@ class Serega
104
167
  end
105
168
  end
106
169
 
107
- module SeregaSerializerInstanceMethods
108
- def serialize(object)
109
- super.tap do |hash|
110
- add_metadata(object, hash)
111
- end
112
- end
113
-
170
+ #
171
+ # Serega additional/patched instance methods
172
+ #
173
+ # @see Serega
174
+ #
175
+ module InstanceMethods
114
176
  private
115
177
 
116
- def add_metadata(object, hash)
117
- self.class.serializer_class.meta_attributes.each_value do |meta_attribute|
118
- metadata = meta_attribute_value(object, meta_attribute)
178
+ def serialize(object, opts)
179
+ result = super
180
+ return result unless result.is_a?(Hash) # return earlier if not a hash, so no root was added
181
+
182
+ root = build_root(object, opts)
183
+ return result unless root # return earlier when no root
184
+
185
+ add_metadata(object, opts[:context], result)
186
+ result
187
+ end
188
+
189
+ def add_metadata(object, context, hash)
190
+ self.class.meta_attributes.each_value do |meta_attribute|
191
+ metadata = meta_attribute_value(object, context, meta_attribute)
119
192
  next unless metadata
120
193
 
121
194
  deep_merge_metadata(hash, metadata)
122
195
  end
123
196
  end
124
197
 
125
- def meta_attribute_value(object, meta_attribute)
198
+ def meta_attribute_value(object, context, meta_attribute)
126
199
  value = meta_attribute.value(object, context)
127
200
  return if meta_attribute.hide?(value)
128
201
 
@@ -4,6 +4,9 @@ class Serega
4
4
  module SeregaPlugins
5
5
  module Metadata
6
6
  class MetaAttribute
7
+ #
8
+ # Validator for meta_attribute block parameter
9
+ #
7
10
  class CheckBlock
8
11
  ALLOWED_PARAM_TYPES = %i[opt req]
9
12
  private_constant :ALLOWED_PARAM_TYPES
@@ -4,10 +4,13 @@ class Serega
4
4
  module SeregaPlugins
5
5
  module Metadata
6
6
  class MetaAttribute
7
+ #
8
+ # Validator for meta_attribute :hide_empty option
9
+ #
7
10
  class CheckOptHideEmpty
8
11
  class << self
9
12
  #
10
- # Checks attribute :after_hide_if option
13
+ # Checks attribute :hide_empty option
11
14
  #
12
15
  # @param opts [Hash] Attribute options
13
16
  #
@@ -4,10 +4,13 @@ class Serega
4
4
  module SeregaPlugins
5
5
  module Metadata
6
6
  class MetaAttribute
7
+ #
8
+ # Validator for meta_attribute :hide_nil option
9
+ #
7
10
  class CheckOptHideNil
8
11
  class << self
9
12
  #
10
- # Checks attribute :after_hide_if option
13
+ # Checks attribute :hide_nil option
11
14
  #
12
15
  # @param opts [Hash] Attribute options
13
16
  #
@@ -4,6 +4,9 @@ class Serega
4
4
  module SeregaPlugins
5
5
  module Metadata
6
6
  class MetaAttribute
7
+ #
8
+ # Validator for meta_attribute options
9
+ #
7
10
  class CheckOpts
8
11
  class << self
9
12
  #
@@ -4,6 +4,9 @@ class Serega
4
4
  module SeregaPlugins
5
5
  module Metadata
6
6
  class MetaAttribute
7
+ #
8
+ # Validator for meta_attribute :path parameter
9
+ #
7
10
  class CheckPath
8
11
  FORMAT_ONE_CHAR = /\A[a-zA-Z0-9]\z/
9
12
  FORMAT_MANY_CHARS = /\A[a-zA-Z0-9][a-zA-Z0-9_-]*?[a-zA-Z0-9]\z/ # allow '-' and '_' in the middle