rails_best_practices 1.10.1 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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)
|