rails_best_practices 1.20.0 → 1.20.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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Gemfile.lock +30 -26
  4. data/Guardfile +2 -0
  5. data/Rakefile +2 -0
  6. data/lib/rails_best_practices.rb +1 -2
  7. data/lib/rails_best_practices/analyzer.rb +56 -46
  8. data/lib/rails_best_practices/core/check.rb +39 -32
  9. data/lib/rails_best_practices/core/checks_loader.rb +8 -6
  10. data/lib/rails_best_practices/core/configs.rb +1 -2
  11. data/lib/rails_best_practices/core/controllers.rb +1 -2
  12. data/lib/rails_best_practices/core/error.rb +1 -1
  13. data/lib/rails_best_practices/core/helpers.rb +1 -2
  14. data/lib/rails_best_practices/core/mailers.rb +1 -2
  15. data/lib/rails_best_practices/core/methods.rb +21 -16
  16. data/lib/rails_best_practices/core/model_associations.rb +9 -4
  17. data/lib/rails_best_practices/core/models.rb +1 -2
  18. data/lib/rails_best_practices/core/modules.rb +1 -1
  19. data/lib/rails_best_practices/core/routes.rb +2 -2
  20. data/lib/rails_best_practices/core/runner.rb +29 -33
  21. data/lib/rails_best_practices/lexicals/long_line_check.rb +7 -3
  22. data/lib/rails_best_practices/option_parser.rb +17 -6
  23. data/lib/rails_best_practices/prepares.rb +1 -1
  24. data/lib/rails_best_practices/prepares/controller_prepare.rb +15 -3
  25. data/lib/rails_best_practices/prepares/gemfile_prepare.rb +1 -1
  26. data/lib/rails_best_practices/prepares/helper_prepare.rb +6 -1
  27. data/lib/rails_best_practices/prepares/initializer_prepare.rb +2 -2
  28. data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -0
  29. data/lib/rails_best_practices/prepares/model_prepare.rb +52 -12
  30. data/lib/rails_best_practices/prepares/route_prepare.rb +16 -10
  31. data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +15 -13
  32. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +34 -29
  33. data/lib/rails_best_practices/reviews/check_destroy_return_value_review.rb +14 -5
  34. data/lib/rails_best_practices/reviews/check_save_return_value_review.rb +19 -8
  35. data/lib/rails_best_practices/reviews/hash_syntax_review.rb +5 -5
  36. data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +4 -4
  37. data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +7 -8
  38. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +6 -6
  39. data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +1 -1
  40. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +6 -7
  41. data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +7 -8
  42. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +12 -10
  43. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +1 -2
  44. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +5 -5
  45. data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +5 -2
  46. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +6 -3
  47. data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +6 -4
  48. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +29 -9
  49. data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +3 -3
  50. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +17 -15
  51. data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +1 -2
  52. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +3 -3
  53. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +2 -1
  54. data/lib/rails_best_practices/reviews/use_model_association_review.rb +5 -5
  55. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +9 -8
  56. data/lib/rails_best_practices/reviews/use_observer_review.rb +9 -9
  57. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +26 -26
  58. data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +8 -7
  59. data/lib/rails_best_practices/reviews/use_scope_access_review.rb +17 -15
  60. data/lib/rails_best_practices/reviews/use_turbo_sprockets_rails3_review.rb +2 -1
  61. data/lib/rails_best_practices/version.rb +1 -1
  62. data/rails_best_practices.gemspec +35 -36
  63. data/spec/fixtures/lib/rails_best_practices/plugins/reviews/not_use_rails_root_review.rb +1 -2
  64. data/spec/rails_best_practices/analyzer_spec.rb +73 -42
  65. data/spec/rails_best_practices/core/check_spec.rb +5 -5
  66. data/spec/rails_best_practices/core/checks_loader_spec.rb +3 -3
  67. data/spec/rails_best_practices/core/configs_spec.rb +1 -1
  68. data/spec/rails_best_practices/core/controllers_spec.rb +1 -1
  69. data/spec/rails_best_practices/core/error_spec.rb +21 -21
  70. data/spec/rails_best_practices/core/except_methods_spec.rb +7 -7
  71. data/spec/rails_best_practices/core/gems_spec.rb +4 -4
  72. data/spec/rails_best_practices/core/helpers_spec.rb +1 -1
  73. data/spec/rails_best_practices/core/klasses_spec.rb +3 -3
  74. data/spec/rails_best_practices/core/mailers_spec.rb +1 -1
  75. data/spec/rails_best_practices/core/methods_spec.rb +6 -6
  76. data/spec/rails_best_practices/core/model_associations_spec.rb +10 -6
  77. data/spec/rails_best_practices/core/model_attributes_spec.rb +4 -4
  78. data/spec/rails_best_practices/core/models_spec.rb +1 -1
  79. data/spec/rails_best_practices/core/modules_spec.rb +5 -5
  80. data/spec/rails_best_practices/core/routes_spec.rb +5 -5
  81. data/spec/rails_best_practices/core/runner_spec.rb +9 -7
  82. data/spec/rails_best_practices/core_ext/erubis_spec.rb +10 -10
  83. data/spec/rails_best_practices/lexicals/long_line_check_spec.rb +11 -10
  84. data/spec/rails_best_practices/lexicals/remove_tab_check_spec.rb +6 -6
  85. data/spec/rails_best_practices/lexicals/remove_trailing_whitespace_check_spec.rb +6 -6
  86. data/spec/rails_best_practices/prepares/config_prepare_spec.rb +2 -2
  87. data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +18 -10
  88. data/spec/rails_best_practices/prepares/gemfile_prepare_spec.rb +2 -2
  89. data/spec/rails_best_practices/prepares/helper_prepare_spec.rb +3 -3
  90. data/spec/rails_best_practices/prepares/initializer_prepare_spec.rb +3 -3
  91. data/spec/rails_best_practices/prepares/mailer_prepare_spec.rb +2 -2
  92. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +79 -43
  93. data/spec/rails_best_practices/prepares/route_prepare_spec.rb +138 -77
  94. data/spec/rails_best_practices/prepares/schema_prepare_spec.rb +2 -2
  95. data/spec/rails_best_practices/reviews/add_model_virtual_attribute_review_spec.rb +18 -12
  96. data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +28 -22
  97. data/spec/rails_best_practices/reviews/check_destroy_return_value_review_spec.rb +15 -13
  98. data/spec/rails_best_practices/reviews/check_save_return_value_review_spec.rb +31 -21
  99. data/spec/rails_best_practices/reviews/default_scope_is_evil_review_spec.rb +6 -6
  100. data/spec/rails_best_practices/reviews/dry_bundler_in_capistrano_review_spec.rb +5 -5
  101. data/spec/rails_best_practices/reviews/hash_syntax_review_spec.rb +9 -9
  102. data/spec/rails_best_practices/reviews/isolate_seed_data_review_spec.rb +7 -7
  103. data/spec/rails_best_practices/reviews/keep_finders_on_their_own_model_review_spec.rb +9 -9
  104. data/spec/rails_best_practices/reviews/law_of_demeter_review_spec.rb +21 -14
  105. data/spec/rails_best_practices/reviews/move_code_into_controller_review_spec.rb +6 -6
  106. data/spec/rails_best_practices/reviews/move_code_into_helper_review_spec.rb +11 -6
  107. data/spec/rails_best_practices/reviews/move_code_into_model_review_spec.rb +26 -16
  108. data/spec/rails_best_practices/reviews/move_finder_to_named_scope_review_spec.rb +7 -7
  109. data/spec/rails_best_practices/reviews/move_model_logic_into_model_review_spec.rb +9 -7
  110. data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +9 -9
  111. data/spec/rails_best_practices/reviews/not_rescue_exception_review_spec.rb +9 -9
  112. data/spec/rails_best_practices/reviews/not_use_default_route_review_spec.rb +5 -5
  113. data/spec/rails_best_practices/reviews/not_use_time_ago_in_words_review_spec.rb +7 -7
  114. data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +7 -7
  115. data/spec/rails_best_practices/reviews/protect_mass_assignment_review_spec.rb +24 -19
  116. data/spec/rails_best_practices/reviews/remove_empty_helpers_review_spec.rb +6 -6
  117. data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +44 -31
  118. data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +17 -12
  119. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +46 -44
  120. data/spec/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review_spec.rb +10 -8
  121. data/spec/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review_spec.rb +16 -10
  122. data/spec/rails_best_practices/reviews/restrict_auto_generated_routes_review_spec.rb +54 -31
  123. data/spec/rails_best_practices/reviews/simplify_render_in_controllers_review_spec.rb +9 -9
  124. data/spec/rails_best_practices/reviews/simplify_render_in_views_review_spec.rb +13 -13
  125. data/spec/rails_best_practices/reviews/use_before_filter_review_spec.rb +11 -9
  126. data/spec/rails_best_practices/reviews/use_model_association_review_spec.rb +7 -7
  127. data/spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb +21 -17
  128. data/spec/rails_best_practices/reviews/use_observer_review_spec.rb +6 -6
  129. data/spec/rails_best_practices/reviews/use_parentheses_in_method_def_review_spec.rb +9 -7
  130. data/spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb +31 -24
  131. data/spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb +15 -11
  132. data/spec/rails_best_practices/reviews/use_scope_access_review_spec.rb +14 -14
  133. data/spec/rails_best_practices/reviews/use_turbo_sprockets_rails3_review_spec.rb +10 -8
  134. metadata +7 -7
