class RuboCop::Cop::Style::NumericPredicate
Checks for usage of comparison operators (`==`, `>`, `<`) to test numbers as zero, positive, or negative. These can be replaced by their respective predicate methods. This cop can also be configured to do the reverse.
This cop can be customized allowed methods with `AllowedMethods`. By default, there are no methods to allowed.
This cop disregards `#nonzero?` as its value is truthy or falsey, but not `true` and `false`, and thus not always interchangeable with `!= 0`.
This cop allows comparisons to global variables, since they are often populated with objects which can be compared with integers, but are not themselves `Integer` polymorphic.
@safety
This cop is unsafe because it cannot be guaranteed that the receiver defines the predicates or can be compared to a number, which may lead to a false positive for non-standard classes.
@example EnforcedStyle: predicate (default)
# bad foo == 0 0 > foo bar.baz > 0 # good foo.zero? foo.negative? bar.baz.positive?
@example EnforcedStyle: comparison
# bad foo.zero? foo.negative? bar.baz.positive? # good foo == 0 0 > foo bar.baz > 0
@example AllowedMethods: [] (default) with EnforcedStyle: predicate
# bad foo == 0 0 > foo bar.baz > 0
@example AllowedMethods: [==] with EnforcedStyle: predicate
# good foo == 0 # bad 0 > foo bar.baz > 0
@example AllowedPatterns: [] (default) with EnforcedStyle: comparison
# bad foo.zero? foo.negative? bar.baz.positive?
@example AllowedPatterns: ['zero'] with EnforcedStyle: predicate
# good # bad foo.zero? # bad foo.negative? bar.baz.positive?
Constants
- MSG
- REPLACEMENTS
- RESTRICT_ON_SEND
Public Instance Methods
# File lib/rubocop/cop/style/numeric_predicate.rb, line 90 def on_send(node) numeric, replacement = check(node) return unless numeric return if allowed_method_name?(node.method_name) || node.each_ancestor(:send, :block).any? do |ancestor| allowed_method_name?(ancestor.method_name) end message = format(MSG, prefer: replacement, current: node.source) add_offense(node, message: message) do |corrector| corrector.replace(node, replacement) end end
Private Instance Methods
# File lib/rubocop/cop/style/numeric_predicate.rb, line 107 def allowed_method_name?(name) allowed_method?(name) || matches_allowed_pattern?(name) end
# File lib/rubocop/cop/style/numeric_predicate.rb, line 111 def check(node) numeric, operator = if style == :predicate comparison(node) || inverted_comparison(node, &invert) else predicate(node) end return unless numeric && operator && replacement_supported?(operator) [numeric, replacement(numeric, operator)] end
# File lib/rubocop/cop/style/numeric_predicate.rb, line 152 def invert lambda do |comparison, numeric| comparison = { :> => :<, :< => :> }[comparison] || comparison [numeric, comparison] end end
# File lib/rubocop/cop/style/numeric_predicate.rb, line 132 def parenthesized_source(node) if require_parentheses?(node) "(#{node.source})" else node.source end end
# File lib/rubocop/cop/style/numeric_predicate.rb, line 124 def replacement(numeric, operation) if style == :predicate [parenthesized_source(numeric), REPLACEMENTS.invert[operation.to_s]].join('.') else [numeric.source, REPLACEMENTS[operation.to_s], 0].join(' ') end end
# File lib/rubocop/cop/style/numeric_predicate.rb, line 144 def replacement_supported?(operator) if %i[> <].include?(operator) target_ruby_version >= 2.3 else true end end
# File lib/rubocop/cop/style/numeric_predicate.rb, line 140 def require_parentheses?(node) node.send_type? && node.binary_operation? && !node.parenthesized? end