serega 0.4.0 → 0.5.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/serega/attribute.rb +6 -1
  4. data/lib/serega/config.rb +2 -2
  5. data/lib/serega/map.rb +2 -4
  6. data/lib/serega/map_point.rb +34 -0
  7. data/lib/serega/object_serializer.rb +67 -0
  8. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +4 -0
  9. data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +27 -8
  10. data/lib/serega/plugins/batch/batch.rb +189 -0
  11. data/lib/serega/plugins/batch/lib/loader.rb +60 -0
  12. data/lib/serega/plugins/batch/lib/loaders.rb +32 -0
  13. data/lib/serega/plugins/batch/lib/plugins_extensions.rb +40 -0
  14. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb +61 -0
  15. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +61 -0
  16. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +47 -0
  17. data/lib/serega/plugins/context_metadata/context_metadata.rb +4 -4
  18. data/lib/serega/plugins/formatters/formatters.rb +25 -13
  19. data/lib/serega/plugins/hide_nil/hide_nil.rb +4 -4
  20. data/lib/serega/plugins/metadata/metadata.rb +11 -11
  21. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +8 -6
  22. data/lib/serega/plugins/preloads/preloads.rb +12 -1
  23. data/lib/serega/plugins/presenter/presenter.rb +6 -5
  24. data/lib/serega/plugins/root/root.rb +12 -12
  25. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +1 -1
  26. data/lib/serega/serializer.rb +32 -0
  27. data/lib/serega.rb +15 -10
  28. metadata +12 -5
  29. data/lib/serega/convert.rb +0 -45
  30. data/lib/serega/convert_item.rb +0 -37
  31. data/lib/serega/plugins/lazy/lazy.rb +0 -53
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1aec10f062296a0af1d3f6b445f189ab2d84a145a8ed3cf0d52c81aa0c4f2c69
4
- data.tar.gz: 1038ea10d7ea09b25d8f74b0561319d6f518a447121686ed689ff16385ec8806
3
+ metadata.gz: 5ed53d558202f013db9523c47d0d66f7c1122e5ef6cccebe590af9f53733e79c
4
+ data.tar.gz: ff3a809f7b783c4585c378d49d64f070db2c62952c574f7a9eff4a2928bab76c
5
5
  SHA512:
6
- metadata.gz: 8d568f45860671e760f56f67fd8206b2476b97ee5d415e881f23c9226285ef5b99a87b12ebd5da552664b6cd345ccb90cf7d43e10ed2a563ef436159f81afec5
7
- data.tar.gz: 3100f9a6bd3b8fba77175677d4e53c01abf467cf2c57a5c1eea6a71ac2fbd73e7d2c0b0ff7071a032acdd73e6b37af37cc3259421e4fc58a96d5400fdea215ed
6
+ metadata.gz: 50de7bfab75d1da976496e86fab440b295cc4c728a5692f1c5126bd76fab1b8c09ddb3de8d0adb3e89e269c79b70a98c41396334e1c8d3e1ecf805e6907b25e1
7
+ data.tar.gz: ad6d8e4b386bd44d54a90415829797b09b36ddcf22b8234ccaac453e0b6ff2b9ab4a107f5df6a7060b75fa7d0ed8a2ef4cc0e00f11861109c1b1005b61b83936
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.5.0
@@ -77,7 +77,12 @@ class Serega
77
77
  def value_block
78
78
  return @value_block if instance_variable_defined?(:@value_block)
79
79
 
80
- @value_block = block || opts[:value] || const_block || delegate_block || keyword_block
80
+ @value_block =
81
+ block ||
82
+ opts[:value] ||
83
+ const_block ||
84
+ delegate_block ||
85
+ keyword_block
81
86
  end
82
87
 
83
88
  #
data/lib/serega/config.rb CHANGED
@@ -15,8 +15,8 @@ class Serega
15
15
  serialize_keys: %i[context many].freeze,
16
16
  check_initiate_params: true,