@@ -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
@@ -38,10 +38,9 @@ module RailsBestPractices
38
38
  else
39
39
  action_names = [second_argument.hash_value('to').to_s]
40
40
  end
41
- elsif first_argument.sexp_type == :symbol_literal && second_argument.try(:sexp_type) && \
41
+ elsif first_argument.sexp_type == :symbol_literal && second_argument.try(:sexp_type) &&
42
42
  second_argument.sexp_type == :symbol_literal
43
- action_names = node.arguments.all.select \
44
- { |arg| arg.sexp_type == :symbol_literal }.map(&:to_s)
43
+ action_names = node.arguments.all.select { |arg| arg.sexp_type == :symbol_literal }.map(&:to_s)
45
44
  else
46
45
  action_names = [first_argument.to_s]
47
46
  end
@@ -84,7 +83,10 @@ module RailsBestPractices
84
83
  action_name = options.hash_value('action').present? ? options.hash_value('action').to_s : '*'
85
84
  @routes.add_route(current_namespaces, controller_name, action_name)
86
85
  else
87
- route_node = options.hash_values.find { |value_node| value_node.sexp_type == :string_literal && value_node.to_s.include?('#') }
86
+ route_node =
87
+ options.hash_values.find do |value_node|
88
+ value_node.sexp_type == :string_literal && value_node.to_s.include?('#')
89
+ end
88
90
  if route_node.present?
