Class: RuboCop::Cop::Rails::RedundantPresenceValidationOnBelongsTo

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector, TargetRailsVersion
Includes:
RangeHelp
Defined in:
lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb

Overview

Since Rails 5.0 the default for ‘belongs_to` is `optional: false` unless `config.active_record.belongs_to_required_by_default` is explicitly set to `false`. The presence validator is added automatically, and explicit presence validation is redundant.

Examples:

# bad
belongs_to :user
validates :user, presence: true

# bad
belongs_to :user
validates :user_id, presence: true

# bad
belongs_to :author, foreign_key: :user_id
validates :user_id, presence: true

# good
belongs_to :user

# good
belongs_to :author, foreign_key: :user_id

Constant Summary collapse

MSG =
'Remove explicit presence validation for %<association>s.'
RESTRICT_ON_SEND =
%i[validates].freeze

Instance Method Summary collapse

Methods included from TargetRailsVersion

minimum_target_rails_version, support_target_rails_version?

Instance Method Details

#any_belongs_to?(node, association: ) ⇒ Array<RuboCop::AST::Node>?

Match a class with ‘belongs_to` with no regard to `foreign_key` option

Examples:

source that matches

belongs_to :user

source that matches - regardless of ‘foreign_key`

belongs_to :author, foreign_key: :user_id

Parameters:

  • node (RuboCop::AST::Node)
  • association (Symbol) (defaults to: )

Returns:

  • (Array<RuboCop::AST::Node>, nil)

    matching node



99
100
101
102
103
104
105
106
# File 'lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb', line 99

def_node_matcher :any_belongs_to?, <<~PATTERN
  (begin
    <
      $(send nil? :belongs_to (sym %association) ...)
      ...
    >
  )
PATTERN

#belongs_to?(node, key: , fk: ) ⇒ Array<RuboCop::AST::Node>

Match a class with a matching association, either by name or an explicit ‘foreign_key` option

Examples:

source that matches - fk matches ‘foreign_key` option

belongs_to :author, foreign_key: :user_id

source that matches - key matches association name

belongs_to :user

source that does not match - explicit ‘foreign_key` does not match

belongs_to :user, foreign_key: :account_id

Parameters:

  • node (RuboCop::AST::Node)
  • key (Symbol) (defaults to: )

    e.g. ‘:user`

  • fk (Symbol) (defaults to: )

    e.g. ‘:user_id`

Returns:

  • (Array<RuboCop::AST::Node>)

    matching nodes



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb', line 125

def_node_matcher :belongs_to?, <<~PATTERN
  (begin
    <
      ${
        #belongs_to_without_fk?(%key)         # belongs_to :user
        #belongs_to_with_a_matching_fk?(%fk)  # belongs_to :author, foreign_key: :user_id
      }
      ...
    >
  )
PATTERN

#belongs_to_with_a_matching_fk?(node, fk) ⇒ Array<RuboCop::AST::Node>

Match a matching ‘belongs_to` association with a matching explicit `foreign_key` option

Examples:

source that matches

belongs_to :author, foreign_key: :user_id

Parameters:

  • node (RuboCop::AST::Node)
  • fk (Symbol)

    e.g. ‘:user_id`

Returns:

  • (Array<RuboCop::AST::Node>)

    matching nodes



160
161
162
# File 'lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb', line 160

def_node_matcher :belongs_to_with_a_matching_fk?, <<~PATTERN
  (send nil? :belongs_to ... (hash <(pair (sym :foreign_key) (sym %1)) ...>))
PATTERN

#belongs_to_without_fk?(node, key) ⇒ Array<RuboCop::AST::Node>

Match a matching ‘belongs_to` association, without an explicit `foreign_key` option

Parameters:

  • node (RuboCop::AST::Node)
  • key (Symbol)

    e.g. ‘:user`

Returns:

  • (Array<RuboCop::AST::Node>)

    matching nodes



143
144
145
146
147
148
149
# File 'lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb', line 143

def_node_matcher :belongs_to_without_fk?, <<~PATTERN
  {
    (send nil? :belongs_to (sym %1))        # belongs_to :user
    (send nil? :belongs_to (sym %1) !hash ...)  # belongs_to :user, -> { not_deleted }
    (send nil? :belongs_to (sym %1) !(hash <(pair (sym :foreign_key) _) ...>))
  }
PATTERN

#on_send(node) ⇒ Object



164
165
166
167
168
169
170
171
# File 'lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb', line 164

def on_send(node)
  presence_validation?(node) do |all_keys, options, presence|
    keys = non_optional_belongs_to(node.parent, all_keys)
    return if keys.none?

    add_offense_and_correct(node, all_keys, keys, options, presence)
  end
end

#optional?(node) ⇒ Object

Match a ‘belongs_to` association with an optional option in a hash



74
75
76
# File 'lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb', line 74

def_node_matcher :optional?, <<~PATTERN
  (send nil? :belongs_to _ ... #optional_option?)
PATTERN

#optional_option?(node) ⇒ Object

Match an optional option in a hash



80
81
82
83
84
85
# File 'lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb', line 80

def_node_matcher :optional_option?, <<~PATTERN
  {
    (hash <(pair (sym :optional) true) ...>)   # optional: true
    (hash <(pair (sym :required) false) ...>)  # required: false
  }
PATTERN

#presence_validation?(node) ⇒ Object

Match a ‘validates` statement with a presence check

Examples:

source that matches - by association

validates :user, presence: true

source that matches - by association

validates :name, :user, presence: true

source that matches - by a foreign key

validates :user_id, presence: true

source that DOES NOT match - strict validation

validates :user_id, presence: true, strict: true

source that DOES NOT match - custom strict validation

validates :user_id, presence: true, strict: MissingUserError


61
62
63
64
65
66
67
68
69
70
# File 'lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb', line 61

def_node_matcher :presence_validation?, <<~PATTERN
  (
    send nil? :validates
    (sym $_)+
    $[
      (hash <$(pair (sym :presence) true) ...>)         # presence: true
      !(hash <$(pair (sym :strict) {true const}) ...>)  # strict: true
    ]
  )
PATTERN