class FlexMock::SignatureValidator
Validate that the call matches a given signature
The validator created by {#initialize} matches any method call
Attributes
The number of optional arguments
The names of optional keyword arguments @return [Set<Symbol>]
The number of required arguments
The names of required keyword arguments @return [Set<Symbol>]
Public Class Methods
Create a validator that represents the signature of an existing method
# File lib/flexmock/validators.rb, line 291 def self.from_instance_method(exp, instance_method) required_arguments, optional_arguments, splat = 0, 0, false required_keyword_arguments, optional_keyword_arguments, keyword_splat = Set.new, Set.new, false instance_method.parameters.each do |type, name| case type when :req then required_arguments += 1 when :opt then optional_arguments += 1 when :rest then splat = true when :keyreq then required_keyword_arguments << name when :key then optional_keyword_arguments << name when :keyrest then keyword_splat = true when :block else raise ArgumentError, "cannot interpret parameter type #{type}" end end new(exp, required_arguments: required_arguments, optional_arguments: optional_arguments, splat: splat, required_keyword_arguments: required_keyword_arguments, optional_keyword_arguments: optional_keyword_arguments, keyword_splat: keyword_splat) end
# File lib/flexmock/validators.rb, line 177 def initialize( expectation, required_arguments: 0, optional_arguments: 0, splat: true, required_keyword_arguments: [], optional_keyword_arguments: [], keyword_splat: true) @exp = expectation @required_arguments = required_arguments @optional_arguments = optional_arguments @required_keyword_arguments = required_keyword_arguments.to_set @optional_keyword_arguments = optional_keyword_arguments.to_set @splat = splat @keyword_splat = keyword_splat end
Public Instance Methods
# File lib/flexmock/validators.rb, line 201 def describe ".with_signature( required_arguments: #{self.required_arguments}, optional_arguments: #{self.optional_arguments}, required_keyword_arguments: #{self.required_keyword_arguments.to_a}, optional_keyword_arguments: #{self.optional_keyword_arguments.to_a}, splat: #{self.splat?}, keyword_splat: #{self.keyword_splat?})" end
Whether this method may have keyword arguments
# File lib/flexmock/validators.rb, line 168 def expects_keyword_arguments? keyword_splat? || !required_keyword_arguments.empty? || !optional_keyword_arguments.empty? end
Whether there is a splat for keyword arguments (double-star)
# File lib/flexmock/validators.rb, line 163 def keyword_splat? @keyword_splat end
Whether this tests anything
It will return if this validator would validate any set of arguments
# File lib/flexmock/validators.rb, line 197 def null? splat? && keyword_splat? end
Whether this method may have keyword arguments
# File lib/flexmock/validators.rb, line 173 def requires_keyword_arguments? !required_keyword_arguments.empty? end
Whether there is a positional argument splat
# File lib/flexmock/validators.rb, line 153 def splat? @splat end
Validates whether the given arguments match the expected signature
@param [Array] args @raise ValidationFailed
# File lib/flexmock/validators.rb, line 215 def validate(args) args = args.dup kw_args = Hash.new last_is_proc = false begin if args.last.kind_of?(Proc) args.pop last_is_proc = true end rescue NoMethodError end last_is_kw_hash = false if expects_keyword_arguments? last_is_kw_hash = begin args.last.kind_of?(Hash) rescue NoMethodError end if last_is_kw_hash kw_args = args.pop elsif requires_keyword_arguments? raise ValidationFailed, "#{@exp} expects keyword arguments but none were provided" end end # There is currently no way to disambiguate "given a block" from "given a # proc as last argument" ... give some leeway in this case positional_count = args.size if required_arguments > positional_count if requires_keyword_arguments? raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{positional_count}" end if (required_arguments - positional_count) == 1 && (last_is_kw_hash || last_is_proc) if last_is_kw_hash last_is_kw_hash = false kw_args = Hash.new else last_is_proc = false end positional_count += 1 elsif (required_arguments - positional_count) == 2 && (last_is_kw_hash && last_is_proc) last_is_kw_hash = false kw_args = Hash.new last_is_proc = false positional_count += 2 else raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{positional_count}" end end if !splat? && (required_arguments + optional_arguments) < positional_count if !last_is_proc || (required_arguments + optional_arguments) < positional_count - 1 raise ValidationFailed, "#{@exp} expects at most #{required_arguments + optional_arguments} positional arguments but got #{positional_count}" end end missing_keyword_arguments = required_keyword_arguments. find_all { |k| !kw_args.has_key?(k) } if !missing_keyword_arguments.empty? raise ValidationFailed, "#{@exp} missing required keyword arguments #{missing_keyword_arguments.map(&:to_s).sort.join(", ")}" end if !keyword_splat? kw_args.each_key do |k| if !optional_keyword_arguments.include?(k) && !required_keyword_arguments.include?(k) raise ValidationFailed, "#{@exp} given unexpected keyword argument #{k}" end end end end