89
91
  controller_name, action_name = route_node.to_s.split('#')
90
92
  @routes.add_route(current_namespaces, controller_name.underscore, action_name)
@@ -101,7 +103,10 @@ module RailsBestPractices
101
103
  options = node.arguments.all.last
102
104
  case options.sexp_type
103
105
  when :bare_assoc_hash
104
- route_node = options.hash_values.find { |value_node| value_node.sexp_type == :string_literal && value_node.to_s.include?('#') }
106
+ route_node =
107
+ options.hash_values.find do |value_node|
108
+ value_node.sexp_type == :string_literal && value_node.to_s.include?('#')
109
+ end
105
110
  if route_node.present?
106
111
  controller_name, action_name = route_node.to_s.split('#')
107
112
  @routes.add_route(current_namespaces, controller_name.underscore, action_name)
@@ -145,11 +150,12 @@ module RailsBestPractices
145
150
  if node.arguments.all.last.hash_value('module').present?
146
151
  @namespaces << node.arguments.all.last.hash_value('module').to_s
147
152
  end
148
- if node.arguments.all.last.hash_value('controller').present?
149
- @controller_name = [:scope, node.arguments.all.last.hash_value('controller').to_s]
150
- else
151
- @controller_name = @controller_name.try(:first) == :scope ? @controller_name : nil
152
- end
153
+ @controller_name =
154
+ if node.arguments.all.last.hash_value('controller').present?
155
+ [:scope, node.arguments.all.last.hash_value('controller').to_s]
156
+ else
157
+ @controller_name.try(:first) == :scope ? @controller_name : nil
158
+ end
153
159
  when 'with_options'
154
160
  argument = node.arguments.all.last
155
161
  if argument.sexp_type == :bare_assoc_hash && argument.hash_value('controller').present?
@@ -46,8 +46,8 @@ module RailsBestPractices
46
46
 
47
47
  private
48
48
 
49
- # check an attribute assignment node, if there is a array reference node in the right value of assignment node,
50
- # then remember this attribute assignment.
49
+ # check an attribute assignment node, if there is a array reference node in the right value of assignment node,
50
+ # then remember this attribute assignment.
51
51
  def assign(node)
52
52
  left_value = node.left_value
53
53
  right_value = node.right_value
@@ -59,34 +59,36 @@ module RailsBestPractices
59
59
  end
60
60
  end
61
61
 
