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.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile +2 -1
  4. data/Gemfile.lock +49 -43
  5. data/Guardfile +2 -0
  6. data/Rakefile +2 -0
  7. data/assets/result.html.erb +2 -0
  8. data/lib/rails_best_practices/analyzer.rb +59 -48
  9. data/lib/rails_best_practices/core/check.rb +39 -32
  10. data/lib/rails_best_practices/core/checks_loader.rb +8 -6
  11. data/lib/rails_best_practices/core/configs.rb +1 -2
  12. data/lib/rails_best_practices/core/controllers.rb +1 -2
  13. data/lib/rails_best_practices/core/error.rb +1 -1
  14. data/lib/rails_best_practices/core/helpers.rb +1 -2
  15. data/lib/rails_best_practices/core/mailers.rb +1 -2
  16. data/lib/rails_best_practices/core/methods.rb +21 -16
  17. data/lib/rails_best_practices/core/model_associations.rb +9 -4
  18. data/lib/rails_best_practices/core/models.rb +1 -2
  19. data/lib/rails_best_practices/core/modules.rb +1 -1
  20. data/lib/rails_best_practices/core/routes.rb +2 -2
  21. data/lib/rails_best_practices/core/runner.rb +49 -34
  22. data/lib/rails_best_practices/inline_disables/comment_ripper.rb +19 -0
  23. data/lib/rails_best_practices/inline_disables/inline_disable.rb +50 -0
  24. data/lib/rails_best_practices/inline_disables.rb +3 -0
  25. data/lib/rails_best_practices/lexicals/long_line_check.rb +7 -3
  26. data/lib/rails_best_practices/option_parser.rb +22 -6
  27. data/lib/rails_best_practices/prepares/controller_prepare.rb +15 -3
  28. data/lib/rails_best_practices/prepares/gemfile_prepare.rb +1 -1
  29. data/lib/rails_best_practices/prepares/helper_prepare.rb +6 -1
  30. data/lib/rails_best_practices/prepares/initializer_prepare.rb +2 -2
  31. data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -0
  32. data/lib/rails_best_practices/prepares/model_prepare.rb +52 -12
  33. data/lib/rails_best_practices/prepares/route_prepare.rb +16 -10
  34. data/lib/rails_best_practices/prepares.rb +1 -1
  35. data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +15 -13
  36. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +34 -29
  37. data/lib/rails_best_practices/reviews/check_destroy_return_value_review.rb +14 -5
  38. data/lib/rails_best_practices/reviews/check_save_return_value_review.rb +19 -8
  39. data/lib/rails_best_practices/reviews/hash_syntax_review.rb +5 -5
  40. data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +4 -4
  41. data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +7 -8
  42. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +6 -6
  43. data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +1 -1
  44. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +6 -7
  45. data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +7 -8
  46. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +12 -10
  47. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +1 -2
  48. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +5 -5
  49. data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +5 -2
  50. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +6 -3
  51. data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +6 -4
  52. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +29 -9
  53. data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +3 -3
  54. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +17 -15
  55. data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +1 -2
  56. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +3 -3
  57. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +2 -1
  58. data/lib/rails_best_practices/reviews/use_model_association_review.rb +5 -5
  59. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +9 -8
  60. data/lib/rails_best_practices/reviews/use_observer_review.rb +9 -9
  61. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +26 -26
  62. data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +8 -7
  63. data/lib/rails_best_practices/reviews/use_scope_access_review.rb +17 -15
  64. data/lib/rails_best_practices/reviews/use_turbo_sprockets_rails3_review.rb +2 -1
  65. data/lib/rails_best_practices/version.rb +1 -1
  66. data/lib/rails_best_practices.rb +2 -2
  67. data/rails_best_practices.gemspec +39 -38
  68. data/spec/fixtures/lib/rails_best_practices/plugins/reviews/not_use_rails_root_review.rb +1 -2
  69. data/spec/rails_best_practices/analyzer_spec.rb +73 -42
  70. data/spec/rails_best_practices/core/check_spec.rb +5 -5
  71. data/spec/rails_best_practices/core/checks_loader_spec.rb +3 -3
  72. data/spec/rails_best_practices/core/configs_spec.rb +1 -1
  73. data/spec/rails_best_practices/core/controllers_spec.rb +1 -1
  74. data/spec/rails_best_practices/core/error_spec.rb +21 -21
  75. data/spec/rails_best_practices/core/except_methods_spec.rb +7 -7
  76. data/spec/rails_best_practices/core/gems_spec.rb +4 -4
  77. data/spec/rails_best_practices/core/helpers_spec.rb +1 -1
  78. data/spec/rails_best_practices/core/klasses_spec.rb +3 -3
  79. data/spec/rails_best_practices/core/mailers_spec.rb +1 -1
  80. data/spec/rails_best_practices/core/methods_spec.rb +6 -6
  81. data/spec/rails_best_practices/core/model_associations_spec.rb +10 -6
  82. data/spec/rails_best_practices/core/model_attributes_spec.rb +4 -4
  83. data/spec/rails_best_practices/core/models_spec.rb +1 -1
  84. data/spec/rails_best_practices/core/modules_spec.rb +5 -5
  85. data/spec/rails_best_practices/core/routes_spec.rb +5 -5
  86. data/spec/rails_best_practices/core/runner_spec.rb +9 -7
  87. data/spec/rails_best_practices/core_ext/erubis_spec.rb +10 -10
  88. data/spec/rails_best_practices/inline_disables/inline_disable_spec.rb +62 -0
  89. data/spec/rails_best_practices/lexicals/long_line_check_spec.rb +11 -10
  90. data/spec/rails_best_practices/lexicals/remove_tab_check_spec.rb +6 -6
  91. data/spec/rails_best_practices/lexicals/remove_trailing_whitespace_check_spec.rb +6 -6
  92. data/spec/rails_best_practices/prepares/config_prepare_spec.rb +2 -2
  93. data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +18 -10
  94. data/spec/rails_best_practices/prepares/gemfile_prepare_spec.rb +2 -2
  95. data/spec/rails_best_practices/prepares/helper_prepare_spec.rb +3 -3
  96. data/spec/rails_best_practices/prepares/initializer_prepare_spec.rb +3 -3
  97. data/spec/rails_best_practices/prepares/mailer_prepare_spec.rb +2 -2
  98. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +79 -43
  99. data/spec/rails_best_practices/prepares/route_prepare_spec.rb +138 -77
  100. data/spec/rails_best_practices/prepares/schema_prepare_spec.rb +2 -2
  101. data/spec/rails_best_practices/reviews/add_model_virtual_attribute_review_spec.rb +18 -12
  102. data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +28 -22
  103. data/spec/rails_best_practices/reviews/check_destroy_return_value_review_spec.rb +15 -13
  104. data/spec/rails_best_practices/reviews/check_save_return_value_review_spec.rb +31 -21
  105. data/spec/rails_best_practices/reviews/default_scope_is_evil_review_spec.rb +6 -6
  106. data/spec/rails_best_practices/reviews/dry_bundler_in_capistrano_review_spec.rb +5 -5
  107. data/spec/rails_best_practices/reviews/hash_syntax_review_spec.rb +9 -9
  108. data/spec/rails_best_practices/reviews/isolate_seed_data_review_spec.rb +7 -7
  109. data/spec/rails_best_practices/reviews/keep_finders_on_their_own_model_review_spec.rb +9 -9
  110. data/spec/rails_best_practices/reviews/law_of_demeter_review_spec.rb +21 -14
  111. data/spec/rails_best_practices/reviews/move_code_into_controller_review_spec.rb +6 -6
  112. data/spec/rails_best_practices/reviews/move_code_into_helper_review_spec.rb +11 -6
  113. data/spec/rails_best_practices/reviews/move_code_into_model_review_spec.rb +26 -16
  114. data/spec/rails_best_practices/reviews/move_finder_to_named_scope_review_spec.rb +7 -7
  115. data/spec/rails_best_practices/reviews/move_model_logic_into_model_review_spec.rb +9 -7
  116. data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +9 -9
  117. data/spec/rails_best_practices/reviews/not_rescue_exception_review_spec.rb +9 -9
  118. data/spec/rails_best_practices/reviews/not_use_default_route_review_spec.rb +5 -5
  119. data/spec/rails_best_practices/reviews/not_use_time_ago_in_words_review_spec.rb +7 -7
  120. data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +7 -7
  121. data/spec/rails_best_practices/reviews/protect_mass_assignment_review_spec.rb +24 -19
  122. data/spec/rails_best_practices/reviews/remove_empty_helpers_review_spec.rb +6 -6
  123. data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +44 -31
  124. data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +17 -12
  125. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +46 -44
  126. data/spec/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review_spec.rb +10 -8
  127. data/spec/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review_spec.rb +16 -10
  128. data/spec/rails_best_practices/reviews/restrict_auto_generated_routes_review_spec.rb +54 -31
  129. data/spec/rails_best_practices/reviews/simplify_render_in_controllers_review_spec.rb +9 -9
  130. data/spec/rails_best_practices/reviews/simplify_render_in_views_review_spec.rb +13 -13
  131. data/spec/rails_best_practices/reviews/use_before_filter_review_spec.rb +11 -9
  132. data/spec/rails_best_practices/reviews/use_model_association_review_spec.rb +7 -7
  133. data/spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb +21 -17
  134. data/spec/rails_best_practices/reviews/use_observer_review_spec.rb +6 -6
  135. data/spec/rails_best_practices/reviews/use_parentheses_in_method_def_review_spec.rb +9 -7
  136. data/spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb +31 -24
  137. data/spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb +15 -11
  138. data/spec/rails_best_practices/reviews/use_scope_access_review_spec.rb +14 -14
  139. data/spec/rails_best_practices/reviews/use_turbo_sprockets_rails3_review_spec.rb +10 -8
  140. 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 { |method| method.method_name == method_name && method.access_control == access_control }
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 && klass.extend_class_name
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.mark_used if 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.mark_used if 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.publicize if 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 && klass.extend_class_name
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 { |method| method.method_name == method_name && method.access_control == access_control }
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 += if access_control
129
- methods.select { |method| method.access_control == access_control && !method.used }
130
- else
131
- methods.reject(&:used)
132
- end
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
- # Methods of a class.
139
- #
140
- # @param [String] class name
141
- # @return [Array] array of methods
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] = { 'meta' => association_meta, 'class_name' => association_class || association_name.classify }
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 and associations[association_name]
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
- associations = @associations.select { |model, _model_associations| model.gsub('::', '').tableize == table_name }.values.first and
53
- association_meta = associations.select { |name, _meta| name == association_name }.values.first and
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
@@ -3,7 +3,6 @@
3
3
  module RailsBestPractices
