class RuboCop::Cop::Naming::PredicatePrefix
Checks that predicate method names end with a question mark and do not start with a forbidden prefix.
A method is determined to be a predicate method if its name starts with one of the prefixes listed in the ‘NamePrefix` configuration. The list defaults to `is_`, `has_`, and `have_` but may be overridden.
Predicate methods must end with a question mark.
When ‘ForbiddenPrefixes` is also set (as it is by default), predicate methods which begin with a forbidden prefix are not allowed, even if they end with a `?`. These methods should be changed to remove the prefix.
When ‘UseSorbetSigs` set to true (optional), the cop will only report offenses if the method has a Sorbet `sig` with a return type of `T::Boolean`. Dynamic methods are not supported with this configuration.
@example NamePrefix: [‘is_’, ‘has_’, ‘have_’] (default)
# bad def is_even(value) end # When ForbiddenPrefixes: ['is_', 'has_', 'have_'] (default) # good def even?(value) end # When ForbiddenPrefixes: [] # good def is_even?(value) end
@example NamePrefix: [‘seems_to_be_’]
# bad def seems_to_be_even(value) end # When ForbiddenPrefixes: ['seems_to_be_'] # good def even?(value) end # When ForbiddenPrefixes: [] # good def seems_to_be_even?(value) end
@example AllowedMethods: [‘is_a?’] (default)
# Despite starting with the `is_` prefix, this method is allowed # good def is_a?(value) end
@example AllowedMethods: [‘is_even?’]
# good def is_even?(value) end
@example UseSorbetSigs: false (default)
# bad sig { returns(String) } def is_this_thing_on "yes" end # good - Sorbet signature is not evaluated sig { returns(String) } def is_this_thing_on? "yes" end
@example UseSorbetSigs: true
# bad sig { returns(T::Boolean) } def odd(value) end # good sig { returns(T::Boolean) } def odd?(value) end
@example MethodDefinitionMacros: [‘define_method’, ‘define_singleton_method’] (default)
# bad define_method(:is_even) { |value| } # good define_method(:even?) { |value| }
@example MethodDefinitionMacros: [‘def_node_matcher’]
# bad def_node_matcher(:is_even) { |value| } # good def_node_matcher(:even?) { |value| }
Public Instance Methods
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 126 def on_def(node) predicate_prefixes.each do |prefix| method_name = node.method_name.to_s next if allowed_method_name?(method_name, prefix) next if use_sorbet_sigs? && !sorbet_sig?(node, return_type: 'T::Boolean') add_offense( node.loc.name, message: message(method_name, expected_name(method_name, prefix)) ) end end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 113 def on_send(node) dynamic_method_define(node) do |method_name| predicate_prefixes.each do |prefix| next if allowed_method_name?(method_name.to_s, prefix) add_offense( node.first_argument, message: message(method_name, expected_name(method_name.to_s, prefix)) ) end end end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 141 def validate_config forbidden_prefixes.each do |forbidden_prefix| next if predicate_prefixes.include?(forbidden_prefix) raise ValidationError, <<~MSG.chomp The `Naming/PredicatePrefix` cop is misconfigured. Prefix #{forbidden_prefix} must be included in NamePrefix because it is included in ForbiddenPrefixes. MSG end end
Private Instance Methods
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 164 def allowed_method_name?(method_name, prefix) !(method_name.start_with?(prefix) && # cheap check to avoid allocating Regexp method_name.match?(/^#{prefix}[^0-9]/)) || method_name == expected_name(method_name, prefix) || method_name.end_with?('=') || allowed_method?(method_name) end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 172 def expected_name(method_name, prefix) new_name = if forbidden_prefixes.include?(prefix) method_name.sub(prefix, '') else method_name.dup end new_name << '?' unless method_name.end_with?('?') new_name end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 186 def forbidden_prefixes cop_config['ForbiddenPrefixes'] end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 182 def message(method_name, new_name) "Rename `#{method_name}` to `#{new_name}`." end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 198 def method_definition_macro?(macro_name) cop_config['MethodDefinitionMacros'].include?(macro_name.to_s) end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 190 def predicate_prefixes cop_config['NamePrefix'] end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 158 def sorbet_sig?(node, return_type: nil) return false unless (type = sorbet_return_type(node.left_sibling)) type.source == return_type end
Source
# File lib/rubocop/cop/naming/predicate_prefix.rb, line 194 def use_sorbet_sigs? cop_config['UseSorbetSigs'] end