forked from rubocop/rubocop-rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvalidation.rb
148 lines (124 loc) · 4.27 KB
/
validation.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# This cop checks for the use of old-style attribute validation macros.
#
# @example
# # bad
# validates_acceptance_of :foo
# validates_confirmation_of :foo
# validates_exclusion_of :foo
# validates_format_of :foo
# validates_inclusion_of :foo
# validates_length_of :foo
# validates_numericality_of :foo
# validates_presence_of :foo
# validates_absence_of :foo
# validates_size_of :foo
# validates_uniqueness_of :foo
#
# # good
# validates :foo, acceptance: true
# validates :foo, confirmation: true
# validates :foo, exclusion: true
# validates :foo, format: true
# validates :foo, inclusion: true
# validates :foo, length: true
# validates :foo, numericality: true
# validates :foo, presence: true
# validates :foo, absence: true
# validates :foo, size: true
# validates :foo, uniqueness: true
#
class Validation < Base
extend AutoCorrector
MSG = 'Prefer the new style validations `%<prefer>s` over ' \
'`%<current>s`.'
TYPES = %w[
acceptance
confirmation
exclusion
format
inclusion
length
numericality
presence
absence
size
uniqueness
].freeze
RESTRICT_ON_SEND = TYPES.map { |p| "validates_#{p}_of".to_sym }.freeze
ALLOWLIST = TYPES.map { |p| "validates :column, #{p}: value" }.freeze
def on_send(node)
return if node.receiver
range = node.loc.selector
add_offense(range, message: message(node)) do |corrector|
last_argument = node.arguments.last
return if !last_argument.literal? && !last_argument.splat_type? &&
!frozen_array_argument?(last_argument)
corrector.replace(range, 'validates')
correct_validate_type(corrector, node)
end
end
private
def message(node)
method_name = node.method_name
format(MSG, prefer: preferred_method(method_name), current: method_name)
end
def preferred_method(method)
ALLOWLIST[RESTRICT_ON_SEND.index(method.to_sym)]
end
def correct_validate_type(corrector, node)
last_argument = node.last_argument
if last_argument.hash_type?
correct_validate_type_for_hash(corrector, node, last_argument)
elsif last_argument.array_type?
loc = last_argument.loc
correct_validate_type_for_array(corrector, node, last_argument, loc)
elsif frozen_array_argument?(last_argument)
arguments = node.last_argument.receiver
loc = arguments.parent.loc
correct_validate_type_for_array(corrector, node, arguments, loc)
else
range = last_argument.source_range
corrector.insert_after(range, ", #{validate_type(node)}: true")
end
end
def correct_validate_type_for_hash(corrector, node, arguments)
corrector.replace(
arguments.loc.expression,
"#{validate_type(node)}: #{braced_options(arguments)}"
)
end
def correct_validate_type_for_array(corrector, node, arguments, loc)
attributes = []
arguments.each_child_node do |child_node|
attributes << if arguments.percent_literal?
":#{child_node.source}"
else
child_node.source
end
end
corrector.replace(
loc.expression,
"#{attributes.join(', ')}, #{validate_type(node)}: true"
)
end
def validate_type(node)
node.method_name.to_s.split('_')[1]
end
def frozen_array_argument?(argument)
argument.send_type? && argument.method?(:freeze)
end
def braced_options(options)
if options.braces?
options.source
else
"{ #{options.source} }"
end
end
end
end
end
end