lib/rubocop/cop/rails/pluralization_grammar.rb
# frozen_string_literal: true module RuboCop module Cop module Rails # Checks for correct grammar when using ActiveSupport's # core extensions to the numeric classes. # # @example # # bad # 3.day.ago # 1.months.ago # 5.megabyte # 1.gigabytes # # # good # 3.days.ago # 1.month.ago # 5.megabytes # 1.gigabyte class PluralizationGrammar < Base extend AutoCorrector SINGULAR_METHODS = { second: :seconds, minute: :minutes, hour: :hours, day: :days, week: :weeks, fortnight: :fortnights, month: :months, year: :years, byte: :bytes, kilobyte: :kilobytes, megabyte: :megabytes, gigabyte: :gigabytes, terabyte: :terabytes, petabyte: :petabytes, exabyte: :exabytes, zettabyte: :zettabytes }.freeze RESTRICT_ON_SEND = SINGULAR_METHODS.keys + SINGULAR_METHODS.values PLURAL_METHODS = SINGULAR_METHODS.invert.freeze MSG = 'Prefer `%<number>s.%<correct>s`.' def on_send(node) return unless duration_method?(node.method_name) && literal_number?(node.receiver) && offense?(node) number, = *node.receiver add_offense(node, message: message(number, node.method_name)) do |corrector| method_name = node.loc.selector.source corrector.replace(node.loc.selector, correct_method(method_name)) end end private def message(number, method_name) format(MSG, number: number, correct: correct_method(method_name)) end def correct_method(method_name) if plural_method?(method_name) singularize(method_name) else pluralize(method_name) end end def offense?(node) number, = *node.receiver (singular_receiver?(number) && plural_method?(node.method_name)) || (plural_receiver?(number) && singular_method?(node.method_name)) end def plural_method?(method_name) method_name.to_s.end_with?('s') end def singular_method?(method_name) !plural_method?(method_name) end def singular_receiver?(number) number.abs == 1 end def plural_receiver?(number) !singular_receiver?(number) end def literal_number?(node) node&.type?(:int, :float) end def pluralize(method_name) SINGULAR_METHODS.fetch(method_name.to_sym).to_s end def singularize(method_name) PLURAL_METHODS.fetch(method_name.to_sym).to_s end def duration_method?(method_name) SINGULAR_METHODS.key?(method_name) || PLURAL_METHODS.key?(method_name) end end end end end