17
17
  max_cached_map_per_serializer_count: 0,
18
- to_json: SeregaJSON.adapter == :oj ? SeregaJSON::OjDump : SeregaJSON::JSONDump,
19
- from_json: SeregaJSON.adapter == :oj ? SeregaJSON::OjLoad : SeregaJSON::JSONLoad
18
+ to_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjDump : SeregaJSON::JSONDump,
19
+ from_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjLoad : SeregaJSON::JSONLoad
20
20
  }.freeze
21
21
  # :nocov:
22
22
 
data/lib/serega/map.rb CHANGED
@@ -36,7 +36,7 @@ class Serega
36
36
  serializer_class.attributes.each_with_object([]) do |(name, attribute), map|
37
37
  next unless attribute.visible?(only: only, except: except, with: with)
38
38
 
39
- nested_map =
39
+ nested_points =
40
40
  if attribute.relation?
41
41
  construct_map(
42
42
  attribute.serializer,
@@ -44,11 +44,9 @@ class Serega
44
44
  with: with[name] || FROZEN_EMPTY_HASH,
45
45
  except: except[name] || FROZEN_EMPTY_HASH
46
46
  )
47
- else
48
- FROZEN_EMPTY_ARRAY
49
47
  end
50
48
 
51
- map << [attribute, nested_map]
49
+ map << serializer_class::SeregaMapPoint.new(attribute, nested_points)
52
50
  end
53
51
  end
54
52
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ class SeregaMapPoint
5
+ module InstanceMethods
6
+ extend Forwardable
7
+
8
+ attr_reader :attribute, :nested_points
9
+
10
+ def_delegators :@attribute, :name, :value, :many
11
+
12
+ def initialize(attribute, nested_points)
13
+ @attribute = attribute
14
+ @nested_points = nested_points
15
+ end
16
+
17
+ def has_nested_points?
18
+ !nested_points.nil?
19
+ end
20
+
21
+ def nested_object_serializer
22
+ attribute.serializer::SeregaObjectSerializer
23
+ end
24
+
25
+ # TODO: remove, we use this method in tests only
26
+ def ==(other)
27
+ (other.attribute == attribute) && (other.nested_points == nested_points)
28
+ end
29
+ end
30
+
31
+ extend Serega::SeregaHelpers::SerializerClassHelper
32
+ include InstanceMethods
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ class SeregaObjectSerializer
5
+ module SeregaObjectSerializerInstanceMethods
6
+ attr_reader :context, :points, :many, :opts
7
+
8
+ # @param context [Hash] Serialization context
9
+ # @param many [TrueClass|FalseClass] is object is enumerable
10
+ # @param points [Array<MapPoint>] Serialization points (attributes)
11
+ def initialize(context:, points:, many: nil, **opts)
12
+ @context = context
13
+ @points = points
14
+ @many = many
15
+ @opts = opts
16
+ end
17
+
18
+ # @param object [Object] Serialized object
19
+ def serialize(object)
20
+ array?(object, many) ? serialize_array(object) : serialize_object(object)
21
+ end
22
+
23
+ private
24
+
25
+ def serialize_array(object)
26
+ object.map { |obj| serialize_object(obj) }
27
+ end
28
+
29
+ def serialize_object(object)
30
+ return unless object
31
+
32
+ points.each_with_object({}) do |point, container|
33
+ attach_value(object, point, container)
34
+ end
35
+ end
36
+
37
+ def attach_value(object, point, container)
38
+ value = point.value(object, context)
39
+ final_value = final_value(value, point)
40
+ attach_final_value(final_value, point, container)
41
+ end
42
+
43
+ def attach_final_value(final_value, point, container)
44
+ container[point.name] = final_value
45
+ end
46
+
47
+ def final_value(value, point)
48
+ point.has_nested_points? ? relation_value(value, point) : value
49
+ end
50
+
51
+ def relation_value(value, point)
52
+ nested_points = point.nested_points
53
+ nested_serializer = point.nested_object_serializer
54
+ nested_many = point.many
55
+ serializer = nested_serializer.new(context: context, points: nested_points, many: nested_many, **opts)
56
+ serializer.serialize(value)
57
+ end
58
+
59
+ def array?(object, many)
60
+ many.nil? ? object.is_a?(Enumerable) : many
61
+ end
62
+ end
63
+
64
+ extend Serega::SeregaHelpers::SerializerClassHelper
65
+ include SeregaObjectSerializerInstanceMethods
66
+ end
67
+ end
@@ -14,6 +14,10 @@ class Serega
14
14
  end
