jmespath-ruby 0.1.1 → 0.1.2

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.
@@ -10,6 +10,8 @@ module JMESPath
10
10
 
11
11
  # A malformed expression with a byte position and readable coordinates.
12
12
  class SyntaxError < Error
13
+ # @return [String] the reason parsing failed, without coordinates
14
+ attr_reader :reason
13
15
  # @return [String] the expression text
14
16
  attr_reader :expression
15
17
  # @return [Integer] zero-based byte offset
@@ -18,10 +20,17 @@ module JMESPath
18
20
  # @param message [String] the reason parsing failed
19
21
  # @param expression [String] expression text
20
22
  # @param position [Integer] zero-based byte offset
21
- def initialize(message, expression: "", position: 0)
23
+ def initialize(message, expression: "", position: nil)
24
+ @reason = message.dup.freeze
22
25
  @expression = expression.dup.freeze
23
- @position = position
24
- super("#{message} at byte #{position}")
26
+ @location_known = !position.nil?
27
+ @position = position || 0
28
+ super("#{message} at byte #{@position}")
29
+ end
30
+
31
+ # @return [Boolean] whether the caller supplied a source position
32
+ def location_known?
33
+ @location_known
25
34
  end
26
35
 
27
36
  # @return [Integer] one-based line number
@@ -41,13 +50,116 @@ module JMESPath
41
50
  end
42
51
  end
43
52
 
53
+ # @api private
54
+ module FunctionFailure
55
+ # @return [String, nil] function identifier
56
+ attr_reader :function_name
57
+ # @return [Array<Array<Symbol>>, nil] complete declared signature
58
+ attr_reader :signature
59
+ # @return [Range<Integer>, nil] exclusive byte range of the function call
60
+ attr_reader :source_range
61
+
62
+ # @api private
63
+ def initialize(message = nil, function_name: nil, signature: nil, variadic: nil, source_range: nil)
64
+ @function_name = function_name.dup.freeze
65
+ @signature = signature&.map { |types| types.dup.freeze }&.freeze
66
+ @variadic = variadic
67
+ @source_range = source_range.dup.freeze
68
+ super(message)
69
+ end
70
+
71
+ # @return [Boolean, nil] whether the last declared argument repeats
72
+ def variadic?
73
+ @variadic
74
+ end
75
+ end
76
+
44
77
  # A function received an unsupported value type.
45
- class InvalidTypeError < RuntimeError; end
78
+ # @example Inspect a validation failure
79
+ # error = begin
80
+ # JMESPath.search("length(@)", 4)
81
+ # rescue JMESPath::Errors::InvalidTypeError => failure
82
+ # failure
83
+ # end
84
+ # error.expected_types #=> [:string, :array, :object]
85
+ # error.argument_position #=> 1
86
+ class InvalidTypeError < RuntimeError
87
+ include FunctionFailure
88
+
89
+ # @!attribute [r] function_name
90
+ # @return [String, nil] function identifier
91
+ # @!attribute [r] signature
92
+ # @return [Array<Array<Symbol>>, nil] complete declared signature
93
+ # @!attribute [r] source_range
94
+ # @return [Range<Integer>, nil] exclusive byte range of the function call
95
+ # @!method variadic?
96
+ # @return [Boolean, nil] whether the last declared argument repeats
97
+
98
+ # @return [Integer, nil] one-based actual argument position
99
+ attr_reader :argument_position
100
+ # @return [Array<Symbol>, nil] types accepted by the failing check
101
+ attr_reader :expected_types
102
+ # @return [Symbol, nil] rejected type, including :unknown
103
+ attr_reader :actual_type
104
+ # @return [Symbol, nil] :argument or :expression_result
105
+ attr_reader :validation_stage
106
+
107
+ # @api private
108
+ def initialize(message = nil, argument_position: nil, expected_types: nil, actual_type: nil, # rubocop:disable Metrics/ParameterLists
109
+ validation_stage: nil, **context)
110
+ @argument_position = argument_position
111
+ @expected_types = expected_types&.dup&.freeze
112
+ @actual_type = actual_type
113
+ @validation_stage = validation_stage
114
+ super(message, **context)
115
+ end
116
+ end
117
+
46
118
  # A value is invalid for the requested operation.
47
119
  class InvalidValueError < RuntimeError; end
120
+
48
121
  # A function received an incorrect number of arguments.
