lib/rubocop/cop/rails/skips_model_validations.rb



# frozen_string_literal: true

module RuboCop
  module Cop
    module Rails
      # This cop checks for the use of methods which skip
      # validations which are listed in
      # http://guides.rubyonrails.org/active_record_validations.html#skipping-validations
      #
      # @example
      #   # bad
      #   Article.first.decrement!(:view_count)
      #   DiscussionBoard.decrement_counter(:post_count, 5)
      #   Article.first.increment!(:view_count)
      #   DiscussionBoard.increment_counter(:post_count, 5)
      #   person.toggle :active
      #   product.touch
      #   Billing.update_all("category = 'authorized', author = 'David'")
      #   user.update_attribute(website: 'example.com')
      #   user.update_columns(last_request_at: Time.current)
      #   Post.update_counters 5, comment_count: -1, action_count: 1
      #
      #   # good
      #   user.update_attributes(website: 'example.com')
      #   FileUtils.touch('file')
      class SkipsModelValidations < Cop
        MSG = 'Avoid using `%s` because it skips validations.'.freeze

        METHODS_WITH_ARGUMENTS = %w[decrement!
                                    decrement_counter
                                    increment!
                                    increment_counter
                                    toggle!
                                    update_all
                                    update_attribute
                                    update_column
                                    update_columns
                                    update_counters].freeze

        def_node_matcher :good_touch?, <<-PATTERN
          (send (const nil? :FileUtils) :touch ...)
        PATTERN

        def on_send(node)
          return unless blacklist.include?(node.method_name.to_s)

          _receiver, method_name, *args = *node

          if METHODS_WITH_ARGUMENTS.include?(method_name.to_s) && args.empty?
            return
          end

          return if good_touch?(node)

          add_offense(node, location: :selector)
        end

        private

        def message(node)
          format(MSG, node.method_name)
        end

        def blacklist
          cop_config['Blacklist'] || []
        end
      end
    end
  end
end