class RuboCop::Cop::Style::NumericLiterals

Checks for big numeric literals without `_` between groups of digits in them.

Additional allowed patterns can be added by adding regexps to the `AllowedPatterns` configuration. All regexps are treated as anchored even if the patterns do not contain anchors (so `d{4}_d{4}` will allow `1234_5678` but not `1234_5678_9012`).

NOTE: Even if `AllowedPatterns` are given, autocorrection will only correct to the standard pattern of an `_` every 3 digits.

@example

# bad
1000000
1_00_000
1_0000

# good
1_000_000
1000

@example Strict: false (default)

# good
10_000_00 # typical representation of $10,000 in cents

@example Strict: true

# bad
10_000_00 # typical representation of $10,000 in cents

@example AllowedNumbers: [3000]

# good
3000 # You can specify allowed numbers. (e.g. port number)

Constants

DELIMITER_REGEXP
MSG

Public Instance Methods

on_float(node) click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 60
def on_float(node)
  check(node)
end
on_int(node) click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 56
def on_int(node)
  check(node)
end

Private Instance Methods

allowed_numbers() click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 118
def allowed_numbers
  cop_config.fetch('AllowedNumbers', []).map(&:to_s)
end
allowed_patterns() click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 122
def allowed_patterns
  # Convert the patterns to be anchored
  super.map { |regexp| Regexp.new(/\A#{regexp}\z/) }
end
check(node) click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 66
def check(node)
  int = integer_part(node)
  # TODO: handle non-decimal literals as well
  return if int.start_with?('0')
  return if allowed_numbers.include?(int)
  return if matches_allowed_pattern?(int)
  return unless int.size >= min_digits

  case int
  when /^\d+$/
    register_offense(node) { self.min_digits = int.size + 1 }
  when /\d{4}/, short_group_regex
    register_offense(node) { self.config_to_allow_offenses = { 'Enabled' => false } }
  end
end
format_int_part(int_part) click to toggle source

@param int_part [String]

# File lib/rubocop/cop/style/numeric_literals.rb, line 107
def format_int_part(int_part)
  int_part = Integer(int_part)
  formatted_int = int_part.abs.to_s.reverse.gsub(/...(?=.)/, '\&_').reverse
  formatted_int.insert(0, '-') if int_part.negative?
  formatted_int
end
format_number(node) click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 93
def format_number(node)
  source = node.source.gsub(/\s+/, '')
  int_part, additional_part = source.split(DELIMITER_REGEXP, 2)
  formatted_int = format_int_part(int_part)
  delimiter = source[DELIMITER_REGEXP]

  if additional_part
    formatted_int + delimiter + additional_part
  else
    formatted_int
  end
end
min_digits() click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 114
def min_digits
  cop_config['MinDigits']
end
register_offense(node) { || ... } click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 82
def register_offense(node, &_block)
  add_offense(node) do |corrector|
    yield
    corrector.replace(node, format_number(node))
  end
end
short_group_regex() click to toggle source
# File lib/rubocop/cop/style/numeric_literals.rb, line 89
def short_group_regex
  cop_config['Strict'] ? /_\d{1,2}(_|$)/ : /_\d{1,2}_/
end