15
15
 
16
16
  def self.before_load_plugin(serializer_class, **opts)
17
+ if serializer_class.plugin_used?(:batch)
18
+ raise SeregaError, "Plugin `activerecord_preloads` must be loaded before `batch`"
19
+ end
20
+
17
21
  serializer_class.plugin(:preloads, **opts) unless serializer_class.plugin_used?(:preloads)
18
22
  end
19
23
 
@@ -6,6 +6,8 @@ class Serega
6
6
  class Preloader
7
7
  module ClassMethods
8
8
  def preload(object, preloads)
9
+ return object if object.nil? || (object.is_a?(Array) && object.empty?) || preloads.empty?
10
+
9
11
  preload_handler = handlers.find { |handler| handler.fit?(object) }
10
12
  raise SeregaError, "Can't preload #{preloads.inspect} to #{object.inspect}" unless preload_handler
11
13
 
@@ -13,7 +15,12 @@ class Serega
13
15
  end
14
16
 
15
17
  def handlers
16
- @handlers ||= [ActiverecordRelation, ActiverecordObject, ActiverecordArray].freeze
18
+ @handlers ||= [
19
+ ActiverecordRelation,
20
+ ActiverecordObject,
21
+ ActiverecordArray,
22
+ ActiverecordEnumerator
23
+ ].freeze
17
24
  end
18
25
  end
19
26
 
@@ -54,13 +61,9 @@ class Serega
54
61
  end
55
62
 
56
63
  def preload(objects, preloads)
57
- if objects.loaded?
58
- array_objects = objects.to_a
59
- Loader.call(array_objects, preloads)
60
- objects
61
- else
62
- objects.preload(preloads).load
63
- end
64
+ objects.load
65
+ Loader.call(objects.to_a, preloads)
66
+ objects
64
67
  end
65
68
  end
66
69
 
@@ -90,6 +93,22 @@ class Serega
90
93
 
91
94
  extend ClassMethods
92
95
  end
96
+
97
+ class ActiverecordEnumerator
98
+ module ClassMethods
99
+ def fit?(objects)
100
+ objects.is_a?(Enumerator) &&
101
+ ActiverecordArray.fit?(objects.to_a)
102
+ end
103
+
104
+ def preload(objects, preloads)
105
+ ActiverecordArray.preload(objects.to_a, preloads)
106
+ objects
107
+ end
108
+ end
109
+
110
+ extend ClassMethods
111
+ end
93
112
  end
94
113
  end
95
114
  end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ def self.plugin_name
