serega 0.6.1 → 0.7.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.
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