rails_best_practices 1.10.1 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +11 -6
- data/assets/result.html.erb +76 -46
- data/install_supported_rubies.sh +3 -0
- data/lib/rails_best_practices.rb +2 -1
- data/lib/rails_best_practices/analyzer.rb +10 -8
- data/lib/rails_best_practices/core.rb +0 -4
- data/lib/rails_best_practices/core/check.rb +41 -117
- data/lib/rails_best_practices/core/error.rb +3 -9
- data/lib/rails_best_practices/core/runner.rb +20 -80
- data/lib/rails_best_practices/lexicals/long_line_check.rb +2 -1
- data/lib/rails_best_practices/lexicals/remove_tab_check.rb +1 -3
- data/lib/rails_best_practices/lexicals/remove_trailing_whitespace_check.rb +1 -3
- data/lib/rails_best_practices/prepares/config_prepare.rb +1 -1
- data/lib/rails_best_practices/prepares/controller_prepare.rb +7 -8
- data/lib/rails_best_practices/prepares/helper_prepare.rb +2 -2
- data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -1
- data/lib/rails_best_practices/prepares/model_prepare.rb +6 -7
- data/lib/rails_best_practices/prepares/route_prepare.rb +12 -13
- data/lib/rails_best_practices/prepares/schema_prepare.rb +2 -2
- data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +19 -15
- data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +10 -17
- data/lib/rails_best_practices/reviews/dry_bundler_in_capistrano_review.rb +2 -5
- data/lib/rails_best_practices/reviews/hash_syntax_review.rb +3 -30
- data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +7 -10
- data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +5 -9
- data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +10 -13
- data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +6 -9
- data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +2 -5
- data/lib/rails_best_practices/reviews/move_code_into_model_review.rb +7 -13
- data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +3 -6
- data/lib/rails_best_practices/reviews/move_model_logic_into_model_review.rb +6 -9
- data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +3 -6
- data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +4 -7
- data/lib/rails_best_practices/reviews/not_use_time_ago_in_words_review.rb +2 -5
- data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +7 -10
- data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +2 -5
- data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +2 -5
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +8 -6
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +1 -2
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +4 -5
- data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +9 -12
- data/lib/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review.rb +2 -13
- data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +18 -26
- data/lib/rails_best_practices/reviews/review.rb +6 -7
- data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +2 -5
- data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +2 -5
- data/lib/rails_best_practices/reviews/use_before_filter_review.rb +2 -5
- data/lib/rails_best_practices/reviews/use_model_association_review.rb +11 -14
- data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +11 -8
- data/lib/rails_best_practices/reviews/use_observer_review.rb +8 -11
- data/lib/rails_best_practices/reviews/use_parentheses_in_method_def_review.rb +1 -1
- data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +12 -18
- data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +7 -10
- data/lib/rails_best_practices/reviews/use_scope_access_review.rb +4 -10
- data/lib/rails_best_practices/version.rb +1 -1
- data/rails_best_practices.gemspec +1 -1
- data/rails_best_practices.yml +5 -5
- data/spec/rails_best_practices/core/check_spec.rb +0 -67
- data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +0 -1
- data/spec/rails_best_practices/prepares/model_prepare_spec.rb +0 -4
- data/spec/rails_best_practices/reviews/hash_syntax_review_spec.rb +3 -30
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +22 -0
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +19 -0
- data/spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -4
- metadata +28 -41
- data/Gemfile.lock +0 -71
- data/lib/rails_best_practices/core/checking_visitor.rb +0 -80
- data/lib/rails_best_practices/core/nil.rb +0 -37
- data/lib/rails_best_practices/core_ext/enumerable.rb +0 -9
- data/lib/rails_best_practices/core_ext/sexp.rb +0 -840
- data/spec/rails_best_practices/core/checking_visitor_spec.rb +0 -79
- data/spec/rails_best_practices/core/nil_spec.rb +0 -37
- data/spec/rails_best_practices/core_ext/enumerable_spec.rb +0 -7
- data/spec/rails_best_practices/core_ext/sexp_spec.rb +0 -613
@@ -17,22 +17,19 @@ module RailsBestPractices
|
|
17
17
|
class RestrictAutoGeneratedRoutesReview < Review
|
18
18
|
interesting_nodes :command, :command_call, :method_add_block
|
19
19
|
interesting_files ROUTE_FILES
|
20
|
+
url "http://rails-bestpractices.com/posts/86-restrict-auto-generated-routes"
|
20
21
|
|
21
22
|
RESOURCE_METHODS = ["show", "new", "create", "edit", "update", "destroy"]
|
22
23
|
RESOURCES_METHODS = RESOURCE_METHODS + ["index"]
|
23
24
|
|
24
|
-
def url
|
25
|
-
"http://rails-bestpractices.com/posts/86-restrict-auto-generated-routes"
|
26
|
-
end
|
27
|
-
|
28
25
|
def initialize
|
29
26
|
super
|
30
27
|
@namespaces = []
|
31
28
|
@resource_controllers = []
|
32
29
|
end
|
33
30
|
|
34
|
-
# check if the generated routes have the corresponding actions in controller for
|
35
|
-
|
31
|
+
# check if the generated routes have the corresponding actions in controller for rails routes.
|
32
|
+
add_callback :start_command, :start_command_call do |node|
|
36
33
|
if "resources" == node.message.to_s
|
37
34
|
check_resources(node)
|
38
35
|
@resource_controllers << node.arguments.all.first.to_s
|
@@ -42,7 +39,7 @@ module RailsBestPractices
|
|
42
39
|
end
|
43
40
|
end
|
44
41
|
|
45
|
-
|
42
|
+
add_callback :end_command do |node|
|
46
43
|
if "resources" == node.message.to_s
|
47
44
|
@resource_controllers.pop
|
48
45
|
elsif "resource" == node.message.to_s
|
@@ -51,37 +48,32 @@ module RailsBestPractices
|
|
51
48
|
end
|
52
49
|
|
53
50
|
# remember the namespace.
|
54
|
-
|
51
|
+
add_callback :start_method_add_block do |node|
|
55
52
|
case node.message.to_s
|
56
53
|
when "namespace"
|
57
|
-
|
58
|
-
@namespaces << node.arguments.all.first.to_s
|
54
|
+
@namespaces << node.arguments.all.first.to_s if check_method_add_block?(node)
|
59
55
|
when "resources", "resource"
|
60
|
-
|
61
|
-
@resource_controllers << node.arguments.all.first.to_s
|
56
|
+
@resource_controllers << node.arguments.all.first.to_s if check_method_add_block?(node)
|
62
57
|
else
|
63
58
|
end
|
64
59
|
end
|
65
60
|
|
66
61
|
# end of namespace call.
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
62
|
+
add_callback :end_method_add_block do |node|
|
63
|
+
if check_method_add_block?(node)
|
64
|
+
case node.message.to_s
|
65
|
+
when "namespace"
|
66
|
+
@namespaces.pop
|
67
|
+
when "resources", "resource"
|
68
|
+
@resource_controllers.pop
|
69
|
+
end
|
75
70
|
end
|
76
71
|
end
|
77
72
|
|
78
73
|
def check_method_add_block?(node)
|
79
|
-
:command == node[1].sexp_type || (:command_call == node[1].sexp_type && "map" != node.
|
74
|
+
:command == node[1].sexp_type || (:command_call == node[1].sexp_type && "map" != node.receiver.to_s)
|
80
75
|
end
|
81
76
|
|
82
|
-
# check if the generated routes have the corresponding actions in controller for rails2 routes.
|
83
|
-
alias_method :start_command_call, :start_command
|
84
|
-
|
85
77
|
private
|
86
78
|
# check resources call, if the routes generated by resources does not exist in the controller.
|
87
79
|
def check_resources(node)
|
@@ -143,7 +135,7 @@ module RailsBestPractices
|
|
143
135
|
elsif hash_key_exist?(option_node, "except")
|
144
136
|
if option_node.hash_value("except").to_s == "all"
|
145
137
|
[]
|
146
|
-
else
|
138
|
+
else
|
147
139
|
(methods - Array(option_node.hash_value("except").to_object))
|
148
140
|
end
|
149
141
|
else
|
@@ -165,7 +157,7 @@ module RailsBestPractices
|
|
165
157
|
elsif hash_key_exist?(option_node, "except")
|
166
158
|
if option_node.hash_value("except").to_s == "all"
|
167
159
|
[]
|
168
|
-
else
|
160
|
+
else
|
169
161
|
(methods - Array(option_node.hash_value("except").to_object))
|
170
162
|
end
|
171
163
|
else
|
@@ -7,9 +7,8 @@ module RailsBestPractices
|
|
7
7
|
# A Review class that takes charge of reviewing one rails best practice.
|
8
8
|
class Review < Core::Check
|
9
9
|
# default url.
|
10
|
-
|
11
|
-
|
12
|
-
end
|
10
|
+
url "#"
|
11
|
+
|
13
12
|
# remember use count for the variable in the call or assign node.
|
14
13
|
#
|
15
14
|
# find the variable in the call or assign node,
|
@@ -35,11 +34,11 @@ module RailsBestPractices
|
|
35
34
|
|
36
35
|
# find variable in the call or field node.
|
37
36
|
def variable(node)
|
38
|
-
while [:call, :field, :method_add_arg, :method_add_block].include?(node.
|
39
|
-
node = node.
|
37
|
+
while [:call, :field, :method_add_arg, :method_add_block].include?(node.receiver.sexp_type)
|
38
|
+
node = node.receiver
|
40
39
|
end
|
41
|
-
return if [:fcall, :hash].include?(node.
|
42
|
-
node.
|
40
|
+
return if [:fcall, :hash].include?(node.receiver.sexp_type)
|
41
|
+
node.receiver
|
43
42
|
end
|
44
43
|
|
45
44
|
# get the models from Prepares.
|
@@ -16,15 +16,12 @@ module RailsBestPractices
|
|
16
16
|
class SimplifyRenderInControllersReview < Review
|
17
17
|
interesting_nodes :command
|
18
18
|
interesting_files CONTROLLER_FILES
|
19
|
-
|
20
|
-
def url
|
21
|
-
"http://rails-bestpractices.com/posts/62-simplify-render-in-controllers"
|
22
|
-
end
|
19
|
+
url "http://rails-bestpractices.com/posts/62-simplify-render-in-controllers"
|
23
20
|
|
24
21
|
# check command node in the controller file,
|
25
22
|
# if its message is render and the arguments contain a key action, template or file,
|
26
23
|
# then it should be replaced by simplified syntax.
|
27
|
-
|
24
|
+
add_callback :start_command do |node|
|
28
25
|
if "render" == node.message.to_s
|
29
26
|
keys = node.arguments.all.first.hash_keys
|
30
27
|
if keys && keys.size == 1 &&
|
@@ -15,17 +15,14 @@ module RailsBestPractices
|
|
15
15
|
class SimplifyRenderInViewsReview < Review
|
16
16
|
interesting_nodes :command
|
17
17
|
interesting_files VIEW_FILES
|
18
|
+
url "http://rails-bestpractices.com/posts/61-simplify-render-in-views"
|
18
19
|
|
19
20
|
VALID_KEYS = %w(object collection locals)
|
20
21
|
|
21
|
-
def url
|
22
|
-
"http://rails-bestpractices.com/posts/61-simplify-render-in-views"
|
23
|
-
end
|
24
|
-
|
25
22
|
# check command node in view file,
|
26
23
|
# if its message is render and the arguments contain a key partial,
|
27
24
|
# then it should be replaced by simplified syntax.
|
28
|
-
|
25
|
+
add_callback :start_command do |node|
|
29
26
|
if "render" == node.message.to_s
|
30
27
|
hash_node = node.arguments.all.first
|
31
28
|
if hash_node && :bare_assoc_hash == hash_node.sexp_type &&
|
@@ -16,10 +16,7 @@ module RailsBestPractices
|
|
16
16
|
class UseBeforeFilterReview < Review
|
17
17
|
interesting_nodes :class
|
18
18
|
interesting_files CONTROLLER_FILES
|
19
|
-
|
20
|
-
def url
|
21
|
-
"http://rails-bestpractices.com/posts/22-use-before_filter"
|
22
|
-
end
|
19
|
+
url "http://rails-bestpractices.com/posts/22-use-before_filter"
|
23
20
|
|
24
21
|
def initialize(options = {})
|
25
22
|
super()
|
@@ -31,7 +28,7 @@ module RailsBestPractices
|
|
31
28
|
# it will check every def nodes in the class node until protected or private identification,
|
32
29
|
# if there are defn nodes who have the same first code line,
|
33
30
|
# then these duplicated first code lines should be moved to before_filter.
|
34
|
-
|
31
|
+
add_callback :start_class do |node|
|
35
32
|
@first_sentences = {}
|
36
33
|
|
37
34
|
node.body.statements.each do |statement_node|
|
@@ -13,26 +13,23 @@ module RailsBestPractices
|
|
13
13
|
# check model define nodes in all controller files,
|
14
14
|
# if there is an attribute assignment node with message xxx_id=,
|
15
15
|
# and after it, there is a call node with message "save" or "save!",
|
16
|
-
# and the
|
16
|
+
# and the receivers of attribute assignment node and call node are the same,
|
17
17
|
# then model association should be used instead of xxx_id assignment.
|
18
18
|
class UseModelAssociationReview < Review
|
19
19
|
interesting_nodes :def
|
20
20
|
interesting_files CONTROLLER_FILES
|
21
|
-
|
22
|
-
def url
|
23
|
-
"http://rails-bestpractices.com/posts/2-use-model-association"
|
24
|
-
end
|
21
|
+
url "http://rails-bestpractices.com/posts/2-use-model-association"
|
25
22
|
|
26
23
|
# check method define nodes to see if there are some attribute assignments that can use model association instead.
|
27
24
|
#
|
28
25
|
# it will check attribute assignment node with message xxx_id=, and call node with message "save" or "save!"
|
29
26
|
#
|
30
27
|
# 1. if there is an attribute assignment node with message xxx_id=,
|
31
|
-
# then remember the
|
28
|
+
# then remember the receiver of attribute assignment node.
|
32
29
|
# 2. after assignment, if there is a call node with message "save" or "save!",
|
33
|
-
# and the
|
30
|
+
# and the receiver of call node is one of the receiver of attribute assignment node,
|
34
31
|
# then the attribute assignment should be replaced by using model association.
|
35
|
-
|
32
|
+
add_callback :start_def do |node|
|
36
33
|
@assignments = {}
|
37
34
|
node.recursive_children do |child|
|
38
35
|
case child.sexp_type
|
@@ -48,21 +45,21 @@ module RailsBestPractices
|
|
48
45
|
|
49
46
|
private
|
50
47
|
# check an attribute assignment node, if its message is xxx_id,
|
51
|
-
# then remember the
|
48
|
+
# then remember the receiver of the attribute assignment in @assignments.
|
52
49
|
def attribute_assignment(node)
|
53
50
|
if node.left_value.message.to_s =~ /_id$/
|
54
|
-
|
55
|
-
@assignments[
|
51
|
+
receiver = node.left_value.receiver.to_s
|
52
|
+
@assignments[receiver] = true
|
56
53
|
end
|
57
54
|
end
|
58
55
|
|
59
56
|
# check a call node with message "save" or "save!",
|
60
|
-
# if the
|
57
|
+
# if the receiver of call node exists in @assignments,
|
61
58
|
# then the attribute assignment should be replaced by using model association.
|
62
59
|
def call_assignment(node)
|
63
60
|
if ["save", "save!"].include? node.message.to_s
|
64
|
-
|
65
|
-
add_error "use model association (for #{
|
61
|
+
receiver = node.receiver.to_s
|
62
|
+
add_error "use model association (for #{receiver})" if @assignments[receiver]
|
66
63
|
end
|
67
64
|
end
|
68
65
|
end
|
data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb
CHANGED
@@ -17,26 +17,29 @@ module RailsBestPractices
|
|
17
17
|
class UseMultipartAlternativeAsContentTypeOfEmailReview < Review
|
18
18
|
interesting_nodes :class, :def
|
19
19
|
interesting_files MAILER_FILES
|
20
|
-
|
21
|
-
def url
|
22
|
-
"http://rails-bestpractices.com/posts/41-use-multipart-alternative-as-content_type-of-email"
|
23
|
-
end
|
20
|
+
url "http://rails-bestpractices.com/posts/41-use-multipart-alternative-as-content_type-of-email"
|
24
21
|
|
25
22
|
# check class node to remember the ActionMailer class name.
|
26
|
-
|
23
|
+
add_callback :start_class do |node|
|
27
24
|
@klazz_name = node.class_name.to_s
|
28
25
|
end
|
29
26
|
|
30
27
|
# check def node and find if the corresponding views exist or not?
|
31
|
-
|
28
|
+
add_callback :start_def do |node|
|
32
29
|
name = node.method_name.to_s
|
33
|
-
|
34
|
-
if rails2_canonical_mailer_views?(name) || rails3_canonical_mailer_views?(name)
|
30
|
+
if deliver_method?(name) && rails_canonical_mailer_views?(name)
|
35
31
|
add_error("use multipart/alternative as content_type of email")
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
39
35
|
private
|
36
|
+
# check if rails's syntax mailer views are canonical.
|
37
|
+
#
|
38
|
+
# @param [String] name method name in action_mailer
|
39
|
+
def rails_canonical_mailer_views?(name)
|
40
|
+
rails2_canonical_mailer_views?(name) || rails3_canonical_mailer_views?(name)
|
41
|
+
end
|
42
|
+
|
40
43
|
# check if rails2's syntax mailer views are canonical.
|
41
44
|
#
|
42
45
|
# @param [String] name method name in action_mailer
|
@@ -22,10 +22,7 @@ module RailsBestPractices
|
|
22
22
|
class UseObserverReview < Review
|
23
23
|
interesting_nodes :def, :command
|
24
24
|
interesting_files MODEL_FILES
|
25
|
-
|
26
|
-
def url
|
27
|
-
"http://rails-bestpractices.com/posts/19-use-observer"
|
28
|
-
end
|
25
|
+
url "http://rails-bestpractices.com/posts/19-use-observer"
|
29
26
|
|
30
27
|
def initialize
|
31
28
|
super
|
@@ -36,7 +33,7 @@ module RailsBestPractices
|
|
36
33
|
#
|
37
34
|
# if it is a callback definition,
|
38
35
|
# then remember its callback methods.
|
39
|
-
|
36
|
+
add_callback :start_command do |node|
|
40
37
|
remember_callback(node)
|
41
38
|
end
|
42
39
|
|
@@ -45,7 +42,7 @@ module RailsBestPractices
|
|
45
42
|
# if it is callback method,
|
46
43
|
# and there is a actionmailer deliver call in the method define node,
|
47
44
|
# then it should be replaced by using observer.
|
48
|
-
|
45
|
+
add_callback :start_def do |node|
|
49
46
|
if callback_method?(node) && deliver_mailer?(node)
|
50
47
|
add_error "use observer"
|
51
48
|
end
|
@@ -73,22 +70,22 @@ module RailsBestPractices
|
|
73
70
|
# for rails2
|
74
71
|
#
|
75
72
|
# if the message of call node is deliver_xxx,
|
76
|
-
# and the
|
73
|
+
# and the receiver of the call node exists in @callbacks,
|
77
74
|
#
|
78
75
|
# for rails3
|
79
76
|
#
|
80
77
|
# if the message of call node is deliver,
|
81
|
-
# and the
|
78
|
+
# and the receiver of the call node is with receiver node who exists in @callbacks,
|
82
79
|
#
|
83
80
|
# then the call node is actionmailer deliver call.
|
84
81
|
def deliver_mailer?(node)
|
85
82
|
node.grep_nodes(sexp_type: :call) do |child_node|
|
86
83
|
# rails2 actionmailer deliver
|
87
|
-
return true if child_node.message.to_s =~ /^deliver_/ && mailers.include?(child_node.
|
84
|
+
return true if child_node.message.to_s =~ /^deliver_/ && mailers.include?(child_node.receiver.to_s)
|
88
85
|
# rails3 actionmailer deliver
|
89
86
|
if "deliver" == child_node.message.to_s
|
90
|
-
if :method_add_arg == child_node.
|
91
|
-
mailers.include?(child_node.
|
87
|
+
if :method_add_arg == child_node.receiver.sexp_type &&
|
88
|
+
mailers.include?(child_node.receiver[1].receiver.to_s)
|
92
89
|
return true
|
93
90
|
end
|
94
91
|
end
|
@@ -14,7 +14,7 @@ module RailsBestPractices
|
|
14
14
|
interesting_files ALL_FILES
|
15
15
|
|
16
16
|
# check def node to see if parameters are wrapped by parentheses.
|
17
|
-
|
17
|
+
add_callback :start_def do |node|
|
18
18
|
if no_parentheses_around_parameters?(node) && has_parameters?(node)
|
19
19
|
add_error("use parentheses around parameters in method definitions")
|
20
20
|
end
|
@@ -11,33 +11,30 @@ module RailsBestPractices
|
|
11
11
|
#
|
12
12
|
# Review process:
|
13
13
|
# check all method calls within conditional statements, like @user.login.nil?
|
14
|
-
# if their
|
14
|
+
# if their receivers are one of the model names
|
15
15
|
# and their messages of first call are not pluralize and not in any of the association names
|
16
16
|
# and their messages of second call are one of nil?, blank?, present?, or they are == ""
|
17
17
|
# then you can use query attribute instead.
|
18
18
|
class UseQueryAttributeReview < Review
|
19
19
|
interesting_nodes :if, :unless, :elsif
|
20
20
|
interesting_files ALL_FILES
|
21
|
+
url "http://rails-bestpractices.com/posts/56-use-query-attribute"
|
21
22
|
|
22
23
|
QUERY_METHODS = %w(nil? blank? present?)
|
23
24
|
|
24
|
-
def url
|
25
|
-
"http://rails-bestpractices.com/posts/56-use-query-attribute"
|
26
|
-
end
|
27
|
-
|
28
25
|
# check if node to see whose conditional statement nodes contain nodes that can use query attribute instead.
|
29
26
|
#
|
30
27
|
# it will check every call nodes in the if nodes. If the call node is
|
31
28
|
#
|
32
29
|
# 1. two method calls, like @user.login.nil?
|
33
|
-
# 2. the
|
30
|
+
# 2. the receiver is one of the model names
|
34
31
|
# 3. the message of first call is the model's attribute,
|
35
32
|
# the message is not in any of associations name and is not pluralize
|
36
33
|
# 4. the message of second call is one of nil?, blank? or present? or
|
37
34
|
# the message is == and the argument is ""
|
38
35
|
#
|
39
36
|
# then the call node can use query attribute instead.
|
40
|
-
|
37
|
+
add_callback :start_if, :start_unless, :start_elsif do |node|
|
41
38
|
all_conditions = if node.conditional_statement == node.conditional_statement.all_conditions
|
42
39
|
[node.conditional_statement]
|
43
40
|
else
|
@@ -45,17 +42,14 @@ module RailsBestPractices
|
|
45
42
|
end
|
46
43
|
all_conditions.each do |condition_node|
|
47
44
|
if query_attribute_node = query_attribute_node(condition_node)
|
48
|
-
|
49
|
-
add_error "use query attribute (#{
|
45
|
+
receiver_node = query_attribute_node.receiver
|
46
|
+
add_error "use query attribute (#{receiver_node.receiver}.#{receiver_node.message}?)",
|
50
47
|
node.file,
|
51
48
|
query_attribute_node.line
|
52
49
|
end
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
56
|
-
alias_method :start_unless, :start_if
|
57
|
-
alias_method :start_elsif, :start_if
|
58
|
-
|
59
53
|
private
|
60
54
|
# recursively check conditional statement nodes to see if there is a call node that may be
|
61
55
|
# possible query attribute.
|
@@ -81,7 +75,7 @@ module RailsBestPractices
|
|
81
75
|
#
|
82
76
|
# if the node contains two method calls, e.g. @user.login.nil?
|
83
77
|
#
|
84
|
-
# for the first call, the
|
78
|
+
# for the first call, the receiver should be one of the class names and
|
85
79
|
# the message should be one of the attribute name.
|
86
80
|
#
|
87
81
|
# for the second call, the message should be one of nil?, blank? or present? or
|
@@ -89,23 +83,23 @@ module RailsBestPractices
|
|
89
83
|
#
|
90
84
|
# the node that may use query attribute.
|
91
85
|
def possible_query_attribute?(node)
|
92
|
-
return false unless :call == node.
|
86
|
+
return false unless :call == node.receiver.sexp_type
|
93
87
|
variable_node = variable(node)
|
94
|
-
message_node = node.grep_node(
|
88
|
+
message_node = node.grep_node(receiver: variable_node.to_s).message
|
95
89
|
|
96
90
|
is_model?(variable_node) && model_attribute?(variable_node, message_node.to_s) &&
|
97
91
|
(QUERY_METHODS.include?(node.message.to_s) || compare_with_empty_string?(node))
|
98
92
|
end
|
99
93
|
|
100
|
-
# check if the
|
94
|
+
# check if the receiver is one of the models.
|
101
95
|
def is_model?(variable_node)
|
102
96
|
return false if variable_node.const?
|
103
97
|
class_name = variable_node.to_s.sub(/^@/, '').classify
|
104
98
|
models.include?(class_name)
|
105
99
|
end
|
106
100
|
|
107
|
-
# check if the
|
108
|
-
# the
|
101
|
+
# check if the receiver and message is one of the model's attribute.
|
102
|
+
# the receiver should match one of the class model name, and the message should match one of attribute name.
|
109
103
|
def model_attribute?(variable_node, message)
|
110
104
|
class_name = variable_node.to_s.sub(/^@/, '').classify
|
111
105
|
attribute_type = model_attributes.get_attribute_type(class_name, message)
|