49
- class InvalidArityError < RuntimeError; end
122
+ class InvalidArityError < RuntimeError
123
+ include FunctionFailure
124
+
125
+ # @!attribute [r] function_name
126
+ # @return [String, nil] function identifier
127
+ # @!attribute [r] signature
128
+ # @return [Array<Array<Symbol>>, nil] complete declared signature
129
+ # @!attribute [r] source_range
130
+ # @return [Range<Integer>, nil] exclusive byte range of the function call
131
+ # @!method variadic?
132
+ # @return [Boolean, nil] whether the last declared argument repeats
133
+
134
+ # @return [Integer, nil] exact argument count, or minimum when variadic
135
+ attr_reader :expected_arity
136
+ # @return [Integer, nil] actual argument count
137
+ attr_reader :actual_arity
138
+
139
+ # @api private
140
+ def initialize(message = nil, expected_arity: nil, actual_arity: nil, **context)
141
+ @expected_arity = expected_arity
142
+ @actual_arity = actual_arity
143
+ super(message, **context)
144
+ end
145
+ end
146
+
50
147
  # A function name is not registered.
51
- class UnknownFunctionError < RuntimeError; end
148
+ class UnknownFunctionError < RuntimeError
149
+ # @return [String, nil] unregistered function identifier
150
+ attr_reader :function_name
151
+ # @return [Array<String>] up to three similar names from the active registry
152
+ attr_reader :suggestions
153
+
154
+ # @param message [String, nil] failure message
155
+ # @param function_name [String, nil] unregistered function identifier
156
+ # @param suggestions [Array<String>] ranked registered names
157
+ def initialize(message = nil, function_name: nil, suggestions: [])
158
+ @function_name = function_name&.dup&.freeze
159
+ @suggestions = suggestions.map { |name| name.dup.freeze }.freeze
160
+ message = "#{message}. Did you mean #{@suggestions.join(', ')}?" unless @suggestions.empty?
161
+ super(message)
162
+ end
163
+ end
52
164
  end
53
165
  end
@@ -43,7 +43,8 @@ module JMESPath
43
43
 
44
44
  def function(node, data)
45
45
  arguments = node.children.map { |child| evaluate(child, data) }
46
- @functions.call(node.value.to_s, arguments, disable_visit_errors: @disable_visit_errors)
46
+ @functions.call_with_context(node.value.to_s, arguments, source_range: node.start...node.finish,
47
+ disable_visit_errors: @disable_visit_errors)
47
48
  end
48
49
  end
49
50
  end
@@ -6,33 +6,49 @@ module JMESPath
6
6
  module Coercions
7
7
  extend self
8
8
 
9
- def number(value)
9
+ NATIVE_TYPES = { String => :string, NilClass => :null, TrueClass => :boolean, FalseClass => :boolean,
10
+ Array => :array, Hash => :object, ExpressionReference => :expref }.freeze
11
+
12
+ def number(value, context: nil, position: nil, stage: :argument)
10
13
  return value if value.is_a?(Integer)
11
14
  return value if value.is_a?(Float) && value.finite?
12
15
 
13
- raise Signature::TypeMismatch.new("Expected a finite number", origin: validation_origin)
16
+ raise type_failure(value, "Expected a finite number", [:number], context, position, stage)
14
17
  end
15
18
 
16
- def string(value)
17
- DataAccess.string(value) || raise(Signature::TypeMismatch.new("Expected a string", origin: validation_origin))
19
+ def string(value, context: nil, position: nil)
20
+ DataAccess.string(value) || raise(type_failure(value, "Expected a string", [:string], context, position))
18
21
  end
19
22
 
20
- def array(value)
21
- DataAccess.array(value) || raise(Signature::TypeMismatch.new("Expected an array", origin: validation_origin))
23
+ def array(value, context: nil, position: nil)
24
+ DataAccess.array(value) || raise(type_failure(value, "Expected an array", [:array], context, position))
22
25
  end
23
26
 
24
- def reference(value)
27
+ def reference(value, context: nil, position: nil)
25
28
  return value if value.is_a?(ExpressionReference)
26
29
 
27
- raise Signature::TypeMismatch.new("Expected an expression reference", origin: validation_origin)
30
+ raise type_failure(value, "Expected an expression reference", [:expref], context, position)
28
31
  end
29
32
 
