forked from rubocop/rubocop-rails
/
has_many_or_has_one_dependent.rb
103 lines (82 loc) · 2.86 KB
/
has_many_or_has_one_dependent.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# This cop looks for `has_many` or `has_one` associations that don't
# specify a `:dependent` option.
# It doesn't register an offense if `:through` option was specified.
#
# @example
# # bad
# class User < ActiveRecord::Base
# has_many :comments
# has_one :avatar
# end
#
# # good
# class User < ActiveRecord::Base
# has_many :comments, dependent: :restrict_with_exception
# has_one :avatar, dependent: :destroy
# has_many :patients, through: :appointments
# end
class HasManyOrHasOneDependent < Cop
MSG = 'Specify a `:dependent` option.'
RESTRICT_ON_SEND = %i[has_many has_one].freeze
def_node_search :active_resource_class?, <<~PATTERN
(const (const nil? :ActiveResource) :Base)
PATTERN
def_node_matcher :association_without_options?, <<~PATTERN
(send nil? {:has_many :has_one} _)
PATTERN
def_node_matcher :association_with_options?, <<~PATTERN
(send nil? {:has_many :has_one} _ (hash $...))
PATTERN
def_node_matcher :dependent_option?, <<~PATTERN
(pair (sym :dependent) !nil)
PATTERN
def_node_matcher :present_option?, <<~PATTERN
(pair (sym :through) !nil)
PATTERN
def_node_matcher :with_options_block, <<~PATTERN
(block
(send nil? :with_options
(hash $...))
(args) ...)
PATTERN
def on_send(node)
return if active_resource?(node.parent)
return if !association_without_options?(node) && valid_options?(association_with_options?(node))
return if valid_options_in_with_options_block?(node)
add_offense(node, location: :selector)
end
private
def valid_options_in_with_options_block?(node)
return true unless node.parent
n = node.parent.begin_type? ? node.parent.parent : node.parent
contain_valid_options_in_with_options_block?(n)
end
def contain_valid_options_in_with_options_block?(node)
if with_options_block(node)
return true if valid_options?(with_options_block(node))
return false unless node.parent
return true if contain_valid_options_in_with_options_block?(
node.parent.parent
)
end
false
end
def valid_options?(options)
return true unless options
return true if options.any? do |o|
dependent_option?(o) || present_option?(o)
end
false
end
def active_resource?(node)
return false if node.nil?
active_resource_class?(node)
end
end
end
end
end