4
4
  module Core
5
5
  # Model classes.
6
- class Models < Klasses
7
- end
6
+ class Models < Klasses; end
8
7
  end
9
8
  end
@@ -10,7 +10,7 @@ module RailsBestPractices
10
10
  # @param [String] descendant name
11
11
  def add_module_descendant(module_name, descendant)
12
12
  mod = find { |mod| mod.to_s == module_name }
13
- mod.add_descendant(descendant) if mod
13
+ mod&.add_descendant(descendant)
14
14
  end
15
15
  end
16
16
 
@@ -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 = $1
28
- action_name = $2
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
- # set the base path.
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
- # set the configuration path
38
- #
39
- # @param path [String] path to rbc config file
40
- def self.config_path=(path)
41
- @config_path = path
42
- end
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
- # get the configuration path, if will default to config/rails_best_practices.yml
45
- #
46
- # @return [String] the config path
47
- def self.config_path
48
- custom_config = @config_path || File.join(Runner.base_path, 'config/rails_best_practices.yml')
49
- File.exist?(custom_config) ? custom_config : RailsBestPractices::Analyzer::DEFAULT_CONFIG
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 ||= CodeAnalyzer::CheckingVisitor::Plain.new(checkers: @prepares.select { |checker| checker.is_a? Prepares::GemfilePrepare })
70
- @default_prepare_checker ||= CodeAnalyzer::CheckingVisitor::Default.new(checkers: @prepares.reject { |checker| checker.is_a? Prepares::GemfilePrepare })
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 ||= (@reviews + @lexicals).collect(&:errors).flatten
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
- # parse html template code, erb, haml and slim.
123
- #
124
- # @param [String] filename is the filename of the erb, haml or slim code.
125
- # @param [String] content is the source code of erb, haml or slim file.
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
- # load all prepares.
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
- # load all plugin reviews.
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
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_rel 'inline_disables'
@@ -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
- if actual_line_length > @max_line_length
26
- add_error("line is longer than #{@max_line_length} characters (#{actual_line_length} characters)", filename, line_no)
27
- end
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, yml, json, xml)
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, yml, json, xml)') do |format|
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('-x', '--exclude PATTERNS', "Don't analyze files matching a pattern", '(comma-separated regexp list)') do |list|
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('-o', '--only PATTERNS', 'Analyze files only matching a pattern', '(comma-separated regexp list)') do |list|
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', 'configuration file location (defaults to config/rails_best_practices.yml)'
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(@current_controller_name, action, 'file' => node.file, 'line_number' => node.line_number)
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(current_class_name, method_name, { 'file' => node.file, 'line_number' => node.line_number }, current_access_control)
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| /#{descendant.underscore}/ }
98
+ Reviews::RemoveUnusedMethodsInHelpersReview.interesting_files *descendants.map { |descendant|
99
+ /#{descendant.underscore}/
100
+ }
89
101
  end