30
- def object(value)
31
- DataAccess.object(value) || raise(Signature::TypeMismatch.new("Expected an object", origin: validation_origin))
33
+ def object(value, context: nil, position: nil)
34
+ DataAccess.object(value) || raise(type_failure(value, "Expected an object", [:object], context, position))
32
35
  end
33
36
 
34
37
  private
35
38
 
39
+ def type_failure(value, message, types, context, position, stage = :argument) # rubocop:disable Metrics/ParameterLists
40
+ return Signature::TypeMismatch.new(message, origin: validation_origin) unless context
41
+
42
+ context.type_error(message, position: position, expected_types: types, actual_type: native_type(value),
43
+ origin: validation_origin, stage: stage)
44
+ end
45
+
46
+ def native_type(value)
47
+ return :number if Types.number?(value)
48
+
49
+ NATIVE_TYPES.find { |klass, _type| value.is_a?(klass) }&.last || :unknown
50
+ end
51
+
36
52
  def validation_origin
37
53
  nil
38
54
  end
@@ -14,60 +14,84 @@ module JMESPath
14
14
  "min_by" => [[:array], [:expref]], "max_by" => [[:array], [:expref]]
15
15
  }.freeze
16
16
 
17
+ DESCRIPTIONS = {
18
+ "avg" => "Mean, or null for an empty array.",
19
+ "sum" => "Sum, including zero for an empty array.",
20
+ "min" => "Minimum, or null for an empty array.",
21
+ "max" => "Maximum, or null for an empty array.",
22
+ "sort" => "Stable sorted array of numbers or strings.",
23
+ "join" => "Join strings with a separator.",
24
+ "keys" => "Object keys as strings.",
25
+ "values" => "Object values.",
26
+ "merge" => "Merge objects, with later values winning.",
27
+ "map" => "Map an expression over an array, including null results.",
28
+ "sort_by" => "Stable array sorted by homogeneous expression results.",
29
+ "min_by" => "Input element with minimum expression result, or null for an empty array.",
30
+ "max_by" => "Input element with maximum expression result, or null for an empty array."
31
+ }.freeze
32
+
17
33
  def self.register(registry)
18
34
  SIGNATURES.reduce(registry) do |result, (name, signature)|
19
- result.with_builtin(name, signature: signature, handler: name.to_sym, variadic: name == "merge")
35
+ result.with_builtin(name, signature: signature, handler: name.to_sym,
36
+ description: DESCRIPTIONS.fetch(name), variadic: name == "merge")
20
37
  end
21
38
  end
22
39
 
23
40
  private
24
41
 
25
- def sum(value)
26
- array(value).sum { |item| number(item) }
42
+ def sum(value, context:)
43
+ element_context = context.array_element(1)
44
+ array(value, context: context, position: 1).sum { |item| number(item, context: element_context, position: 1) }
27
45
  end
28
46
 
29
- def avg(value)
30
- values = array(value)
31
- number(sum(value)).fdiv(values.length) unless values.empty?
47
+ def avg(value, context:)
48
+ values = array(value, context: context, position: 1)
49
+ return if values.empty?
50
+
51
+ number(sum(value, context: context), context: context.array_element(1), position: 1).fdiv(values.length)
32
52
  end
33
53
 
34
- def min(value)
35
- array(value).min_by { |item| sort_key(item) }
54
+ def min(value, context:)
55
+ element_context = context.array_element(1)
56
+ array(value, context: context, position: 1).min_by { |item| sort_key(item, context: element_context) }
36
57
  end
37
58
 
38
- def max(value)
39
- array(value).max_by { |item| sort_key(item) }
59
+ def max(value, context:)
60
+ element_context = context.array_element(1)
61
+ array(value, context: context, position: 1).max_by { |item| sort_key(item, context: element_context) }
40
62
  end
41
63
 
42
- def sort(value)
43
- array(value).each_with_index.sort_by do |item, index|
44
- [sort_key(item), index]
64
+ def sort(value, context:)
65
+ element_context = context.array_element(1)
66
+ array(value, context: context, position: 1).each_with_index.sort_by do |item, index|
67
+ [sort_key(item, context: element_context), index]
45
68
  end.map(&:first)
46
69
  end
47
70
 
