Don't use case-matching for proc arguments

A proc on the left side of === calls itself passing in the right side.
This causes typing issues and is easier to avoid for now.
Procs arguments are compared with standard equality (==) instead of case-equality (===).
This commit is contained in:
Michael Miller 2022-12-17 19:19:33 -07:00
parent 9f54a9e542
commit 149c0e6e4b
No known key found for this signature in database
GPG Key ID: 32B47AE8F388A1FF
4 changed files with 45 additions and 6 deletions

View File

@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplify string representation of mock-related types.
- Remove unnecessary redefinitions of methods when adding stub functionality to a type.
- Allow metadata to be stored as nil to reduce overhead when tracking nodes without tags.
- Use normal equality (==) instead of case-equality (===) with proc arguments in stubs.
## [0.11.4] - 2022-11-27
### Added

View File

@ -6,11 +6,41 @@ module Spectator
to_s(io)
end
# Utility method for comparing two tuples considering special types.
private def compare_tuples(a : Tuple, b : Tuple)
return false if a.size != b.size
a.zip(b) do |a_value, b_value|
if a_value.is_a?(Proc)
# Using procs as argument matchers isn't supported currently.
# Compare directly instead.
return false unless a_value == b_value
else
return false unless a_value === b_value
end
end
true
end
# Utility method for comparing two tuples considering special types.
# Supports nilable tuples (ideal for splats).
private def compare_tuples(a : Tuple?, b : Tuple?)
return false if a.nil? ^ b.nil?
compare_tuples(a.not_nil!, b.not_nil!)
end
# Utility method for comparing two named tuples ignoring order.
private def compare_named_tuples(a : NamedTuple, b : NamedTuple)
a.each do |k, v1|
v2 = b.fetch(k) { return false }
return false unless v1 === v2
if v1.is_a?(Proc)
# Using procs as argument matchers isn't supported currently.
# Compare directly instead.
return false unless v1 == v2
else
return false unless v1 === v2
end
end
true
end

View File

@ -81,7 +81,7 @@ module Spectator
# Checks if another set of arguments matches this set of arguments.
def ===(other : Arguments)
positional === other.positional && compare_named_tuples(kwargs, other.kwargs)
compare_tuples(positional, other.positional) && compare_named_tuples(kwargs, other.kwargs)
end
# :ditto:
@ -95,13 +95,21 @@ module Spectator
v1 = positional[i]
i += 1
return false unless v1 === v2
if v1.is_a?(Proc)
return false unless v1 == v2
else
return false unless v1 === v2
end
end
other.splat.try &.each do |v2|
v1 = positional.fetch(i) { return false }
i += 1
return false unless v1 === v2
if v1.is_a?(Proc)
return false unless v1 == v2
else
return false unless v1 === v2
end
end
i == positional.size

View File

@ -122,12 +122,12 @@ module Spectator
# Checks if another set of arguments matches this set of arguments.
def ===(other : Arguments)
positional === other.positional && compare_named_tuples(kwargs, other.kwargs)
compare_tuples(positional, other.positional) && compare_named_tuples(kwargs, other.kwargs)
end
# :ditto:
def ===(other : FormalArguments)
compare_named_tuples(args, other.args) && splat === other.splat && compare_named_tuples(kwargs, other.kwargs)
compare_named_tuples(args, other.args) && compare_tuples(splat, other.splat) && compare_named_tuples(kwargs, other.kwargs)
end
end
end