62
- # check a call node with message "save" or "save!",
63
- # if there exists an attribute assignment for the receiver of this call node,
64
- # and if the arguments of this attribute assignments has duplicated entries (different message and same arguments),
65
- # then this node needs to add a virtual attribute.
62
+ # check a call node with message "save" or "save!",
63
+ # if there exists an attribute assignment for the receiver of this call node,
64
+ # and if the arguments of this attribute assignments has duplicated entries (different message and same arguments),
65
+ # then this node needs to add a virtual attribute.
66
66
  def call_assignment(node)
67
67
  if ['save', 'save!'].include? node.message.to_s
68
68
  receiver = node.receiver.to_s
69
- add_error "add model virtual attribute (for #{receiver})" if params_dup?(assignments(receiver).collect { |h| h[:arguments] })
69
+ add_error "add model virtual attribute (for #{receiver})" if params_dup?(
70
+ assignments(receiver).collect { |h| h[:arguments] }
71
+ )
70
72
  end
71
73
  end
72
74
 
73
- # if the nodes are duplicated.
75
+ # if the nodes are duplicated.
74
76
  def params_dup?(nodes)
75
77
  return false if nodes.nil?
76
78
 
77
79
  !dups(nodes).empty?
78
80
  end
79
81
 
80
- # get the assignments of receiver.
82
+ # get the assignments of receiver.
81
83
  def assignments(receiver)
82
84
  @assignments[receiver] ||= []
83
85
  end
84
86
 
85
- # Get the duplicate entries from an Enumerable.
86
- #
87
- # @return [Enumerable] the duplicate entries.
87
+ # Get the duplicate entries from an Enumerable.
88
+ #
89
+ # @return [Enumerable] the duplicate entries.
88
90
  def dups(nodes)
89
- nodes.each_with_object({}) { |v, h| h[v] = h[v].to_i + 1; }.reject { |_k, v| v == 1 }.keys
91
+ nodes.each_with_object({}) { |v, h| h[v] = h[v].to_i + 1 }.reject { |_k, v| v == 1 }.keys
90
92
  end
91
93
  end
92
94
  end
@@ -75,18 +75,20 @@ module RailsBestPractices
75
75
  @foreign_keys.each do |table, foreign_key|
76
76
  table_node = @table_nodes[table]
77
77
  foreign_key.each do |column|
78
- if not_indexed?(table, column)
79
- add_error "always add db index (#{table} => [#{Array(column).join(', ')}])", table_node.file, table_node.line_number
80
- end
78
+ next unless not_indexed?(table, column)
79
+
80
+ add_error "always add db index (#{table} => [#{Array(column).join(', ')}])",
81
+ table_node.file,
82
+ table_node.line_number
81
83
  end
82
84
  end
83
85
  end
84
86
 
85
87
  private
86
88
 
87
- # remember the node as index columns, when used outside a table
88
- # block, i.e.
89
- # add_index :table_name, :column_name
89
+ # remember the node as index columns, when used outside a table
90
+ # block, i.e.
91
+ # add_index :table_name, :column_name
90
92
  def remember_index_columns_outside_table(node)
91
93
  table_name = node.arguments.all.first.to_s
92
94
  index_column = node.arguments.all[1].to_object
@@ -95,9 +97,9 @@ module RailsBestPractices
95
97
  @index_columns[table_name] << index_column
96
98
  end
97
99
 
98
- # remember the node as index columns, when used inside a table
99
- # block, i.e.
100
- # t.index [:column_name, ...]
100
+ # remember the node as index columns, when used inside a table
101
+ # block, i.e.
102
+ # t.index [:column_name, ...]
101
103
  def remember_index_columns_inside_table(node)
102
104
  table_name = @table_name
103
105
  index_column = node.arguments.all.first.to_object
@@ -106,31 +108,33 @@ module RailsBestPractices
106
108
  @index_columns[table_name] << index_column
107
109
  end
108
110
 
109
- # remember table nodes
111
+ # remember table nodes
110
112
  def remember_table_nodes(node)
111
113
  @table_name = node.arguments.all.first.to_s
112
114
  @table_nodes[@table_name] = node
113
115
  end
114
116
 
115
- # remember foreign key columns
117
+ # remember foreign key columns
116
118
  def remember_foreign_key_columns(node)
117
119
  table_name = @table_name
118
120
  foreign_key_column = node.arguments.all.first.to_s
119
121
  @foreign_keys[table_name] ||= []