48
- def join(separator, value)
49
- array(value).map do |item|
50
- string(item)
51
- end.join(string(separator))
71
+ def join(separator, value, context:)
72
+ element_context = context.array_element(2)
73
+ array(value, context: context, position: 2).map do |item|
74
+ string(item, context: element_context, position: 2)
75
+ end.join(string(separator, context: context, position: 1))
52
76
  end
53
77
 
54
- def keys(value)
55
- object(value).keys.map(&:to_s)
78
+ def keys(value, context:)
79
+ object(value, context: context, position: 1).keys.map(&:to_s)
56
80
  end
57
81
 
58
- def values(value)
59
- object(value).values
82
+ def values(value, context:)
83
+ object(value, context: context, position: 1).values
60
84
  end
61
85
 
62
- def merge(*values)
86
+ def merge(*values, context:)
63
87
  # @type var result: Hash[value, value]
64
88
  result = {}
65
- values.each { |value| result.merge!(object(value)) }
89
+ values.each_with_index { |value, index| result.merge!(object(value, context: context, position: index + 1)) }
66
90
  result
67
91
  end
68
92
 
69
- def sort_key(value)
70
- DataAccess.string(value) || number(value)
93
+ def sort_key(value, context:, position: 1, stage: :argument)
94
+ DataAccess.string(value) || number(value, context: context, position: position, stage: stage)
71
95
  end
72
96
  end
73
97
 
@@ -8,17 +8,17 @@ module JMESPath
8
8
 
9
9
  private
10
10
 
11
- def to_array(value)
11
+ def to_array(value, **)
12
12
  DataAccess.array(value) || [value]
13
13
  end
14
14
 
15
- def to_string(value)
15
+ def to_string(value, **)
16
16
  DataAccess.string(value) || JSON.generate(Types.normalize(value))
17
17
  rescue JSON::NestingError
18
18
  raise Errors::InvalidValueError, "Value is too deeply nested to serialize"
19
19
  end
20
20
 
21
- def to_number(value)
21
+ def to_number(value, **)
22
22
  return value if Types.number?(value)
23
23
 
24
24
  text = DataAccess.string(value)&.strip
@@ -10,10 +10,15 @@ module JMESPath
10
10
 
11
11
  private :number, :string, :array, :reference, :object
12
12
 
13
- def call(definition, name, arguments, disable_visit_errors: false)
14
- definition.signature.validate!(arguments, name, origin: self)
13
+ def call(definition, name, arguments, disable_visit_errors: false, context: nil)
14
+ context ||= ValidationContext.new(name, definition.signature)
15
+ definition.signature.validate!(arguments, name, origin: self, context: context)
15
16
  implementation = definition.implementation
16
- implementation.is_a?(Symbol) ? send(implementation, *arguments) : implementation.call(*arguments)
17
+ if implementation.is_a?(Symbol)
18
+ send(implementation, *arguments, context: context)
19
+ else
20
+ implementation.call(*arguments)
21
+ end
17
22
  rescue Signature::TypeMismatch, Signature::ArityMismatch => e
18
23
  raise unless disable_visit_errors && e.origin.equal?(self)
19
24
 
@@ -8,51 +8,68 @@ module JMESPath
8
8
 
9
9
  private
10
10
 
11
- def map(expression, value)
12
- reference = reference(expression)
13
- array(value).map { |item| reference.call(item) }
11
+ def map(expression, value, context:)
12
+ reference = reference(expression, context: context, position: 1)
13
+ array(value, context: context, position: 2).map { |item| reference.call(item) }
14
14
  end
15
15
 
16
- def sort_by(value, expression)
17
- reference = reference(expression)
16
+ def sort_by(value, expression, context:)
17
+ reference = reference(expression, context: context, position: 2)
18
18
  # @type var expected: Symbol?
19
19
  expected = nil
20
+ # @type var result_context: ValidationContext?
21
+ result_context = nil
20
22
  index = 0
21
- array(value).sort_by do |item|
23
+ array(value, context: context, position: 1).sort_by do |item|
22
24
  key = reference.call(item)
23
- expected = checked_type(key, expected)
25
+ type = checked_type(key, expected, context: context)
26
+ expected = type
27
+ key_context = result_context || context.with_types([type])
28
+ result_context = key_context
24
29
  index += 1
25
- [sort_key(key), index]
30
+ [sort_key(key, context: key_context, position: 2, stage: :expression_result), index]
26
31
  end
27
32
  end
28
33
 
