serega 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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