serega 0.20.1 → 0.30.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -226
  3. data/VERSION +1 -1
  4. data/lib/serega/attribute.rb +31 -3
  5. data/lib/serega/attribute_normalizer.rb +143 -42
  6. data/lib/serega/batch/attribute_loader.rb +70 -0
  7. data/lib/serega/batch/attribute_loaders.rb +59 -0
  8. data/lib/serega/batch/auto_resolver.rb +24 -0
  9. data/lib/serega/batch/auto_resolver_factory.rb +48 -0
  10. data/lib/serega/batch/loader.rb +67 -0
  11. data/lib/serega/config.rb +59 -1
  12. data/lib/serega/object_serializer.rb +11 -9
  13. data/lib/serega/plan.rb +16 -0
  14. data/lib/serega/plan_point.rb +31 -5
  15. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +42 -23
  16. data/lib/serega/plugins/activerecord_preloads/lib/active_record_objects.rb +31 -0
  17. data/lib/serega/plugins/camel_case/camel_case.rb +1 -1
  18. data/lib/serega/plugins/formatters/formatters.rb +79 -22
  19. data/lib/serega/plugins/if/if.rb +41 -22
  20. data/lib/serega/plugins/if/validations/check_opt_if.rb +27 -6
  21. data/lib/serega/plugins/if/validations/check_opt_if_value.rb +27 -6
  22. data/lib/serega/plugins/if/validations/check_opt_unless.rb +27 -6
  23. data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +27 -6
  24. data/lib/serega/plugins/metadata/meta_attribute.rb +9 -10
  25. data/lib/serega/plugins/metadata/validations/check_block.rb +2 -4
  26. data/lib/serega/plugins/metadata/validations/check_opt_value.rb +2 -3
  27. data/lib/serega/plugins/presenter/presenter.rb +1 -1
  28. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +43 -30
  29. data/lib/serega/utils/format_user_preloads.rb +58 -0
  30. data/lib/serega/utils/method_signature.rb +89 -0
  31. data/lib/serega/utils/preload_paths.rb +53 -0
  32. data/lib/serega/utils/preloads_constructor.rb +77 -0
  33. data/lib/serega/validations/attribute/check_block.rb +38 -6
  34. data/lib/serega/validations/attribute/check_opt_batch.rb +101 -0
  35. data/lib/serega/validations/attribute/check_opt_const.rb +1 -0
  36. data/lib/serega/validations/attribute/check_opt_delegate.rb +1 -0
  37. data/lib/serega/validations/attribute/check_opt_method.rb +1 -0
  38. data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload.rb +2 -2
  39. data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload_path.rb +3 -3
  40. data/lib/serega/validations/attribute/check_opt_value.rb +35 -9
  41. data/lib/serega/validations/check_attribute_params.rb +3 -2
  42. data/lib/serega/validations/check_batch_loader_params.rb +80 -0
  43. data/lib/serega/validations/initiate/check_modifiers.rb +1 -1
  44. data/lib/serega.rb +110 -11
  45. metadata +17 -37
  46. data/lib/serega/plugins/batch/batch.rb +0 -168
  47. data/lib/serega/plugins/batch/lib/batch_config.rb +0 -77
  48. data/lib/serega/plugins/batch/lib/loader.rb +0 -113
  49. data/lib/serega/plugins/batch/lib/loaders.rb +0 -45
  50. data/lib/serega/plugins/batch/lib/modules/attribute.rb +0 -26
  51. data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +0 -78
  52. data/lib/serega/plugins/batch/lib/modules/check_attribute_params.rb +0 -22
  53. data/lib/serega/plugins/batch/lib/modules/config.rb +0 -23
  54. data/lib/serega/plugins/batch/lib/modules/object_serializer.rb +0 -46
  55. data/lib/serega/plugins/batch/lib/modules/plan_point.rb +0 -22
  56. data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +0 -43
  57. data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +0 -54
  58. data/lib/serega/plugins/batch/lib/plugins_extensions/if.rb +0 -31
  59. data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +0 -34
  60. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_id_method.rb +0 -43
  61. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +0 -59
  62. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +0 -62
  63. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +0 -60
  64. data/lib/serega/plugins/preloads/lib/modules/attribute.rb +0 -28
  65. data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +0 -99
  66. data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +0 -22
  67. data/lib/serega/plugins/preloads/lib/modules/config.rb +0 -19
  68. data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +0 -41
  69. data/lib/serega/plugins/preloads/lib/preload_paths.rb +0 -53
  70. data/lib/serega/plugins/preloads/lib/preloads_config.rb +0 -62
  71. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +0 -79
  72. data/lib/serega/plugins/preloads/preloads.rb +0 -162
  73. data/lib/serega/utils/params_count.rb +0 -50
  74. data/lib/serega/validations/utils/check_extra_keyword_arg.rb +0 -33
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- module SeregaPlugins
5
- module Preloads
6
- #
7
- # Finds preloads for provided attributes plan
8
- #
9
- class PreloadsConstructor
10
- class << self
11
- #
12
- # Constructs preloads hash for given attributes plan
13
- #
14
- # @param plan [Array<SeregaPlanPoint>] Serialization plan
15
- #
16
- # @return [Hash]
17
- #
18
- def call(plan)
19
- return FROZEN_EMPTY_HASH unless plan
20
-
21
- preloads = {}
22
- append_many(preloads, plan)
23
- preloads
24
- end
25
-
26
- private
27
-
28
- def append_many(preloads, plan)
29
- plan.points.each do |point|
30
- current_preloads = point.attribute.preloads
31
- next unless current_preloads
32
-
33
- child_plan = point.child_plan
34
- current_preloads = SeregaUtils::EnumDeepDup.call(current_preloads) if child_plan
35
- append_current(preloads, current_preloads)
36
- next unless child_plan
37
-
38
- each_child_preloads(preloads, point.preloads_path) do |child_preloads|
39
- append_many(child_preloads, child_plan)
40
- end
41
- end
42
- end
43
-
44
- def append_current(preloads, current_preloads)
45
- merge(preloads, current_preloads) unless current_preloads.empty?
46
- end
47
-
48
- def merge(preloads, current_preloads)
49
- preloads.merge!(current_preloads) do |_key, value_one, value_two|
50
- merge(value_one, value_two)
51
- end
52
- end
53
-
54
- def each_child_preloads(preloads, preloads_path)
55
- return yield(preloads) if preloads_path.nil?
56
-
57
- if preloads_path[0].is_a?(Array)
58
- preloads_path.each do |path|
59
- yield dig_fetch(preloads, path)
60
- end
61
- else
62
- yield dig_fetch(preloads, preloads_path)
63
- end
64
- end
65
-
66
- def dig_fetch(preloads, preloads_path)
67
- return preloads if !preloads_path || preloads_path.empty?
68
-
69
- preloads_path.each do |path|
70
- preloads = preloads.fetch(path)
71
- end
72
-
73
- preloads
74
- end
75
- end
76
- end
77
- end
78
- end
79
- end
@@ -1,162 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- module SeregaPlugins
5
- #
6
- # Plugin `:preloads`
7
- #
8
- # Allows to define `:preloads` to attributes and then allows to merge preloads
9
- # from serialized attributes and return single associations hash.
10
- #
11
- # Plugin accepts options:
12
- # - `auto_preload_attributes_with_delegate` - default false
13
- # - `auto_preload_attributes_with_serializer` - default false
14
- # - `auto_hide_attributes_with_preload` - default false
15
- #
16
- # This options are very handy if you want to forget about finding preloads manually.
17
- #
18
- # Preloads can be disabled with `preload: false` attribute option.
19
- # Also automatically added preloads can be overwritten with manually specified `preload: :another_value`.
20
- #
21
- # Some examples, **please read comments in the code below**
22
- #
23
- # @example
24
- # class AppSerializer < Serega
25
- # plugin :preloads,
26
- # auto_preload_attributes_with_delegate: true,
27
- # auto_preload_attributes_with_serializer: true,
28
- # auto_hide_attributes_with_preload: true
29
- # end
30
- #
31
- # class UserSerializer < AppSerializer
32
- # # No preloads
33
- # attribute :username
34
- #
35
- # # Specify `preload: :user_stats` manually
36
- # attribute :followers_count, preload: :user_stats, value: proc { |user| user.user_stats.followers_count }
37
- #
38
- # # Automatically `preloads: :user_stats` as `auto_preload_attributes_with_delegate` option is true
39
- # attribute :comments_count, delegate: { to: :user_stats }
40
- #
41
- # # Automatically `preloads: :albums` as `auto_preload_attributes_with_serializer` option is true
42
- # attribute :albums, serializer: 'AlbumSerializer'
43
- # end
44
- #
45
- # class AlbumSerializer < AppSerializer
46
- # attribute :images_count, delegate: { to: :album_stats }
47
- # end
48
- #
49
- # # By default preloads are empty, as we specify `auto_hide_attributes_with_preload = true`,
50
- # # and attributes with preloads will be not serialized
51
- # UserSerializer.new.preloads # => {}
52
- # UserSerializer.new.to_h(OpenStruct.new(username: 'foo')) # => {:username=>"foo"}
53
- #
54
- # UserSerializer.new(with: :followers_count).preloads # => {:user_stats=>{}}
55
- # UserSerializer.new(with: %i[followers_count comments_count]).preloads # => {:user_stats=>{}}
56
- # UserSerializer.new(with: [:followers_count, :comments_count, { albums: :images_count }]).preloads # => {:user_stats=>{}, :albums=>{:album_stats=>{}}}
57
- #
58
-
59
- module Preloads
60
- DEFAULT_CONFIG = {
61
- auto_preload_attributes_with_delegate: false,
62
- auto_preload_attributes_with_serializer: false,
63
- auto_hide_attributes_with_preload: false
64
- }.freeze
65
-
66
- private_constant :DEFAULT_CONFIG
67
-
68
- # @return [Symbol] Plugin name
69
- def self.plugin_name
70
- :preloads
71
- end
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
-
92
- if serializer_class.plugin_used?(:batch)
93
- raise SeregaError, "Plugin #{plugin_name.inspect} must be loaded before the :batch plugin"
94
- end
95
- end
96
-
97
- #
98
- # Applies plugin code to specific serializer
99
- #
100
- # @param serializer_class [Class<Serega>] Current serializer class
101
- # @param _opts [Hash] Plugin options
102
- #
103
- # @return [void]
104
- #
105
- def self.load_plugin(serializer_class, **_opts)
106
- require_relative "lib/format_user_preloads"
107
- require_relative "lib/modules/attribute"
108
- require_relative "lib/modules/attribute_normalizer"
109
- require_relative "lib/modules/check_attribute_params"
110
- require_relative "lib/modules/config"
111
- require_relative "lib/modules/plan_point"
112
- require_relative "lib/preload_paths"
113
- require_relative "lib/preloads_config"
114
- require_relative "lib/preloads_constructor"
115
- require_relative "validations/check_opt_preload"
116
- require_relative "validations/check_opt_preload_path"
117
-
118
- serializer_class.include(InstanceMethods)
119
- serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
120
- serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
121
- serializer_class::SeregaConfig.include(ConfigInstanceMethods)
122
- serializer_class::SeregaPlanPoint.include(PlanPointInstanceMethods)
123
-
124
- serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
125
- end
126
-
127
- #
128
- # Adds config options and runs other callbacks after plugin was loaded
129
- #
130
- # @param serializer_class [Class<Serega>] Current serializer class
131
- # @param opts [Hash] Plugin options
132
- #
133
- # @return [void]
134
- #
135
- def self.after_load_plugin(serializer_class, **opts)
136
- config = serializer_class.config
137
- config.attribute_keys << :preload << :preload_path
138
-
139
- preloads_opts = DEFAULT_CONFIG.merge(opts.slice(*DEFAULT_CONFIG.keys))
140
- config.opts[:preloads] = {}
141
- preloads_config = config.preloads
142
- preloads_config.auto_preload_attributes_with_delegate = preloads_opts[:auto_preload_attributes_with_delegate]
143
- preloads_config.auto_preload_attributes_with_serializer = preloads_opts[:auto_preload_attributes_with_serializer]
144
- preloads_config.auto_hide_attributes_with_preload = preloads_opts[:auto_hide_attributes_with_preload]
145
- end
146
-
147
- #
148
- # Serega additional/patched instance methods
149
- #
150
- # @see Serega
151
- #
152
- module InstanceMethods
153
- # @return [Hash] merged preloads of all serialized attributes
154
- def preloads
155
- @preloads ||= PreloadsConstructor.call(plan)
156
- end
157
- end
158
- end
159
-
160
- register_plugin(Preloads.plugin_name, Preloads)
161
- end
162
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- #
5
- # Utilities
6
- #
7
- module SeregaUtils
8
- #
9
- # Utility to count regular parameters of callable object
10
- #
11
- class ParamsCount
12
- NO_NAMED_REST_PARAM = [:rest].freeze
13
- private_constant :NO_NAMED_REST_PARAM
14
-
15
- class << self
16
- #
17
- # Count parameters for callable object
18
- #
19
- # @param object [#call] callable object
20
- #
21
- # @return [Integer] count of regular parameters
22
- #
23
- def call(object, max_count:)
24
- # Procs (but not lambdas) can accept all provided parameters
25
- parameters = object.is_a?(Proc) ? object.parameters : object.method(:call).parameters
26
- return 1 if parameters[0] == NO_NAMED_REST_PARAM
27
- return max_count if object.is_a?(Proc) && !object.lambda?
28
-
29
- count = 0
30
-
31
- # If all we have is no-name *rest parameters, then we assume we need to provide
32
- # 1 argument. It is now always correct, but in serialization context it's most common that
33
- # only one argument is needed.
34
- parameters.each do |parameter|
35
- next if parameter == NO_NAMED_REST_PARAM # Workaround for procs like :odd?.to_proc
36
- param_type = parameter[0]
37
-
38
- case param_type
39
- when :req then count += 1
40
- when :opt then count += 1 if count < max_count
41
- when :rest then count += max_count - count if max_count > count
42
- end # else :opt, :keyreq, :key, :keyrest, :block - do nothing
43
- end
44
-
45
- count
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- module SeregaValidations
5
- #
6
- # Validations Utilities
7
- #
8
- module Utils
9
- #
10
- # Utility to check that callable object has no required keyword arguments
11
- #
12
- class CheckExtraKeywordArg
13
- # Checks hash keys are allowed
14
- #
15
- # @param callable [#call] Callable object
16
- # @param callable_description [Symbol] Callable object description
17
- #
18
- # @raise [Serega::SeregaError] error if callable accepts required keyword argument
19
- #
20
- # @return [void]
21
- def self.call(callable, callable_description)
22
- parameters = callable.is_a?(Proc) ? callable.parameters : callable.method(:call).parameters
23
-
24
- parameters.each do |parameter|
25
- next unless parameter[0] == :keyreq
26
-
27
- raise Serega::SeregaError, "Invalid #{callable_description}. It should not have any required keyword arguments"
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end