90
102
  end
91
103
  end
@@ -13,7 +13,7 @@ module RailsBestPractices
13
13
  def check(_filename, content)
14
14
  content.split("\n").each do |line|
15
15
  if line =~ /([^ ]+) \((\d.*)\)/
16
- @gems << Core::Gem.new($1, $2)
16
+ @gems << Core::Gem.new(Regexp.last_match(1), Regexp.last_match(2))
17
17
  end
18
18
  end
19
19
  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(current_module_name, method_name, { 'file' => node.file, 'line_number' => node.line_number }, current_access_control)
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 the mailer names.
6
+
6
7
  class MailerPrepare < Core::Check
7
8
  include Core::Check::Classable
8
9
 
@@ -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[belongs_to has_one has_many has_and_belongs_to_many embeds_many embeds_one embedded_in many one].freeze
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(current_class_name, method_name, { 'file' => node.file, 'line_number' => node.line_number }, current_access_control)
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(current_class_name, method_name, { 'file' => node.file, 'line_number' => node.line_number }, current_access_control)
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(current_class_name, method_name, { 'file' => node.file, 'line_number' => node.line_number }, current_access_control)
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(current_class_name, "#{method}_with_#{feature}", { 'file' => node.file, 'line_number' => node.line_number }, current_access_control)
88
- @methods.add_method(current_class_name, method.to_s, { 'file' => node.file, 'line_number' => node.line_number }, current_access_control)
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 = arguments.last.hash_value('type').present? ? arguments.last.hash_value('type').to_s : 'String'
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(current_class_name, method_name, { 'file' => node.file, 'line_number' => node.line_number }, current_access_control)
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
- # remember associations, with class to association names.
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