120
122
  if foreign_key_column =~ /(.*?)_id$/
121
- if @foreign_keys[table_name].delete("#{$1}_type")
122
- @foreign_keys[table_name] << ["#{$1}_id", "#{$1}_type"]
123
- else
124
- @foreign_keys[table_name] << foreign_key_column
125
- end
123
+ @foreign_keys[table_name] <<
124
+ if @foreign_keys[table_name].delete("#{Regexp.last_match(1)}_type")
125
+ ["#{Regexp.last_match(1)}_id", "#{Regexp.last_match(1)}_type"]
126
+ else
127
+ foreign_key_column
128
+ end
126
129
  foreign_id_column = foreign_key_column
127
130
  elsif foreign_key_column =~ /(.*?)_type$/
128
- if @foreign_keys[table_name].delete("#{$1}_id")
129
- @foreign_keys[table_name] << ["#{$1}_id", "#{$1}_type"]
130
- else
131
- @foreign_keys[table_name] << foreign_key_column
132
- end
133
- foreign_id_column = "#{$1}_id"
131
+ @foreign_keys[table_name] <<
132
+ if @foreign_keys[table_name].delete("#{Regexp.last_match(1)}_id")
133
+ ["#{Regexp.last_match(1)}_id", "#{Regexp.last_match(1)}_type"]
134
+ else
135
+ foreign_key_column
136
+ end
137
+ foreign_id_column = "#{Regexp.last_match(1)}_id"
134
138
  end
135
139
 
136
140
  if foreign_id_column
@@ -142,7 +146,7 @@ module RailsBestPractices
142
146
  end
143
147
  end
144
148
 
145
- # remove the non foreign keys without corresponding tables.
149
+ # remove the non foreign keys without corresponding tables.
146
150
  def remove_table_not_exist_foreign_keys
147
151
  @foreign_keys.each do |table, foreign_keys|
148
152
  foreign_keys.delete_if do |key|
@@ -154,21 +158,22 @@ module RailsBestPractices
154
158
  end
155
159
  end
156
160
 
157
- # remove the non foreign keys with only _type column.
161
+ # remove the non foreign keys with only _type column.
158
162
  def remove_only_type_foreign_keys
159
163
  @foreign_keys.each do |_table, foreign_keys|
160
164
  foreign_keys.delete_if { |key| key.is_a?(String) && key =~ /_type$/ }
161
165
  end
162
166
  end
163
167
 
164
- # combine polymorphic foreign keys, e.g.
165
- # [tagger_id], [tagger_type] => [tagger_id, tagger_type]
168
+ # combine polymorphic foreign keys, e.g.
169
+ # [tagger_id], [tagger_type] => [tagger_id, tagger_type]
166
170
  def combine_polymorphic_foreign_keys
167
171
  @index_columns.each do |_table, foreign_keys|
168
172
  foreign_id_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_id/ }
169
173
  foreign_type_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_type/ }
170
174
  foreign_id_keys.each do |id_key|
171
- next unless type_key = foreign_type_keys.detect { |type_key| type_key.first == id_key.first.sub(/_id/, '') + '_type' }
175
+ next unless type_key =
176
+ foreign_type_keys.detect { |type_key| type_key.first == id_key.first.sub(/_id/, '') + '_type' }
172
177
 
173
178
  foreign_keys.delete(id_key)
174
179
  foreign_keys.delete(type_key)
@@ -177,13 +182,13 @@ module RailsBestPractices
177
182
  end
178
183
  end
179
184
 
180
- # check if the table's column is indexed.
185
+ # check if the table's column is indexed.
181
186
  def not_indexed?(table, column)
182
187
  index_columns = @index_columns[table]
183
188
  !index_columns || index_columns.none? { |e| greater_than_or_equal(Array(e), Array(column)) }
184
189
  end
185
190
 
186
- # check if more_array is greater than less_array or equal to less_array.
191
+ # check if more_array is greater than less_array or equal to less_array.
187
192
  def greater_than_or_equal(more_array, less_array)
188
193
  more_size = more_array.size
189
194
  less_size = less_array.size
@@ -10,7 +10,17 @@ module RailsBestPractices
10
10
  # Check all "save" calls to check the return value is used by a node we have visited.
11
11
  class CheckDestroyReturnValueReview < Review
12
12
  include Classable
