class RuboCop::Cop::Style::NumericPredicate
bar.baz.positive?
foo.negative?
# bad
foo.zero?
# bad
# good
@example AllowedPatterns: [‘zero’] with EnforcedStyle: predicate
bar.baz.positive?
foo.negative?
foo.zero?
# bad
@example AllowedPatterns: [] (default) with EnforcedStyle: comparison
bar.baz > 0
0 > foo
# bad
foo == 0
# good
@example AllowedMethods: [==] with EnforcedStyle: predicate
bar.baz > 0
0 > foo
foo == 0
# bad
@example AllowedMethods: [] (default) with EnforcedStyle: predicate
bar.baz > 0
0 > foo
foo == 0
# good
bar.baz.positive?
foo.negative?
foo.zero?
# bad
@example EnforcedStyle: comparison
bar.baz.positive?
foo.negative?
foo.zero?
# good
bar.baz > 0
0 > foo
foo == 0
# bad
@example EnforcedStyle: predicate (default)
to a false positive for non-standard classes.
defines the predicates or can be compared to a number, which may lead
This cop is unsafe because it cannot be guaranteed that the receiver
@safety
not themselves ‘Integer` polymorphic.
populated with objects which can be compared with integers, but are
This cop allows comparisons to global variables, since they are often
`!= 0`.
but not `true` and `false`, and thus not always interchangeable with
This cop disregards `#nonzero?` as its value is truthy or falsey,
By default, there are no methods to allowed.
This cop can be customized allowed methods with `AllowedMethods`.
This cop can also be configured to do the reverse.
These can be replaced by their respective predicate methods.
`>`, `<`) to test numbers as zero, positive, or negative.
Checks for usage of comparison operators (`==`,
def allowed_method_name?(name)
def allowed_method_name?(name) allowed_method?(name) || matches_allowed_pattern?(name) end
def check(node)
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
def invert
def invert lambda do |comparison, numeric| comparison = { :> => :<, :< => :> }[comparison] || comparison [numeric, comparison] end end
def on_send(node)
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
def parenthesized_source(node)
def parenthesized_source(node) if require_parentheses?(node) "(#{node.source})" else node.source end end
def replacement(numeric, operation)
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
def replacement_supported?(operator)
def replacement_supported?(operator) if %i[> <].include?(operator) target_ruby_version >= 2.3 else true end end
def require_parentheses?(node)
def require_parentheses?(node) node.send_type? && node.binary_operation? && !node.parenthesized? end