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
@@ -46,7 +46,9 @@ module RailsBestPractices
|
|
46
46
|
# @return [Boolean] has a method or not
|
47
47
|
def has_method?(class_name, method_name, access_control = nil)
|
48
48
|
if access_control
|
49
|
-
!!methods(class_name).find
|
49
|
+
!!methods(class_name).find do |method|
|
50
|
+
method.method_name == method_name && method.access_control == access_control
|
51
|
+
end
|
50
52
|
else
|
51
53
|
!!methods(class_name).find { |method| method.method_name == method_name }
|
52
54
|
end
|
@@ -58,10 +60,10 @@ module RailsBestPractices
|
|
58
60
|
# @param [String] method name
|
59
61
|
def mark_parent_class_method_used(class_name, method_name)
|
60
62
|
klass = Prepares.klasses.find { |klass| klass.to_s == class_name }
|
61
|
-
if klass
|
63
|
+
if klass&.extend_class_name
|
62
64
|
mark_parent_class_method_used(klass.extend_class_name, method_name)
|
63
65
|
method = get_method(klass.extend_class_name, method_name)
|
64
|
-
method
|
66
|
+
method&.mark_used
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
@@ -73,7 +75,7 @@ module RailsBestPractices
|
|
73
75
|
Prepares.klasses.select { |klass| klass.extend_class_name == class_name }.each do |klass|
|
74
76
|
mark_subclasses_method_used(klass.to_s, method_name)
|
75
77
|
method = get_method(klass.to_s, method_name)
|
76
|
-
method
|
78
|
+
method&.mark_used
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
@@ -83,7 +85,7 @@ module RailsBestPractices
|
|
83
85
|
# @param [String] method name
|
84
86
|
def mark_publicize(class_name, method_name)
|
85
87
|
method = get_method(class_name, method_name)
|
86
|
-
method
|
88
|
+
method&.publicize
|
87
89
|
end
|
88
90
|
|
89
91
|
# Mark parent classs' method as public.
|
@@ -92,7 +94,7 @@ module RailsBestPractices
|
|
92
94
|
# @param [String] method name
|
93
95
|
def mark_parent_class_methods_publicize(class_name, method_name)
|
94
96
|
klass = Prepares.klasses.find { |klass| klass.to_s == class_name }
|
95
|
-
if klass
|
97
|
+
if klass&.extend_class_name
|
96
98
|
mark_parent_class_methods_publicize(klass.extend_class_name, method_name)
|
97
99
|
mark_publicize(class_name, method_name)
|
98
100
|
end
|
@@ -113,7 +115,9 @@ module RailsBestPractices
|
|
113
115
|
# @return [Method] Method object
|
114
116
|
def get_method(class_name, method_name, access_control = nil)
|
115
117
|
if access_control
|
116
|
-
methods(class_name).find
|
118
|
+
methods(class_name).find do |method|
|
119
|
+
method.method_name == method_name && method.access_control == access_control
|
120
|
+
end
|
117
121
|
else
|
118
122
|
methods(class_name).find { |method| method.method_name == method_name }
|
119
123
|
end
|
@@ -125,20 +129,21 @@ module RailsBestPractices
|
|
125
129
|
# @return [Array] array of Method
|
126
130
|
def get_all_unused_methods(access_control = nil)
|
127
131
|
@methods.inject([]) do |unused_methods, (_class_name, methods)|
|
128
|
-
unused_methods +=
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
132
|
+
unused_methods +=
|
133
|
+
if access_control
|
134
|
+
methods.select { |method| method.access_control == access_control && !method.used }
|
135
|
+
else
|
136
|
+
methods.reject(&:used)
|
137
|
+
end
|
133
138
|
end.reject { |method| method.access_control == 'public' && @possible_methods[method.method_name] }
|
134
139
|
end
|
135
140
|
|
136
141
|
private
|
137
142
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
143
|
+
# Methods of a class.
|
144
|
+
#
|
145
|
+
# @param [String] class name
|
146
|
+
# @return [Array] array of methods
|
142
147
|
def methods(class_name)
|
143
148
|
@methods[class_name] ||= []
|
144
149
|
end
|
@@ -15,7 +15,9 @@ module RailsBestPractices
|
|
15
15
|
# @param [String] association class name
|
16
16
|
def add_association(model_name, association_name, association_meta, association_class = nil)
|
17
17
|
@associations[model_name] ||= {}
|
18
|
-
@associations[model_name][association_name] = {
|
18
|
+
@associations[model_name][association_name] = {
|
19
|
+
'meta' => association_meta, 'class_name' => association_class || association_name.classify
|
20
|
+
}
|
19
21
|
end
|
20
22
|
|
21
23
|
# Get a model association.
|
@@ -25,7 +27,7 @@ module RailsBestPractices
|
|
25
27
|
# @return [Hash] {"meta" => association_meta, "class_name" => association_class}
|
26
28
|
def get_association(model_name, association_name)
|
27
29
|
associations = @associations[model_name]
|
28
|
-
associations
|
30
|
+
associations && associations[association_name]
|
29
31
|
end
|
30
32
|
|
31
33
|
# If it is a model's association.
|
@@ -49,8 +51,11 @@ module RailsBestPractices
|
|
49
51
|
# @param [String] association_name
|
50
52
|
# @return [String] association's class name
|
51
53
|
def get_association_class_name(table_name, association_name)
|
52
|
-
|
53
|
-
|
54
|
+
(
|
55
|
+
associations =
|
56
|
+
@associations.select { |model, _model_associations| model.gsub('::', '').tableize == table_name }.values
|
57
|
+
.first
|
58
|
+
) && (association_meta = associations.select { |name, _meta| name == association_name }.values.first) &&
|
54
59
|
association_meta['class_name']
|
55
60
|
end
|
56
61
|
end
|
@@ -24,8 +24,8 @@ module RailsBestPractices
|
|
24
24
|
# mappings can be specified by e.g.
|
25
25
|
# post 'some/:pattern' => 'controller#action'
|
26
26
|
if action_name.is_a?(String) && action_name =~ /\A(\w+)#(\w+)\z/
|
27
|
-
controller_name =
|
28
|
-
action_name =
|
27
|
+
controller_name = Regexp.last_match(1)
|
28
|
+
action_name = Regexp.last_match(2)
|
29
29
|
end
|
30
30
|
|
31
31
|
if controller_name
|
@@ -20,33 +20,23 @@ module RailsBestPractices
|
|
20
20
|
class Runner
|
21
21
|
attr_reader :checks
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
# @param [String] path the base path
|
26
|
-
def self.base_path=(path)
|
27
|
-
@base_path = path
|
28
|
-
end
|
29
|
-
|
30
|
-
# get the base path, by default, the base path is current path.
|
31
|
-
#
|
32
|
-
# @return [String] the base path
|
33
|
-
def self.base_path
|
34
|
-
@base_path || '.'
|
35
|
-
end
|
23
|
+
class << self
|
24
|
+
attr_writer :base_path, :config_path
|
36
25
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
26
|
+
# get the base path, by default, the base path is current path.
|
27
|
+
#
|
28
|
+
# @return [String] the base path
|
29
|
+
def base_path
|
30
|
+
@base_path || '.'
|
31
|
+
end
|
43
32
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
33
|
+
# get the configuration path, if will default to config/rails_best_practices.yml
|
34
|
+
#
|
35
|
+
# @return [String] the config path
|
36
|
+
def config_path
|
37
|
+
custom_config = @config_path || File.join(Runner.base_path, 'config/rails_best_practices.yml')
|
38
|
+
File.exist?(custom_config) ? custom_config : RailsBestPractices::Analyzer::DEFAULT_CONFIG
|
39
|
+
end
|
50
40
|
end
|
51
41
|
|
52
42
|
# initialize the runner.
|
@@ -66,9 +56,18 @@ module RailsBestPractices
|
|
66
56
|
load_plugin_reviews if reviews.empty?
|
67
57
|
|
68
58
|
@lexical_checker ||= CodeAnalyzer::CheckingVisitor::Plain.new(checkers: @lexicals)
|
69
|
-
@plain_prepare_checker ||=
|
70
|
-
|
59
|
+
@plain_prepare_checker ||=
|
60
|
+
CodeAnalyzer::CheckingVisitor::Plain.new(
|
61
|
+
checkers: @prepares.select { |checker| checker.is_a? Prepares::GemfilePrepare }
|
62
|
+
)
|
63
|
+
@default_prepare_checker ||=
|
64
|
+
CodeAnalyzer::CheckingVisitor::Default.new(
|
65
|
+
checkers: @prepares.reject { |checker| checker.is_a? Prepares::GemfilePrepare }
|
66
|
+
)
|
71
67
|
@review_checker ||= CodeAnalyzer::CheckingVisitor::Default.new(checkers: @reviews)
|
68
|
+
|
69
|
+
@inlnie_disable ||= InlineDisables::InlineDisable.new
|
70
|
+
@inline_disable_checker ||= CodeAnalyzer::CheckingVisitor::Plain.new(checkers: [@inlnie_disable])
|
72
71
|
end
|
73
72
|
|
74
73
|
# lexical analysis the file.
|
@@ -110,19 +109,35 @@ module RailsBestPractices
|
|
110
109
|
@review_checker.after_check
|
111
110
|
end
|
112
111
|
|
112
|
+
# disable check by inline comment the file.
|
113
|
+
#
|
114
|
+
# @param [String] filename of the file
|
115
|
+
# @param [String] content of the file
|
116
|
+
def inline_disable(filename, content)
|
117
|
+
content = parse_html_template(filename, content)
|
118
|
+
@inline_disable_checker.check(filename, content)
|
119
|
+
end
|
120
|
+
|
121
|
+
def after_inline_disable
|
122
|
+
@inline_disable_checker.after_check
|
123
|
+
end
|
124
|
+
|
113
125
|
# get all errors from lexicals and reviews.
|
114
126
|
#
|
115
127
|
# @return [Array] all errors from lexicals and reviews
|
116
128
|
def errors
|
117
|
-
@errors ||=
|
129
|
+
@errors ||= begin
|
130
|
+
reported_errors = (@reviews + @lexicals).collect(&:errors).flatten
|
131
|
+
reported_errors.reject { |error| @inlnie_disable.disabled?(error) }
|
132
|
+
end
|
118
133
|
end
|
119
134
|
|
120
135
|
private
|
121
136
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
137
|
+
# parse html template code, erb, haml and slim.
|
138
|
+
#
|
139
|
+
# @param [String] filename is the filename of the erb, haml or slim code.
|
140
|
+
# @param [String] content is the source code of erb, haml or slim file.
|
126
141
|
def parse_html_template(filename, content)
|
127
142
|
if filename =~ /.*\.erb$|.*\.rhtml$/
|
128
143
|
content = Erubis::OnlyRuby.new(content).src
|
@@ -150,12 +165,12 @@ module RailsBestPractices
|
|
150
165
|
content
|
151
166
|
end
|
152
167
|
|
153
|
-
|
168
|
+
# load all prepares.
|
154
169
|
def load_prepares
|
155
170
|
Prepares.constants.map { |prepare| Prepares.const_get(prepare).new }
|
156
171
|
end
|
157
172
|
|
158
|
-
|
173
|
+
# load all plugin reviews.
|
159
174
|
def load_plugin_reviews
|
160
175
|
plugins = File.join(Runner.base_path, 'lib', 'rails_best_practices', 'plugins', 'reviews')
|
161
176
|
if File.directory?(plugins)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBestPractices
|
4
|
+
module InlineDisables
|
5
|
+
class CommentRipper < Ripper::SexpBuilder
|
6
|
+
attr_reader :comments
|
7
|
+
|
8
|
+
def initialize(*arg)
|
9
|
+
super
|
10
|
+
@comments = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_comment(*arg)
|
14
|
+
# [sexp_type, statement, [lineno, column]] = super
|
15
|
+
comments << super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsBestPractices
|
4
|
+
module InlineDisables
|
5
|
+
class InlineDisable < Core::Check
|
6
|
+
interesting_files ALL_FILES
|
7
|
+
url '#'
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
@disabled_errors = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def check(filename, content)
|
15
|
+
comments = CommentRipper.new(content).tap(&:parse).comments
|
16
|
+
comments.each do |_sexp_type, statement, (line_number, _column)|
|
17
|
+
add_as_disable_errors(filename, statement, line_number)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def disabled?(error)
|
22
|
+
error_key = [error.filename, error.line_number, error.type.split('::').last].join('-')
|
23
|
+
disabled_error_keys.include?(error_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def disabled_error_keys
|
29
|
+
@disabled_error_keys ||= Set.new(@disabled_errors.map { |e| [e.filename, e.line_number, e.type].join('-') })
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_as_disable_errors(filename, statement, line_number)
|
33
|
+
match = statement.match(/rails_b(?:est_)?p(?:ractices)?:disable (.*)/)
|
34
|
+
return unless match
|
35
|
+
|
36
|
+
check_names = match[1].split(',')
|
37
|
+
check_names.each do |check_name|
|
38
|
+
add_as_disable_error(filename, check_name.gsub(/Check$/, 'Review'), line_number)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_as_disable_error(filename, check_name, line_number)
|
43
|
+
@disabled_errors <<
|
44
|
+
RailsBestPractices::Core::Error.new(
|
45
|
+
filename: filename, line_number: line_number, message: 'disable by inline comment', type: check_name, url: url
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -22,9 +22,13 @@ module RailsBestPractices
|
|
22
22
|
content.each_line do |line|
|
23
23
|
line_no += 1
|
24
24
|
actual_line_length = line.sub(/\s+$/, '').length
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
next unless actual_line_length > @max_line_length
|
26
|
+
|
27
|
+
add_error(
|
28
|
+
"line is longer than #{@max_line_length} characters (#{actual_line_length} characters)",
|
29
|
+
filename,
|
30
|
+
line_no
|
31
|
+
)
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
@@ -7,9 +7,10 @@ module RailsBestPractices
|
|
7
7
|
# Usage: rails_best_practices [options] path
|
8
8
|
# -d, --debug debug mode
|
9
9
|
# --silent silent mode
|
10
|
-
# -f, --format FORMAT output format (text, html,
|
10
|
+
# -f, --format FORMAT output format (text, html, yaml, json, xml)
|
11
11
|
# --output-file FILE output html file for the analyzing result
|
12
12
|
# --without-color only output plain text without color
|
13
|
+
# --with-atom open file by atom in html format
|
13
14
|
# --with-textmate open file by textmate in html format
|
14
15
|
# --with-vscode open file by vscode in html format
|
15
16
|
# --with-sublime open file by sublime in html format (requires subl-handler)
|
@@ -41,7 +42,7 @@ module RailsBestPractices
|
|
41
42
|
options['debug'] = true
|
42
43
|
end
|
43
44
|
|
44
|
-
opts.on('-f', '--format FORMAT', 'output format (text, html,
|
45
|
+
opts.on('-f', '--format FORMAT', 'output format (text, html, yaml, json, xml)') do |format|
|
45
46
|
options['format'] = format
|
46
47
|
end
|
47
48
|
|
@@ -49,6 +50,10 @@ module RailsBestPractices
|
|
49
50
|
options['without-color'] = true
|
50
51
|
end
|
51
52
|
|
53
|
+
opts.on('--with-atom', 'open file by atom in html format') do
|
54
|
+
options['with-atom'] = true
|
55
|
+
end
|
56
|
+
|
52
57
|
opts.on('--with-textmate', 'open file by textmate in html format') do
|
53
58
|
options['with-textmate'] = true
|
54
59
|
end
|
@@ -111,7 +116,12 @@ module RailsBestPractices
|
|
111
116
|
exit
|
112
117
|
end
|
113
118
|
|
114
|
-
opts.on(
|
119
|
+
opts.on(
|
120
|
+
'-x',
|
121
|
+
'--exclude PATTERNS',
|
122
|
+
"Don't analyze files matching a pattern",
|
123
|
+
'(comma-separated regexp list)'
|
124
|
+
) do |list|
|
115
125
|
begin
|
116
126
|
options['exclude'] = list.split(',').map { |x| Regexp.new x }
|
117
127
|
rescue RegexpError => e
|
@@ -119,11 +129,16 @@ module RailsBestPractices
|
|
119
129
|
end
|
120
130
|
end
|
121
131
|
|
122
|
-
opts.on(
|
132
|
+
opts.on(
|
133
|
+
'-o',
|
134
|
+
'--only PATTERNS',
|
135
|
+
'Analyze files only matching a pattern',
|
136
|
+
'(comma-separated regexp list)'
|
137
|
+
) do |list|
|
123
138
|
begin
|
124
139
|
options['only'] = list.split(',').map { |x| Regexp.new x }
|
125
140
|
rescue RegexpError => e
|
126
|
-
raise OptionParser::InvalidArgument e.message
|
141
|
+
raise OptionParser::InvalidArgument, e.message
|
127
142
|
end
|
128
143
|
end
|
129
144
|
|
@@ -133,7 +148,8 @@ module RailsBestPractices
|
|
133
148
|
|
134
149
|
opts.on(
|
135
150
|
'-c',
|
136
|
-
'--config CONFIG_PATH',
|
151
|
+
'--config CONFIG_PATH',
|
152
|
+
'configuration file location (defaults to config/rails_best_practices.yml)'
|
137
153
|
) do |config_path|
|
138
154
|
options['config'] = config_path
|
139
155
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module RailsBestPractices
|
4
4
|
module Prepares
|
5
5
|
# Remember controllers and controller methods
|
6
|
+
|
6
7
|
class ControllerPrepare < Core::Check
|
7
8
|
include Core::Check::Classable
|
8
9
|
include Core::Check::InheritedResourcesable
|
@@ -32,7 +33,11 @@ module RailsBestPractices
|
|
32
33
|
add_callback :end_class do |node|
|
33
34
|
if @inherited_resources && @current_controller_name != 'ApplicationController'
|
34
35
|
@actions.each do |action|
|
35
|
-
@methods.add_method(
|
36
|
+
@methods.add_method(
|
37
|
+
@current_controller_name,
|
38
|
+
action,
|
39
|
+
'file' => node.file, 'line_number' => node.line_number
|
40
|
+
)
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
@@ -78,14 +83,21 @@ module RailsBestPractices
|
|
78
83
|
# }
|
79
84
|
add_callback :start_def do |node|
|
80
85
|
method_name = node.method_name.to_s
|
81
|
-
@methods.add_method(
|
86
|
+
@methods.add_method(
|
87
|
+
current_class_name,
|
88
|
+
method_name,
|
89
|
+
{ 'file' => node.file, 'line_number' => node.line_number },
|
90
|
+
current_access_control
|
91
|
+
)
|
82
92
|
end
|
83
93
|
|
84
94
|
# ask Reviews::RemoveUnusedMoethodsInHelperReview to check the controllers who include helpers.
|
85
95
|
add_callback :after_check do
|
86
96
|
descendants = @helpers.map(&:descendants).flatten
|
87
97
|
if descendants.present?
|
88
|
-
Reviews::RemoveUnusedMethodsInHelpersReview.interesting_files *descendants.map { |descendant|
|
98
|
+
Reviews::RemoveUnusedMethodsInHelpersReview.interesting_files *descendants.map { |descendant|
|
99
|
+
/#{descendant.underscore}/
|
100
|
+
}
|
89
101
|
end
|
90
102
|
end
|
91
103
|
end
|
@@ -32,7 +32,12 @@ module RailsBestPractices
|
|
32
32
|
add_callback :start_def do |node|
|
33
33
|
if node.file =~ HELPER_FILES
|
34
34
|
method_name = node.method_name.to_s
|
35
|
-
@methods.add_method(
|
35
|
+
@methods.add_method(
|
36
|
+
current_module_name,
|
37
|
+
method_name,
|
38
|
+
{ 'file' => node.file, 'line_number' => node.line_number },
|
39
|
+
current_access_control
|
40
|
+
)
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module RailsBestPractices
|
4
4
|
module Prepares
|
5
5
|
# Check all initializers
|
6
|
+
|
6
7
|
class InitializerPrepare < Core::Check
|
7
8
|
interesting_nodes :method_add_arg, :class
|
8
9
|
interesting_files INITIALIZER_FILES
|
@@ -21,8 +22,7 @@ module RailsBestPractices
|
|
21
22
|
# check if the node is
|
22
23
|
# ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
|
23
24
|
def include_forbidden_attributes_protection?(node)
|
24
|
-
node.receiver.to_s == 'ActiveRecord::Base' &&
|
25
|
-
node.message.to_s == 'send' &&
|
25
|
+
node.receiver.to_s == 'ActiveRecord::Base' && node.message.to_s == 'send' &&
|
26
26
|
node.arguments.all.map(&:to_s) == ['include', 'ActiveModel::ForbiddenAttributesProtection']
|
27
27
|
end
|
28
28
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module RailsBestPractices
|
4
4
|
module Prepares
|
5
5
|
# Remember models and model associations.
|
6
|
+
|
6
7
|
class ModelPrepare < Core::Check
|
7
8
|
include Core::Check::Classable
|
8
9
|
include Core::Check::Accessable
|
@@ -10,7 +11,17 @@ module RailsBestPractices
|
|
10
11
|
interesting_nodes :class, :def, :defs, :command, :alias
|
11
12
|
interesting_files MODEL_FILES
|
12
13
|
|
13
|
-
ASSOCIATION_METHODS = %w[
|
14
|
+
ASSOCIATION_METHODS = %w[
|
15
|
+
belongs_to
|
16
|
+
has_one
|
17
|
+
has_many
|
18
|
+
has_and_belongs_to_many
|
19
|
+
embeds_many
|
20
|
+
embeds_one
|
21
|
+
embedded_in
|
22
|
+
many
|
23
|
+
one
|
24
|
+
].freeze
|
14
25
|
|
15
26
|
def initialize
|
16
27
|
@models = Prepares.models
|
@@ -39,11 +50,14 @@ module RailsBestPractices
|
|
39
50
|
# }
|
40
51
|
# }
|
41
52
|
add_callback :start_def do |node|
|
42
|
-
if @klass &&
|
43
|
-
current_extend_class_name != 'ActionMailer::Base' &&
|
44
|
-
(classable_modules.empty? || klasses.any?)
|
53
|
+
if @klass && current_extend_class_name != 'ActionMailer::Base' && (classable_modules.empty? || klasses.any?)
|
45
54
|
method_name = node.method_name.to_s
|
46
|
-
@methods.add_method(
|
55
|
+
@methods.add_method(
|
56
|
+
current_class_name,
|
57
|
+
method_name,
|
58
|
+
{ 'file' => node.file, 'line_number' => node.line_number },
|
59
|
+
current_access_control
|
60
|
+
)
|
47
61
|
end
|
48
62
|
end
|
49
63
|
|
@@ -62,7 +76,12 @@ module RailsBestPractices
|
|
62
76
|
add_callback :start_defs do |node|
|
63
77
|
if @klass && current_extend_class_name != 'ActionMailer::Base'
|
64
78
|
method_name = node.method_name.to_s
|
65
|
-
@methods.add_method(
|
79
|
+
@methods.add_method(
|
80
|
+
current_class_name,
|
81
|
+
method_name,
|
82
|
+
{ 'file' => node.file, 'line_number' => node.line_number },
|
83
|
+
current_access_control
|
84
|
+
)
|
66
85
|
end
|
67
86
|
end
|
68
87
|
|
@@ -81,15 +100,31 @@ module RailsBestPractices
|
|
81
100
|
case node.message.to_s
|
82
101
|
when 'named_scope', 'scope', 'alias_method'
|
83
102
|
method_name = node.arguments.all.first.to_s
|
84
|
-
@methods.add_method(
|
103
|
+
@methods.add_method(
|
104
|
+
current_class_name,
|
105
|
+
method_name,
|
106
|
+
{ 'file' => node.file, 'line_number' => node.line_number },
|
107
|
+
current_access_control
|
108
|
+
)
|
85
109
|
when 'alias_method_chain'
|
86
110
|
method, feature = *node.arguments.all.map(&:to_s)
|
87
|
-
@methods.add_method(
|
88
|
-
|
111
|
+
@methods.add_method(
|
112
|
+
current_class_name,
|
113
|
+
"#{method}_with_#{feature}",
|
114
|
+
{ 'file' => node.file, 'line_number' => node.line_number },
|
115
|
+
current_access_control
|
116
|
+
)
|
117
|
+
@methods.add_method(
|
118
|
+
current_class_name,
|
119
|
+
method.to_s,
|
120
|
+
{ 'file' => node.file, 'line_number' => node.line_number },
|
121
|
+
current_access_control
|
122
|
+
)
|
89
123
|
when 'field'
|
90
124
|
arguments = node.arguments.all
|
91
125
|
attribute_name = arguments.first.to_s
|
92
|
-
attribute_type =
|
126
|
+
attribute_type =
|
127
|
+
arguments.last.hash_value('type').present? ? arguments.last.hash_value('type').to_s : 'String'
|
93
128
|
@model_attributes.add_attribute(current_class_name, attribute_name, attribute_type)
|
94
129
|
when 'key'
|
95
130
|
attribute_name, attribute_type = node.arguments.all.map(&:to_s)
|
@@ -102,7 +137,12 @@ module RailsBestPractices
|
|
102
137
|
# check alias node to remembr the alias methods.
|
103
138
|
add_callback :start_alias do |node|
|
104
139
|
method_name = node.new_method.to_s
|
105
|
-
@methods.add_method(
|
140
|
+
@methods.add_method(
|
141
|
+
current_class_name,
|
142
|
+
method_name,
|
143
|
+
{ 'file' => node.file, 'line_number' => node.line_number },
|
144
|
+
current_access_control
|
145
|
+
)
|
106
146
|
end
|
107
147
|
|
108
148
|
# after prepare process, fix incorrect associations' class_name.
|
@@ -122,7 +162,7 @@ module RailsBestPractices
|
|
122
162
|
|
123
163
|
private
|
124
164
|
|
125
|
-
|
165
|
+
# remember associations, with class to association names.
|
126
166
|
def remember_association(node)
|
127
167
|
association_meta = node.message.to_s
|
128
168
|
association_name = node.arguments.all.first.to_s
|