13
- interesting_nodes :call, :command_call, :method_add_arg, :if, :ifop, :elsif, :unless, :if_mod, :unless_mod, :assign, :binary
13
+ interesting_nodes :call,
14
+ :command_call,
15
+ :method_add_arg,
16
+ :if,
17
+ :ifop,
18
+ :elsif,
19
+ :unless,
20
+ :if_mod,
21
+ :unless_mod,
22
+ :assign,
23
+ :binary
14
24
  interesting_files ALL_FILES
15
25
 
16
26
  add_callback :start_if, :start_ifop, :start_elsif, :start_unless, :start_if_mod, :start_unless_mod do |node|
@@ -21,14 +31,13 @@ module RailsBestPractices
21
31
  @used_return_value_of = node.right_value
22
32
  end
23
33
 
34
+ # Consider anything used in an expression like "A or B" as used
24
35
  add_callback :start_binary do |node|
25
- # Consider anything used in an expression like "A or B" as used
26
36
  if %w[&& || and or].include?(node[2].to_s)
27
37
  all_conditions = node.all_conditions
28
38
  # if our current binary is a subset of the @used_return_value_of
29
39
  # then don't overwrite it
30
- already_included = @used_return_value_of &&
31
- (all_conditions - @used_return_value_of).empty?
40
+ already_included = @used_return_value_of && (all_conditions - @used_return_value_of).empty?
32
41
 
33
42
  @used_return_value_of = node.all_conditions unless already_included
34
43
  end
@@ -37,7 +46,7 @@ module RailsBestPractices
37
46
  def return_value_is_used?(node)
38
47
  return false unless @used_return_value_of
39
48
 
40
- node == @used_return_value_of or @used_return_value_of.include?(node)
49
+ (node == @used_return_value_of) || @used_return_value_of.include?(node)
41
50
  end
42
51
 
43
52
  def model_classnames
@@ -14,7 +14,17 @@ module RailsBestPractices
14
14
  # Check all "save" calls to check the return value is used by a node we have visited.
15
15
  class CheckSaveReturnValueReview < Review
16
16
  include Classable
17
- interesting_nodes :call, :command_call, :method_add_arg, :if, :ifop, :elsif, :unless, :if_mod, :unless_mod, :assign, :binary
17
+ interesting_nodes :call,
18
+ :command_call,
19
+ :method_add_arg,
20
+ :if,
21
+ :ifop,
22
+ :elsif,
23
+ :unless,
24
+ :if_mod,
25
+ :unless_mod,
26
+ :assign,
27
+ :binary
18
28
  interesting_files ALL_FILES
19
29
  url 'https://rails-bestpractices.com/posts/2012/11/02/check-the-return-value-of-save-otherwise-use-save/'
20
30
 
@@ -26,14 +36,13 @@ module RailsBestPractices
26
36
  @used_return_value_of = node.right_value
27
37
  end
28
38
 
39
+ # Consider anything used in an expression like "A or B" as used
29
40
  add_callback :start_binary do |node|
30
- # Consider anything used in an expression like "A or B" as used
31
41
  if %w[&& || and or].include?(node[2].to_s)
32
42
  all_conditions = node.all_conditions
33
43
  # if our current binary is a subset of the @used_return_value_of
34
44
  # then don't overwrite it
35
- already_included = @used_return_value_of &&
36
- (all_conditions - @used_return_value_of).empty?
45
+ already_included = @used_return_value_of && (all_conditions - @used_return_value_of).empty?
37
46
 
38
47
  @used_return_value_of = node.all_conditions unless already_included
39
48
  end
@@ -42,7 +51,7 @@ module RailsBestPractices
42
51
  def return_value_is_used?(node)
43
52
  return false unless @used_return_value_of
44
53
 
45
- node == @used_return_value_of or @used_return_value_of.include?(node)
54
+ (node == @used_return_value_of) || @used_return_value_of.include?(node)
46
55
  end
47
56
 
48
57
  def model_classnames
@@ -58,9 +67,11 @@ module RailsBestPractices
58
67
  end
59
68
  elsif message == 'create'
60
69
  # We're only interested in 'create' calls on model classes:
61
- possible_receiver_classes = [node.receiver.to_s] + classable_modules.map do |mod|
62
- "#{mod}::#{node.receiver}"
63
- end
70
+ possible_receiver_classes =
71
+ [node.receiver.to_s] +
72
+ classable_modules.map do |mod|
73
+ "#{mod}::#{node.receiver}"
74
+ end
64
75
  unless (possible_receiver_classes & model_classnames).empty?
65
76
  add_error "use 'create!' instead of 'create' as the latter may not always save"
66
77
  end