29
- def min_by(value, expression)
30
- keyed(value, expression).min_by(&:first)&.last
34
+ def min_by(value, expression, context:)
35
+ keyed(value, expression, context: context).min_by(&:first)&.last
31
36
  end
32
37
 
33
- def max_by(value, expression)
34
- keyed(value, expression).max_by(&:first)&.last
38
+ def max_by(value, expression, context:)
39
+ keyed(value, expression, context: context).max_by(&:first)&.last
35
40
  end
36
41
 
37
- def keyed(value, expression)
38
- reference = reference(expression)
42
+ def keyed(value, expression, context:)
43
+ reference = reference(expression, context: context, position: 2)
39
44
  # @type var expected: Symbol?
40
45
  expected = nil
41
- array(value).map do |item|
46
+ # @type var result_context: ValidationContext?
47
+ result_context = nil
48
+ array(value, context: context, position: 1).map do |item|
42
49
  key = reference.call(item)
43
- expected = checked_type(key, expected)
44
- [sort_key(key), item]
50
+ type = checked_type(key, expected, context: context)
51
+ expected = type
52
+ key_context = result_context || context.with_types([type])
53
+ result_context = key_context
54
+ [sort_key(key, context: key_context, position: 2, stage: :expression_result), item]
45
55
  end
46
56
  end
47
57
 
48
- def checked_type(value, expected)
58
+ def checked_type(value, expected, context:)
49
59
  type = Types.name(value)
50
60
  unless SORTABLE_TYPES.include?(type)
51
- raise Signature::TypeMismatch.new("Expression keys must be numbers or strings", origin: validation_origin)
61
+ raise context.type_error(
62
+ "Expression keys must be numbers or strings",
63
+ position: 2, expected_types: expected ? [expected] : SORTABLE_TYPES, actual_type: type,
64
+ origin: validation_origin, stage: :expression_result
65
+ )
52
66
  end
53
67
  if expected && expected != type
54
- raise Signature::TypeMismatch.new("Expression keys must have one type",
55
- origin: validation_origin)
68
+ raise context.type_error(
69
+ "Expression keys must have one type",
70
+ position: 2, expected_types: [expected], actual_type: type,
71
+ origin: validation_origin, stage: :expression_result
72
+ )
56
73
  end
57
74
 
58
75
  type
@@ -13,57 +13,74 @@ module JMESPath
13
13
  "to_number" => [[:any]], "not_null" => [[:any]]
14
14
  }.freeze
15
15
 
16
+ DESCRIPTIONS = {
17
+ "abs" => "Absolute value.",
18
+ "ceil" => "Integer rounded upward.",
19
+ "floor" => "Integer rounded downward.",
20
+ "contains" => "Test string or array membership.",
21
+ "ends_with" => "Test whether a string ends with a suffix.",
22
+ "starts_with" => "Test whether a string starts with a prefix.",
23
+ "length" => "Character or element count.",
24
+ "reverse" => "Reverse a string or array.",
25
+ "type" => "JMESPath type name.",
26
+ "to_array" => "Wrap a value in an array, preserving arrays.",
27
+ "to_string" => "String or compact JSON representation.",
28
+ "to_number" => "Number when convertible, otherwise null.",
29
+ "not_null" => "First non-null value."
30
+ }.freeze
31
+
16
32
  def self.register(registry)
17
33
  SIGNATURES.reduce(registry) do |result, (name, signature)|
18
34
  handler = %w[contains ends_with starts_with].include?(name) ? "#{name}?" : name
19
- result.with_builtin(name, signature: signature, handler: handler.to_sym, variadic: name == "not_null")
35
+ result.with_builtin(name, signature: signature, handler: handler.to_sym,
36
+ description: DESCRIPTIONS.fetch(name), variadic: name == "not_null")
20
37
  end
21
38
  end
22
39
 
23
40
  private
24
41
 
25
- def abs(value)
26
- number(value).abs
42
+ def abs(value, context:)
43
+ number(value, context: context, position: 1).abs
27
44
  end
28
45
 
29
- def ceil(value)
30
- number(value).ceil
46
+ def ceil(value, context:)
47
+ number(value, context: context, position: 1).ceil
31
48
  end
32
49
 
33
- def floor(value)
34
- number(value).floor
50
+ def floor(value, context:)
51
+ number(value, context: context, position: 1).floor
35
52
  end
36
53
 
