rails_best_practices 1.20.0 → 1.22.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +49 -43
- data/Guardfile +2 -0
- data/Rakefile +2 -0
- data/assets/result.html.erb +2 -0
- data/lib/rails_best_practices/analyzer.rb +59 -48
- data/lib/rails_best_practices/core/check.rb +39 -32
- data/lib/rails_best_practices/core/checks_loader.rb +8 -6
- data/lib/rails_best_practices/core/configs.rb +1 -2
- data/lib/rails_best_practices/core/controllers.rb +1 -2
- data/lib/rails_best_practices/core/error.rb +1 -1
- data/lib/rails_best_practices/core/helpers.rb +1 -2
- data/lib/rails_best_practices/core/mailers.rb +1 -2
- data/lib/rails_best_practices/core/methods.rb +21 -16
- data/lib/rails_best_practices/core/model_associations.rb +9 -4
- data/lib/rails_best_practices/core/models.rb +1 -2
- data/lib/rails_best_practices/core/modules.rb +1 -1
- data/lib/rails_best_practices/core/routes.rb +2 -2
- data/lib/rails_best_practices/core/runner.rb +49 -34
- data/lib/rails_best_practices/inline_disables/comment_ripper.rb +19 -0
- data/lib/rails_best_practices/inline_disables/inline_disable.rb +50 -0
- data/lib/rails_best_practices/inline_disables.rb +3 -0
- data/lib/rails_best_practices/lexicals/long_line_check.rb +7 -3
- data/lib/rails_best_practices/option_parser.rb +22 -6
- data/lib/rails_best_practices/prepares/controller_prepare.rb +15 -3
- data/lib/rails_best_practices/prepares/gemfile_prepare.rb +1 -1
- data/lib/rails_best_practices/prepares/helper_prepare.rb +6 -1
- data/lib/rails_best_practices/prepares/initializer_prepare.rb +2 -2
- data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -0
- data/lib/rails_best_practices/prepares/model_prepare.rb +52 -12
- data/lib/rails_best_practices/prepares/route_prepare.rb +16 -10
- data/lib/rails_best_practices/prepares.rb +1 -1
- data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +15 -13
- data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +34 -29
- data/lib/rails_best_practices/reviews/check_destroy_return_value_review.rb +14 -5
- data/lib/rails_best_practices/reviews/check_save_return_value_review.rb +19 -8
- data/lib/rails_best_practices/reviews/hash_syntax_review.rb +5 -5
- data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +4 -4
- data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +7 -8
- data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +6 -6
- data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +1 -1
- data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +6 -7
- data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +7 -8
- data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +12 -10
- data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +1 -2
- data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +5 -5
- data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +5 -2
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +6 -3
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +6 -4
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +29 -9
- data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +3 -3
- data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +17 -15
- data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +1 -2
- data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +3 -3
- data/lib/rails_best_practices/reviews/use_before_filter_review.rb +2 -1
- data/lib/rails_best_practices/reviews/use_model_association_review.rb +5 -5
- data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +9 -8
- data/lib/rails_best_practices/reviews/use_observer_review.rb +9 -9
- data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +26 -26
- data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +8 -7
- data/lib/rails_best_practices/reviews/use_scope_access_review.rb +17 -15
- data/lib/rails_best_practices/reviews/use_turbo_sprockets_rails3_review.rb +2 -1
- data/lib/rails_best_practices/version.rb +1 -1
- data/lib/rails_best_practices.rb +2 -2
- data/rails_best_practices.gemspec +39 -38
- data/spec/fixtures/lib/rails_best_practices/plugins/reviews/not_use_rails_root_review.rb +1 -2
- data/spec/rails_best_practices/analyzer_spec.rb +73 -42
- data/spec/rails_best_practices/core/check_spec.rb +5 -5
- data/spec/rails_best_practices/core/checks_loader_spec.rb +3 -3
- data/spec/rails_best_practices/core/configs_spec.rb +1 -1
- data/spec/rails_best_practices/core/controllers_spec.rb +1 -1
- data/spec/rails_best_practices/core/error_spec.rb +21 -21
- data/spec/rails_best_practices/core/except_methods_spec.rb +7 -7
- data/spec/rails_best_practices/core/gems_spec.rb +4 -4
- data/spec/rails_best_practices/core/helpers_spec.rb +1 -1
- data/spec/rails_best_practices/core/klasses_spec.rb +3 -3
- data/spec/rails_best_practices/core/mailers_spec.rb +1 -1
- data/spec/rails_best_practices/core/methods_spec.rb +6 -6
- data/spec/rails_best_practices/core/model_associations_spec.rb +10 -6
- data/spec/rails_best_practices/core/model_attributes_spec.rb +4 -4
- data/spec/rails_best_practices/core/models_spec.rb +1 -1
- data/spec/rails_best_practices/core/modules_spec.rb +5 -5
- data/spec/rails_best_practices/core/routes_spec.rb +5 -5
- data/spec/rails_best_practices/core/runner_spec.rb +9 -7
- data/spec/rails_best_practices/core_ext/erubis_spec.rb +10 -10
- data/spec/rails_best_practices/inline_disables/inline_disable_spec.rb +62 -0
- data/spec/rails_best_practices/lexicals/long_line_check_spec.rb +11 -10
- data/spec/rails_best_practices/lexicals/remove_tab_check_spec.rb +6 -6
- data/spec/rails_best_practices/lexicals/remove_trailing_whitespace_check_spec.rb +6 -6
- data/spec/rails_best_practices/prepares/config_prepare_spec.rb +2 -2
- data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +18 -10
- data/spec/rails_best_practices/prepares/gemfile_prepare_spec.rb +2 -2
- data/spec/rails_best_practices/prepares/helper_prepare_spec.rb +3 -3
- data/spec/rails_best_practices/prepares/initializer_prepare_spec.rb +3 -3
- data/spec/rails_best_practices/prepares/mailer_prepare_spec.rb +2 -2
- data/spec/rails_best_practices/prepares/model_prepare_spec.rb +79 -43
- data/spec/rails_best_practices/prepares/route_prepare_spec.rb +138 -77
- data/spec/rails_best_practices/prepares/schema_prepare_spec.rb +2 -2
- data/spec/rails_best_practices/reviews/add_model_virtual_attribute_review_spec.rb +18 -12
- data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +28 -22
- data/spec/rails_best_practices/reviews/check_destroy_return_value_review_spec.rb +15 -13
- data/spec/rails_best_practices/reviews/check_save_return_value_review_spec.rb +31 -21
- data/spec/rails_best_practices/reviews/default_scope_is_evil_review_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/dry_bundler_in_capistrano_review_spec.rb +5 -5
- data/spec/rails_best_practices/reviews/hash_syntax_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/isolate_seed_data_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/keep_finders_on_their_own_model_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/law_of_demeter_review_spec.rb +21 -14
- data/spec/rails_best_practices/reviews/move_code_into_controller_review_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/move_code_into_helper_review_spec.rb +11 -6
- data/spec/rails_best_practices/reviews/move_code_into_model_review_spec.rb +26 -16
- data/spec/rails_best_practices/reviews/move_finder_to_named_scope_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/move_model_logic_into_model_review_spec.rb +9 -7
- data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/not_rescue_exception_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/not_use_default_route_review_spec.rb +5 -5
- data/spec/rails_best_practices/reviews/not_use_time_ago_in_words_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/protect_mass_assignment_review_spec.rb +24 -19
- data/spec/rails_best_practices/reviews/remove_empty_helpers_review_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +44 -31
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +17 -12
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +46 -44
- data/spec/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review_spec.rb +10 -8
- data/spec/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review_spec.rb +16 -10
- data/spec/rails_best_practices/reviews/restrict_auto_generated_routes_review_spec.rb +54 -31
- data/spec/rails_best_practices/reviews/simplify_render_in_controllers_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/simplify_render_in_views_review_spec.rb +13 -13
- data/spec/rails_best_practices/reviews/use_before_filter_review_spec.rb +11 -9
- data/spec/rails_best_practices/reviews/use_model_association_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb +21 -17
- data/spec/rails_best_practices/reviews/use_observer_review_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/use_parentheses_in_method_def_review_spec.rb +9 -7
- data/spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb +31 -24
- data/spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb +15 -11
- data/spec/rails_best_practices/reviews/use_scope_access_review_spec.rb +14 -14
- data/spec/rails_best_practices/reviews/use_turbo_sprockets_rails3_review_spec.rb +10 -8
- metadata +12 -7
@@ -20,15 +20,17 @@ module RailsBestPractices
|
|
20
20
|
def initialize(options = {})
|
21
21
|
super
|
22
22
|
@helper_methods = Prepares.helper_methods
|
23
|
-
self.class.interesting_files Prepares.helpers.map(&:descendants)
|
23
|
+
self.class.interesting_files *Prepares.helpers.map(&:descendants)
|
24
24
|
end
|
25
25
|
|
26
26
|
# get all unused methods at the end of review process
|
27
27
|
add_callback :after_check do
|
28
28
|
@helper_methods.get_all_unused_methods.each do |method|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
next if excepted?(method)
|
30
|
+
|
31
|
+
add_error "remove unused methods (#{method.class_name}##{method.method_name})",
|
32
|
+
method.file,
|
33
|
+
method.line_number
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
@@ -80,9 +80,11 @@ module RailsBestPractices
|
|
80
80
|
# get all unused methods at the end of review process.
|
81
81
|
add_callback :after_check do
|
82
82
|
@model_methods.get_all_unused_methods.each do |method|
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
next unless !excepted?(method) && method.method_name !~ /=$/
|
84
|
+
|
85
|
+
add_error "remove unused methods (#{method.class_name}##{method.method_name})",
|
86
|
+
method.file,
|
87
|
+
method.line_number
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
@@ -95,13 +97,31 @@ module RailsBestPractices
|
|
95
97
|
def internal_except_methods
|
96
98
|
%w[
|
97
99
|
initialize
|
98
|
-
validate
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
100
|
+
validate
|
101
|
+
validate_each
|
102
|
+
validate_on_create
|
103
|
+
validate_on_update
|
104
|
+
human_attribute_name
|
105
|
+
assign_attributes
|
106
|
+
attributes
|
107
|
+
attribute
|
108
|
+
to_xml
|
109
|
+
to_json
|
110
|
+
as_json
|
111
|
+
to_param
|
112
|
+
before_save
|
113
|
+
before_create
|
114
|
+
before_update
|
115
|
+
before_destroy
|
116
|
+
after_save
|
117
|
+
after_create
|
118
|
+
after_update
|
119
|
+
after_destroy
|
120
|
+
after_find
|
121
|
+
after_initialize
|
103
122
|
method_missing
|
104
|
-
table_name
|
123
|
+
table_name
|
124
|
+
module_prefix
|
105
125
|
].map { |method_name| "*\##{method_name}" }
|
106
126
|
end
|
107
127
|
end
|
data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb
CHANGED
@@ -49,9 +49,9 @@ module RailsBestPractices
|
|
49
49
|
|
50
50
|
private
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
# check the call node to see if it is with message "save" or "save!",
|
53
|
+
# and the count attribute assignment on the receiver of the call node is greater than @assign_count defined,
|
54
|
+
# then it is a complex creation, should be replaced with factory method.
|
55
55
|
def check_variable_save(node)
|
56
56
|
if ['save', 'save!'].include? node.message.to_s
|
57
57
|
variable = node.receiver.to_s
|
@@ -95,32 +95,33 @@ module RailsBestPractices
|
|
95
95
|
|
96
96
|
private
|
97
97
|
|
98
|
-
|
98
|
+
# check resources call, if the routes generated by resources does not exist in the controller.
|
99
99
|
def check_resources(node)
|
100
100
|
_check(node, resources_methods)
|
101
101
|
end
|
102
102
|
|
103
|
-
|
103
|
+
# check resource call, if the routes generated by resources does not exist in the controller.
|
104
104
|
def check_resource(node)
|
105
105
|
_check(node, resource_methods)
|
106
106
|
end
|
107
107
|
|
108
|
-
|
108
|
+
# get the controller name.
|
109
109
|
def controller_name(node)
|
110
110
|
if option_with_hash(node)
|
111
111
|
option_node = node.arguments.all[1]
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
112
|
+
name =
|
113
|
+
if hash_key_exist?(option_node, 'controller')
|
114
|
+
option_node.hash_value('controller').to_s
|
115
|
+
else
|
116
|
+
node.arguments.all.first.to_s.gsub('::', '').tableize
|
117
|
+
end
|
117
118
|
else
|
118
119
|
name = node.arguments.all.first.to_s.gsub('::', '').tableize
|
119
120
|
end
|
120
121
|
namespaced_class_name(name)
|
121
122
|
end
|
122
123
|
|
123
|
-
|
124
|
+
# get the class name with namespace.
|
124
125
|
def namespaced_class_name(name)
|
125
126
|
class_name = "#{name.split('/').map(&:camelize).join('::')}Controller"
|
126
127
|
if @namespaces.empty?
|
@@ -138,11 +139,12 @@ module RailsBestPractices
|
|
138
139
|
unless _methods.all? { |meth| Prepares.controller_methods.has_method?(controller_name, meth) }
|
139
140
|
prepared_method_names = Prepares.controller_methods.get_methods(controller_name).map(&:method_name)
|
140
141
|
only_methods = (_methods & prepared_method_names).map { |meth| ":#{meth}" }
|
141
|
-
routes_message =
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
142
|
+
routes_message =
|
143
|
+
if only_methods.size > 3
|
144
|
+
"except: [#{(methods.map { |meth| ':' + meth } - only_methods).join(', ')}]"
|
145
|
+
else
|
146
|
+
"only: [#{only_methods.join(', ')}]"
|
147
|
+
end
|
146
148
|
add_error "restrict auto-generated routes #{friendly_route_name(node)} (#{routes_message})"
|
147
149
|
end
|
148
150
|
end
|
@@ -178,7 +180,7 @@ module RailsBestPractices
|
|
178
180
|
end
|
179
181
|
|
180
182
|
def hash_key_exist?(node, key)
|
181
|
-
node.hash_keys
|
183
|
+
node.hash_keys&.include?(key)
|
182
184
|
end
|
183
185
|
|
184
186
|
def friendly_route_name(node)
|
@@ -23,8 +23,7 @@ module RailsBestPractices
|
|
23
23
|
add_callback :start_command do |node|
|
24
24
|
if node.message.to_s == 'render'
|
25
25
|
keys = node.arguments.all.first.hash_keys
|
26
|
-
if keys && keys.size == 1 &&
|
27
|
-
(keys.include?('action') || keys.include?('template') || keys.include?('file'))
|
26
|
+
if keys && keys.size == 1 && (keys.include?('action') || keys.include?('template') || keys.include?('file'))
|
28
27
|
add_error 'simplify render in controllers'
|
29
28
|
end
|
30
29
|
end
|
@@ -23,9 +23,9 @@ module RailsBestPractices
|
|
23
23
|
# then it should be replaced by simplified syntax.
|
24
24
|
add_callback :start_command do |node|
|
25
25
|
if node.message.to_s == 'render'
|
26
|
-
hash_node =
|
27
|
-
if hash_node && hash_node.sexp_type == :bare_assoc_hash &&
|
28
|
-
|
26
|
+
hash_node = node.arguments.all.first
|
27
|
+
if hash_node && hash_node.sexp_type == :bare_assoc_hash && include_partial?(hash_node) &&
|
28
|
+
valid_hash?(hash_node)
|
29
29
|
add_error 'simplify render in views'
|
30
30
|
end
|
31
31
|
end
|
@@ -12,6 +12,7 @@ module RailsBestPractices
|
|
12
12
|
# Review process:
|
13
13
|
# check all first code line_number in method definitions (actions),
|
14
14
|
# if they are duplicated, then they should be moved to before_filter.
|
15
|
+
|
15
16
|
class UseBeforeFilterReview < Review
|
16
17
|
interesting_nodes :class
|
17
18
|
interesting_files CONTROLLER_FILES
|
@@ -48,7 +49,7 @@ module RailsBestPractices
|
|
48
49
|
|
49
50
|
private
|
50
51
|
|
51
|
-
|
52
|
+
# check method define node, and remember the first sentence.
|
52
53
|
def remember_first_sentence(node)
|
53
54
|
first_sentence = node.body.statements.first
|
54
55
|
return unless first_sentence
|
@@ -43,8 +43,8 @@ module RailsBestPractices
|
|
43
43
|
|
44
44
|
private
|
45
45
|
|
46
|
-
|
47
|
-
|
46
|
+
# check an attribute assignment node, if its message is xxx_id,
|
47
|
+
# then remember the receiver of the attribute assignment in @assignments.
|
48
48
|
def attribute_assignment(node)
|
49
49
|
if node.left_value.message.is_a?(Sexp) && node.left_value.message.to_s =~ /_id$/
|
50
50
|
receiver = node.left_value.receiver.to_s
|
@@ -52,9 +52,9 @@ module RailsBestPractices
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
# check a call node with message "save" or "save!",
|
56
|
+
# if the receiver of call node exists in @assignments,
|
57
|
+
# then the attribute assignment should be replaced by using model association.
|
58
58
|
def call_assignment(node)
|
59
59
|
if ['save', 'save!'].include? node.message.to_s
|
60
60
|
receiver = node.receiver.to_s
|
data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb
CHANGED
@@ -11,6 +11,7 @@ module RailsBestPractices
|
|
11
11
|
# Review process:
|
12
12
|
# check class node to remember the class name,
|
13
13
|
# and check the method definition nodes to see if the corresponding mailer views exist or not.
|
14
|
+
|
14
15
|
class UseMultipartAlternativeAsContentTypeOfEmailReview < Review
|
15
16
|
interesting_nodes :class, :def
|
16
17
|
interesting_files MAILER_FILES
|
@@ -31,14 +32,14 @@ module RailsBestPractices
|
|
31
32
|
|
32
33
|
private
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
# check if rails's syntax mailer views are canonical.
|
36
|
+
#
|
37
|
+
# @param [String] name method name in action_mailer
|
37
38
|
def rails_canonical_mailer_views?(name); end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
# check if rails3's syntax mailer views are canonical.
|
41
|
+
#
|
42
|
+
# @param [String] name method name in action_mailer
|
42
43
|
def rails3_canonical_mailer_views?(name)
|
43
44
|
return true if mailer_files(name).empty?
|
44
45
|
return true if mailer_files(name).none? { |filename| filename.index 'html' }
|
@@ -47,12 +48,12 @@ module RailsBestPractices
|
|
47
48
|
mailer_files(name).any? { |filename| filename.index 'text' }
|
48
49
|
end
|
49
50
|
|
50
|
-
|
51
|
+
# all mail view files for a method name.
|
51
52
|
def mailer_files(name)
|
52
53
|
Dir.entries(mailer_directory) { |filename| filename.index name.to_s }
|
53
54
|
end
|
54
55
|
|
55
|
-
|
56
|
+
# the view directory of mailer.
|
56
57
|
def mailer_directory
|
57
58
|
File.join(Core::Runner.base_path, "app/views/#{@klazz_name.to_s.underscore}")
|
58
59
|
end
|
@@ -49,8 +49,8 @@ module RailsBestPractices
|
|
49
49
|
|
50
50
|
private
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
# check a command node, if it is a callback definition, such as after_create, before_create,
|
53
|
+
# then save the callback methods in @callbacks
|
54
54
|
def remember_callback(node)
|
55
55
|
if node.message.to_s =~ /^after_|^before_/
|
56
56
|
node.arguments.all.each do |argument|
|
@@ -60,21 +60,21 @@ module RailsBestPractices
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
|
63
|
+
# check a defn node to see if the method name exists in the @callbacks.
|
64
64
|
def callback_method?(node)
|
65
65
|
@callbacks.find { |callback| callback == node.method_name.to_s }
|
66
66
|
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
# check a def node to see if it contains a actionmailer deliver call.
|
69
|
+
#
|
70
|
+
# if the message of call node is deliver,
|
71
|
+
# and the receiver of the call node is with receiver node who exists in @callbacks,
|
72
|
+
# then the call node is actionmailer deliver call.
|
73
73
|
def deliver_mailer?(node)
|
74
74
|
node.grep_nodes(sexp_type: :call) do |child_node|
|
75
75
|
if child_node.message.to_s == 'deliver'
|
76
76
|
if child_node.receiver.sexp_type == :method_add_arg &&
|
77
|
-
|
77
|
+
mailers.include?(child_node.receiver[1].receiver.to_s)
|
78
78
|
return true
|
79
79
|
end
|
80
80
|
end
|
@@ -34,11 +34,12 @@ module RailsBestPractices
|
|
34
34
|
#
|
35
35
|
# then the call node can use query attribute instead.
|
36
36
|
add_callback :start_if, :start_unless, :start_elsif, :start_ifop, :start_if_mod, :start_unless_mod do |node|
|
37
|
-
all_conditions =
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
all_conditions =
|
38
|
+
if node.conditional_statement == node.conditional_statement.all_conditions
|
39
|
+
[node.conditional_statement]
|
40
|
+
else
|
41
|
+
node.conditional_statement.all_conditions
|
42
|
+
end
|
42
43
|
all_conditions.each do |condition_node|
|
43
44
|
next unless query_attribute_node = query_attribute_node(condition_node)
|
44
45
|
|
@@ -51,13 +52,13 @@ module RailsBestPractices
|
|
51
52
|
|
52
53
|
private
|
53
54
|
|
54
|
-
|
55
|
-
|
55
|
+
# recursively check conditional statement nodes to see if there is a call node that may be
|
56
|
+
# possible query attribute.
|
56
57
|
def query_attribute_node(conditional_statement_node)
|
57
58
|
case conditional_statement_node.sexp_type
|
58
59
|
when :and, :or
|
59
|
-
node =
|
60
|
-
|
60
|
+
node =
|
61
|
+
query_attribute_node(conditional_statement_node[1]) || query_attribute_node(conditional_statement_node[2])
|
61
62
|
node.file = conditional_statement_code.file
|
62
63
|
return node
|
63
64
|
when :not
|
@@ -71,17 +72,17 @@ module RailsBestPractices
|
|
71
72
|
nil
|
72
73
|
end
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
75
|
+
# check if the node may use query attribute instead.
|
76
|
+
#
|
77
|
+
# if the node contains two method calls, e.g. @user.login.nil?
|
78
|
+
#
|
79
|
+
# for the first call, the receiver should be one of the class names and
|
80
|
+
# the message should be one of the attribute name.
|
81
|
+
#
|
82
|
+
# for the second call, the message should be one of nil?, blank? or present? or
|
83
|
+
# it is compared with an empty string.
|
84
|
+
#
|
85
|
+
# the node that may use query attribute.
|
85
86
|
def possible_query_attribute?(node)
|
86
87
|
return false unless node.receiver.sexp_type == :call
|
87
88
|
|
@@ -92,7 +93,7 @@ module RailsBestPractices
|
|
92
93
|
(QUERY_METHODS.include?(node.message.to_s) || compare_with_empty_string?(node))
|
93
94
|
end
|
94
95
|
|
95
|
-
|
96
|
+
# check if the receiver is one of the models.
|
96
97
|
def is_model?(variable_node)
|
97
98
|
return false if variable_node.const?
|
98
99
|
|
@@ -100,18 +101,17 @@ module RailsBestPractices
|
|
100
101
|
models.include?(class_name)
|
101
102
|
end
|
102
103
|
|
103
|
-
|
104
|
-
|
104
|
+
# check if the receiver and message is one of the model's attribute.
|
105
|
+
# the receiver should match one of the class model name, and the message should match one of attribute name.
|
105
106
|
def model_attribute?(variable_node, message)
|
106
107
|
class_name = variable_node.to_s.sub(/^@/, '').classify
|
107
108
|
attribute_type = model_attributes.get_attribute_type(class_name, message)
|
108
109
|
attribute_type && !%w[integer float].include?(attribute_type)
|
109
110
|
end
|
110
111
|
|
111
|
-
|
112
|
+
# check if the node is with node type :binary, node message :== and node argument is empty string.
|
112
113
|
def compare_with_empty_string?(node)
|
113
|
-
node.sexp_type == :binary &&
|
114
|
-
['==', '!='].include?(node.message.to_s) &&
|
114
|
+
node.sexp_type == :binary && ['==', '!='].include?(node.message.to_s) &&
|
115
115
|
s(:string_literal, s(:string_content)) == node.argument
|
116
116
|
end
|
117
117
|
end
|
@@ -31,13 +31,14 @@ module RailsBestPractices
|
|
31
31
|
node.body.statements.each do |child_node|
|
32
32
|
next if child_node.grep_nodes_count(sexp_type: %i[fcall command], message: WITH_SAY_METHODS) > 0
|
33
33
|
|
34
|
-
receiver_node =
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
receiver_node =
|
35
|
+
if child_node.sexp_type == :method_add_block
|
36
|
+
child_node[1]
|
37
|
+
elsif child_node.sexp_type == :method_add_arg
|
38
|
+
child_node[1]
|
39
|
+
else
|
40
|
+
child_node
|
41
|
+
end
|
41
42
|
if receiver_node.sexp_type == :call
|
42
43
|
add_error('use say with time in migrations', node.file, child_node.line_number)
|
43
44
|
end
|
@@ -30,25 +30,27 @@ module RailsBestPractices
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
# check a if node to see
|
34
|
+
#
|
35
|
+
# if the conditional statement is compared with current_user or current_user.id,
|
36
|
+
# and there is a redirect_to method call in the block body,
|
37
|
+
# then it should be replaced by using scope access.
|
38
38
|
def current_user_redirect?(node)
|
39
|
-
all_conditions =
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
all_conditions =
|
40
|
+
if node.conditional_statement == node.conditional_statement.all_conditions
|
41
|
+
[node.conditional_statement]
|
42
|
+
else
|
43
|
+
node.conditional_statement.all_conditions
|
44
|
+
end
|
45
|
+
results =
|
46
|
+
all_conditions.map do |condition_node|
|
47
|
+
['==', '!='].include?(condition_node.message.to_s) &&
|
48
|
+
(current_user?(condition_node.argument) || current_user?(condition_node.receiver))
|
49
|
+
end
|
48
50
|
results.any? { |result| result == true } && node.body.grep_node(message: 'redirect_to')
|
49
51
|
end
|
50
52
|
|
51
|
-
|
53
|
+
# check a call node to see if it uses current_user, or current_user.id.
|
52
54
|
def current_user?(node)
|
53
55
|
node.to_s == 'current_user' || (node.receiver.to_s == 'current_user' && node.message.to_s == 'id')
|
54
56
|
end
|
@@ -18,7 +18,8 @@ module RailsBestPractices
|
|
18
18
|
# check command node to see if load 'deploy/assets'
|
19
19
|
add_callback :start_command do |node|
|
20
20
|
if Prepares.gems.gem_version('rails').to_i == 3
|
21
|
-
if !Prepares.gems.has_gem?('turbo-sprockets-rails3') && node.message.to_s == 'load' &&
|
21
|
+
if !Prepares.gems.has_gem?('turbo-sprockets-rails3') && node.message.to_s == 'load' &&
|
22
|
+
node.arguments.to_s == 'deploy/assets'
|
22
23
|
add_error 'speed up assets precompile with turbo-sprockets-rails3'
|
23
24
|
end
|
24
25
|
end
|
data/lib/rails_best_practices.rb
CHANGED
@@ -8,8 +8,8 @@ require 'rails_best_practices/analyzer'
|
|
8
8
|
require 'rails_best_practices/lexicals'
|
9
9
|
require 'rails_best_practices/prepares'
|
10
10
|
require 'rails_best_practices/reviews'
|
11
|
+
require 'rails_best_practices/inline_disables'
|
11
12
|
require 'rails_best_practices/option_parser'
|
12
13
|
require 'rails_best_practices/cli'
|
13
14
|
|
14
|
-
module RailsBestPractices
|
15
|
-
end
|
15
|
+
module RailsBestPractices; end
|
@@ -1,43 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
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
|
-
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'rails_best_practices/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'rails_best_practices'
|
9
|
+
spec.version = RailsBestPractices::VERSION
|
10
|
+
spec.platform = Gem::Platform::RUBY
|
11
|
+
spec.authors = ['Richard Huang']
|
12
|
+
spec.email = ['flyerhzm@gmail.com']
|
13
|
+
spec.homepage = 'http://rails-bestpractices.com'
|
14
|
+
spec.summary = 'a code metric tool for rails codes.'
|
15
|
+
spec.description = 'a code metric tool for rails codes, written in Ruby.'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.required_ruby_version = '>= 1.9.0'
|
19
|
+
spec.required_rubygems_version = '>= 1.3.6'
|
20
|
+
|
21
|
+
spec.add_runtime_dependency('activesupport')
|
22
|
+
spec.add_runtime_dependency('code_analyzer', '>= 0.5.2')
|
23
|
+
spec.add_runtime_dependency('erubis')
|
24
|
+
spec.add_runtime_dependency('i18n')
|
25
|
+
spec.add_runtime_dependency('json')
|
26
|
+
spec.add_runtime_dependency('require_all', '~> 3.0')
|
27
|
+
spec.add_runtime_dependency('ruby-progressbar')
|
28
|
+
|
29
|
+
spec.add_development_dependency('awesome_print')
|
30
|
+
spec.add_development_dependency('bundler')
|
31
|
+
spec.add_development_dependency('haml')
|
32
|
+
spec.add_development_dependency('rake')
|
33
|
+
spec.add_development_dependency('rspec')
|
34
|
+
spec.add_development_dependency('slim')
|
35
|
+
|
36
|
+
spec.files = `git ls-files`.split($/)
|
37
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
38
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
39
|
+
spec.require_paths = %w[lib assets]
|
40
|
+
|
41
|
+
spec.post_install_message = <<~POST_INSTALL_MESSAGE
|
41
42
|
#{'*' * 80}
|
42
43
|
|
43
44
|
rails_best_practices is a code metric tool to check the quality of rails codes.
|
@@ -5,8 +5,7 @@ require 'rails_best_practices/reviews/review'
|
|
5
5
|
module RailsBestPractices
|
6
6
|
module Plugins
|
7
7
|
module Reviews
|
8
|
-
class NotUseRailsRootReview < RailsBestPractices::Reviews::Review
|
9
|
-
end
|
8
|
+
class NotUseRailsRootReview < RailsBestPractices::Reviews::Review; end
|
10
9
|
end
|
11
10
|
end
|
12
11
|
end
|