forked from rubocop/rubocop-rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dynamic_find_by.rb
91 lines (75 loc) · 2.46 KB
/
dynamic_find_by.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
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# This cop checks dynamic `find_by_*` methods.
# Use `find_by` instead of dynamic method.
# See. https://rails.rubystyle.guide#find_by
#
# @example
# # bad
# User.find_by_name(name)
#
# # bad
# User.find_by_name_and_email(name)
#
# # bad
# User.find_by_email!(name)
#
# # good
# User.find_by(name: name)
#
# # good
# User.find_by(name: name, email: email)
#
# # good
# User.find_by!(email: email)
class DynamicFindBy < Cop
MSG = 'Use `%<static_name>s` instead of dynamic `%<method>s`.'
METHOD_PATTERN = /^find_by_(.+?)(!)?$/.freeze
def on_send(node)
method_name = node.method_name.to_s
return if whitelist.include?(method_name)
static_name = static_method_name(method_name)
return unless static_name
add_offense(node,
message: format(MSG, static_name: static_name,
method: node.method_name))
end
alias on_csend on_send
def autocorrect(node)
keywords = column_keywords(node.method_name)
return if keywords.size != node.arguments.size
lambda do |corrector|
autocorrect_method_name(corrector, node)
autocorrect_argument_keywords(corrector, node, keywords)
end
end
private
def autocorrect_method_name(corrector, node)
corrector.replace(node.loc.selector,
static_method_name(node.method_name.to_s))
end
def autocorrect_argument_keywords(corrector, node, keywords)
keywords.each.with_index do |keyword, idx|
corrector.insert_before(node.arguments[idx].loc.expression, keyword)
end
end
def whitelist
cop_config['Whitelist']
end
def column_keywords(method)
keyword_string = method.to_s[METHOD_PATTERN, 1]
keyword_string.split('_and_').map { |keyword| "#{keyword}: " }
end
# Returns static method name.
# If code isn't wrong, returns nil
def static_method_name(method_name)
match = METHOD_PATTERN.match(method_name)
return nil unless match
match[2] ? 'find_by!' : 'find_by'
end
end
end
end
end