37
- def contains?(subject, search)
54
+ def contains?(subject, search, context:)
38
55
  text = DataAccess.string(subject)
39
- return text.include?(string(search)) if text
56
+ return text.include?(string(search, context: context, position: 2)) if text
40
57
 
41
- array(subject).any? { |item| Types.equal?(item, search) }
58
+ array(subject, context: context, position: 1).any? { |item| Types.equal?(item, search) }
42
59
  end
43
60
 
44
- def ends_with?(text, suffix)
45
- string(text).end_with?(string(suffix))
61
+ def ends_with?(text, suffix, context:)
62
+ string(text, context: context, position: 1).end_with?(string(suffix, context: context, position: 2))
46
63
  end
47
64
 
48
- def starts_with?(text, prefix)
49
- string(text).start_with?(string(prefix))
65
+ def starts_with?(text, prefix, context:)
66
+ string(text, context: context, position: 1).start_with?(string(prefix, context: context, position: 2))
50
67
  end
51
68
 
52
- def length(value)
53
- collection = DataAccess.string(value) || DataAccess.array(value) || object(value)
69
+ def length(value, context:)
70
+ collection = DataAccess.string(value) || DataAccess.array(value) || object(value, context: context, position: 1)
54
71
  collection.length
55
72
  end
56
73
 
57
- def reverse(value)
58
- (DataAccess.string(value) || array(value)).reverse
74
+ def reverse(value, context:)
75
+ (DataAccess.string(value) || array(value, context: context, position: 1)).reverse
59
76
  end
60
77
 
61
- def type(value)
78
+ def type(value, **)
62
79
  name = Types.name(value)
63
80
  name.to_s unless name == :unknown
64
81
  end
65
82
 
66
- def not_null(*values)
83
+ def not_null(*values, **)
67
84
  values.find { |value| !value.nil? }
68
85
  end
69
86
  end
@@ -10,9 +10,9 @@ module JMESPath
10
10
  module FailureOrigin
11
11
  attr_reader :origin
12
12
 
13
- def initialize(message = nil, origin: nil)
13
+ def initialize(message = nil, origin: nil, **metadata)
14
14
  @origin = origin
15
- super(message)
15
+ super(message, **metadata)
16
16
  end
17
17
  end
18
18
 
@@ -26,6 +26,12 @@ module JMESPath
26
26
  include FailureOrigin
27
27
  end
28
28
 
29
+ attr_reader :arguments
30
+
31
+ def variadic?
32
+ @variadic
33
+ end
34
+
29
35
  def initialize(arguments, variadic: false)
30
36
  raise ArgumentError, "Invalid function signature" unless valid_definition?(arguments)
31
37
  raise ArgumentError, "Variadic signatures require an argument" if variadic && arguments.empty?
@@ -35,21 +41,29 @@ module JMESPath
35
41
  freeze
36
42
  end
37
43
 
38
- def validate!(values, name, origin: nil)
44
+ def validate!(values, name, origin: nil, context: nil)
45
+ context ||= ValidationContext.new(name, self)
39
46
  unless valid_arity?(values.length)
40
- raise ArityMismatch.new("Invalid number of arguments for #{name}", origin: origin)
47
+ raise context.arity_error("Invalid number of arguments for #{name}", actual_arity: values.length,
48
+ origin: origin)
41
49
  end
42
50
 
43
51
  values.each_with_index do |value, index|
44
52
  types = @arguments.fetch(index) { @arguments.fetch(-1) }
45
53
  next if types.any? { |type| matches?(value, type) }
46
54
 
47
- raise TypeMismatch.new("Invalid argument #{index + 1} for #{name}: #{Types.name(value)}", origin: origin)
55
+ reject_type!(value, index, types, name, origin, context)
48
56
  end
49
57
  end
50
58
 
51
59
  private
52
60
 
61
+ def reject_type!(value, index, types, name, origin, context) # rubocop:disable Metrics/ParameterLists
62
+ actual_type = Types.name(value)
63
+ raise context.type_error("Invalid argument #{index + 1} for #{name}: #{actual_type}",
64
+ position: index + 1, expected_types: types, actual_type: actual_type, origin: origin)
65
+ end
66
+
53
67
  def valid_definition?(arguments)
54
68
  arguments.is_a?(Array) && arguments.all? do |types|
55
69
  types.is_a?(Array) && !types.empty? && (types - TYPES).empty?