7
+ :batch
8
+ end
9
+
10
+ def self.load_plugin(serializer_class, **_opts)
11
+ require_relative "./lib/loader"
12
+ require_relative "./lib/loaders"
13
+ require_relative "./lib/validations/check_batch_opt_key"
14
+ require_relative "./lib/validations/check_batch_opt_loader"
15
+ require_relative "./lib/validations/check_opt_batch"
16
+
17
+ serializer_class.extend(ClassMethods)
18
+ serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
19
+ serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
20
+ serializer_class::SeregaMapPoint.include(MapPointInstanceMethods)
21
+ serializer_class::SeregaSerializer.include(SeregaSerializerInstanceMethods)
22
+ serializer_class::SeregaObjectSerializer.include(SeregaObjectSerializerInstanceMethods)
23
+ end
24
+
25
+ def self.after_load_plugin(serializer_class, **opts)
26
+ config = serializer_class.config
27
+ config.attribute_keys << :batch
28
+ config.opts[:batch] = {loaders: {}}
29
+ serializer_class::SeregaConfig.include(ConfigInstanceMethods)
30
+
31
+ batch_loaders_class = Class.new(SeregaBatchLoaders)
32
+ batch_loaders_class.serializer_class = serializer_class
33
+ serializer_class.const_set(:SeregaBatchLoaders, batch_loaders_class)
34
+
35
+ batch_loader_class = Class.new(SeregaBatchLoader)
36
+ batch_loader_class.serializer_class = serializer_class
37
+ serializer_class.const_set(:SeregaBatchLoader, batch_loader_class)
38
+
39
+ if serializer_class.plugin_used?(:activerecord_preloads)
40
+ require_relative "./lib/plugins_extensions"
41
+ serializer_class::SeregaBatchLoader.include(PluginsExtensions::ActiveRecordPreloads::BatchLoaderInstanceMethods)
42
+ end
43
+
44
+ if serializer_class.plugin_used?(:formatters)
45
+ require_relative "./lib/plugins_extensions"
46
+ serializer_class::SeregaBatchLoader.include(PluginsExtensions::Formatters::BatchLoaderInstanceMethods)
47
+ end
48
+ end
49
+
50
+ class BatchLoadersConfig
51
+ attr_reader :opts
52
+
53
+ def initialize(opts)
54
+ @opts = opts
55
+ end
56
+
57
+ def define(loader_name, &block)
58
+ unless block
59
+ raise SeregaError, "Block must be given to batch_loaders.define method"
60
+ end
61
+
62
+ params = block.parameters
63
+ if params.count > 3 || !params.map!(&:first).all? { |type| (type == :req) || (type == :opt) }
64
+ raise SeregaError, "Block can have maximum 3 regular parameters"
65
+ end
66
+
67
+ opts[loader_name] = block
68
+ end
69
+
70
+ def fetch(loader_name)
71
+ opts[loader_name] || (raise SeregaError, "Batch loader with name `#{loader_name.inspect}` was not defined. Define example: config.batch_loaders.define(:#{loader_name}) { |keys, ctx, points| ... }")
72
+ end
73
+ end
74
+
75
+ class BatchModel
76
+ attr_reader :opts, :loaders, :many
77
+
78
+ def initialize(opts, loaders, many)
79
+ @opts = opts
80
+ @loaders = loaders
81
+ @many = many
82
+ end
83
+
84
+ def loader
85
+ @batch_loader ||= begin
86
+ loader = opts[:loader]
87
+ loader = loaders.fetch(loader) if loader.is_a?(Symbol)
88
+ loader
89
+ end
90
+ end
91
+
92
+ def key
93
+ @batch_key ||= begin
94
+ key = opts[:key]
95
+ key.is_a?(Symbol) ? proc { |object| object.public_send(key) } : key
96
+ end
97
+ end
98
+
99
+ def default_value
100
+ if opts.key?(:default)
101
+ opts[:default]
102
+ elsif many
103
+ FROZEN_EMPTY_ARRAY
104
+ end
105
+ end
106
+ end
107
+
108
+ module ConfigInstanceMethods
109
+ def batch_loaders
110
+ @batch_loaders ||= BatchLoadersConfig.new(opts.fetch(:batch).fetch(:loaders))
111
+ end
112
+ end
113
+
114
+ module ClassMethods
115
+ private
116
+
117
+ def inherited(subclass)
118
+ super
119
+
120
+ batch_loaders_class = Class.new(self::SeregaBatchLoaders)
121
+ batch_loaders_class.serializer_class = subclass
122
+ subclass.const_set(:SeregaBatchLoaders, batch_loaders_class)
123
+
124
+ batch_loader_class = Class.new(self::SeregaBatchLoader)
125
+ batch_loader_class.serializer_class = subclass
126
+ subclass.const_set(:SeregaBatchLoader, batch_loader_class)
127
+ end
128
+ end
129
+
130
+ module CheckAttributeParamsInstanceMethods
131
+ def check_opts
132
+ super
133
+
134
+ CheckOptBatch.call(opts, block)
135
+ end
136
+ end
137
+
138
+ module AttributeInstanceMethods
139
+ def batch
140
+ opts[:batch]
141
+ end
142
+ end
143
+
144
+ module MapPointInstanceMethods
145
+ def batch
146
+ return @batch if instance_variable_defined?(:@batch)
147
+
148
+ @batch = begin
149
+ opts = attribute.batch
150
+ BatchModel.new(opts, self.class.serializer_class.config.batch_loaders, many) if opts
151
+ end
152
+ end
153
+ end
154
+
155
+ module SeregaSerializerInstanceMethods
156
+ def initialize(**_args)
157
+ super
158
+ opts[:batch_loaders] = self.class.serializer_class::SeregaBatchLoaders.new
159
+ end
160
+
161
+ def serialize(*)
162
+ result = super
163
+
164
+ opts[:batch_loaders].load_all
165
+
166
+ result
167
+ end
168
+ end
169
+
170
+ module SeregaObjectSerializerInstanceMethods
171
+ private
172
+
173
+ def attach_value(object, point, container)
174
+ batch = point.batch
175
+
176
+ if batch
177
+ key = batch.key.call(object, context)
178
+ opts[:batch_loaders].get(point, self).remember(key, container)
179
+ container[point.name] = nil # Reserve attribute place in resulted hash. We will set correct value later
180
+ else
181
+ super
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ register_plugin(Batch.plugin_name, Batch)
188
+ end
189
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ class SeregaBatchLoader
7
+ module InstanceMethods
8
+ attr_reader :object_serializer, :point
9
+
10
+ def initialize(object_serializer, point)
11
+ @object_serializer = object_serializer
12
+ @point = point
13
+ end
14
+
15
+ def remember(key, container)
16
+ (keys[key] ||= []) << container
17
+ end
18
+
19
+ def load
20
+ keys_values = keys_values()
21
+
22
+ each_key do |key, container|
23
+ value = keys_values.fetch(key) { point.batch.default_value }
24
+ final_value = object_serializer.__send__(:final_value, value, point)
25
+ object_serializer.__send__(:attach_final_value, final_value, point, container)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def each_key
32
+ keys.each do |key, containers|
33
+ containers.each do |container|
34
+ yield(key, container)
35
+ end
36
+ end
37
+ end
38
+
39
+ def keys_values
40
+ ids = keys.keys
41
+
42
+ point.batch.loader.call(ids, object_serializer.context, point.nested_points).tap do |vals|
43
+ next if vals.is_a?(Hash)
44
+
45
+ attribute_name = "#{point.class.serializer_class}.#{point.name}"
46
+ raise SeregaError, "Batch loader for `#{attribute_name}` must return Hash, but #{vals.inspect} was returned"
47
+ end
48
+ end
49
+
50
+ def keys
51
+ @keys ||= {}
52
+ end
53
+ end
54
+
55
+ include InstanceMethods
56
+ extend Serega::SeregaHelpers::SerializerClassHelper
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ class SeregaBatchLoaders
7
+ module InstanceMethods
8
+ def get(point, object_serializer)
9
+ batch_loaders[point] ||= self.class.serializer_class::SeregaBatchLoader.new(object_serializer, point)
10
+ end
11
+
12
+ def load_all
13
+ return unless defined?(@batch_loaders)
14
+
15
+ while (_point, batch_loader = batch_loaders.shift)
16
+ batch_loader.load
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def batch_loaders
23
+ @batch_loaders ||= {}.compare_by_identity
24
+ end
25
+ end
26
+
27
+ include InstanceMethods
28
+ extend Serega::SeregaHelpers::SerializerClassHelper
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ module PluginsExtensions
7
+ module ActiveRecordPreloads
8
+ module BatchLoaderInstanceMethods
9
+ def keys_values(*)
10
+ data = super
11
+
12
+ if point.has_nested_points?
13
+ associations = point.preloads
14
+ return data if associations.empty?
15
+
16
+ ActiverecordPreloads::Preloader.preload(data.values.flatten(1), associations)
17
+ end
18
+
19
+ data
20
+ end
21
+ end
22
+ end
23
+
24
+ module Formatters
25
+ module BatchLoaderInstanceMethods
26
+ # Format values after they are prepared
27
+ def keys_values(*)
28
+ data = super
29
+
30
+ formatter = point.attribute.formatter_resolved
31
+ data.transform_values! { |value| formatter.call(value) } if formatter
32
+
33
+ data
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ class CheckBatchOptKey
7
+ class << self
8
+ def call(key)
9
+ return if key.is_a?(Symbol)
10
+
11
+ raise SeregaError, must_be_callable unless key.respond_to?(:call)
12
+
13
+ if key.is_a?(Proc)
14
+ check_block(key)
15
+ else
16
+ check_callable(key)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def check_block(block)
23
+ return if valid_parameters?(block, accepted_count: 0..2)
24
+
25
+ raise SeregaError, block_parameters_error
26
+ end
27
+
28
+ def check_callable(callable)
29
+ return if valid_parameters?(callable.method(:call), accepted_count: 2..2)
30
+
31
+ raise SeregaError, callable_parameters_error
32
+ end
33
+
34
+ def valid_parameters?(data, accepted_count:)
35
+ params = data.parameters
36
+ accepted_count.include?(params.count) && valid_parameters_types?(params)
37
+ end
38
+
39
+ def valid_parameters_types?(params)
40
+ params.all? do |param|
41
+ type = param[0]
42
+ (type == :req) || (type == :opt)
43
+ end
44
+ end
45
+
46
+ def block_parameters_error
47
+ "Invalid :batch option :key. When it is a Proc it can have maximum two regular parameters (object, context)"
48
+ end
49
+
50
+ def callable_parameters_error
51
+ "Invalid :batch option :key. When it is a callable object it must have two regular parameters (object, context)"
52
+ end
53
+
54
+ def must_be_callable
55
+ "Invalid :batch option :key. It must be a Symbol, a Proc or respond to :call"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Batch
6
+ class CheckBatchOptLoader
7
+ class << self
8
+ def call(loader)
9
+ return if loader.is_a?(Symbol)
10
+
11
+ raise SeregaError, must_be_callable unless loader.respond_to?(:call)
12
+
13
+ if loader.is_a?(Proc)
14
+ check_block(loader)
15
+ else
16
+ check_callable(loader)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def check_block(block)
23
+ return if valid_parameters?(block, accepted_count: 0..3)
24
+
25
+ raise SeregaError, block_parameters_error
26
+ end
27
+
28
+ def check_callable(callable)
29
+ return if valid_parameters?(callable.method(:call), accepted_count: 3..3)
30
+
31
+ raise SeregaError, callable_parameters_error
32
+ end
33
+
34
+ def valid_parameters?(data, accepted_count:)
35
+ params = data.parameters
36
+ accepted_count.include?(params.count) && valid_parameters_types?(params)
37
+ end
38
+
39
+ def valid_parameters_types?(params)
40
+ params.all? do |param|
41
+ type = param[0]
42
+ (type == :req) || (type == :opt)
43
+ end
44
+ end
45
+
46
+ def block_parameters_error
47
+ "Invalid :batch option :loader. When it is a Proc it can have maximum three regular parameters (keys, context, points)"
48
+ end
49
+
50
+ def callable_parameters_error
51
+ "Invalid :batch option :loader. When it is a callable object it must have three regular parameters (keys, context, points)"
52
+ end
53
+
54
+ def must_be_callable
55
+ "Invalid :batch option :loader. It must be a Symbol, a Proc or respond to :call"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end