rails_best_practices 1.19.3 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -1
  3. data/.travis.yml +2 -3
  4. data/CHANGELOG.md +10 -11
  5. data/Gemfile +3 -5
  6. data/Gemfile.lock +125 -0
  7. data/Guardfile +2 -0
  8. data/README.md +5 -1
  9. data/Rakefile +2 -17
  10. data/assets/result.html.erb +2 -0
  11. data/lib/rails_best_practices.rb +4 -2
  12. data/lib/rails_best_practices/analyzer.rb +63 -51
  13. data/lib/rails_best_practices/cli.rb +22 -0
  14. data/lib/rails_best_practices/command.rb +1 -131
  15. data/lib/rails_best_practices/core/check.rb +63 -55
  16. data/lib/rails_best_practices/core/checks_loader.rb +24 -23
  17. data/lib/rails_best_practices/core/configs.rb +1 -2
  18. data/lib/rails_best_practices/core/controllers.rb +1 -2
  19. data/lib/rails_best_practices/core/error.rb +1 -1
  20. data/lib/rails_best_practices/core/helpers.rb +1 -2
  21. data/lib/rails_best_practices/core/mailers.rb +1 -2
  22. data/lib/rails_best_practices/core/methods.rb +27 -21
  23. data/lib/rails_best_practices/core/model_associations.rb +10 -5
  24. data/lib/rails_best_practices/core/models.rb +1 -2
  25. data/lib/rails_best_practices/core/modules.rb +1 -1
  26. data/lib/rails_best_practices/core/routes.rb +2 -2
  27. data/lib/rails_best_practices/core/runner.rb +87 -72
  28. data/lib/rails_best_practices/inline_disables.rb +3 -0
  29. data/lib/rails_best_practices/inline_disables/comment_ripper.rb +19 -0
  30. data/lib/rails_best_practices/inline_disables/inline_disable.rb +50 -0
  31. data/lib/rails_best_practices/lexicals/long_line_check.rb +7 -3
  32. data/lib/rails_best_practices/option_parser.rb +156 -0
  33. data/lib/rails_best_practices/prepares.rb +1 -1
  34. data/lib/rails_best_practices/prepares/controller_prepare.rb +23 -17
  35. data/lib/rails_best_practices/prepares/gemfile_prepare.rb +2 -2
  36. data/lib/rails_best_practices/prepares/helper_prepare.rb +6 -1
  37. data/lib/rails_best_practices/prepares/initializer_prepare.rb +3 -3
  38. data/lib/rails_best_practices/prepares/mailer_prepare.rb +2 -1
  39. data/lib/rails_best_practices/prepares/model_prepare.rb +63 -23
  40. data/lib/rails_best_practices/prepares/route_prepare.rb +28 -21
  41. data/lib/rails_best_practices/prepares/schema_prepare.rb +1 -1
  42. data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +38 -34
  43. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +94 -88
  44. data/lib/rails_best_practices/reviews/check_destroy_return_value_review.rb +15 -5
  45. data/lib/rails_best_practices/reviews/check_save_return_value_review.rb +20 -8
  46. data/lib/rails_best_practices/reviews/default_scope_is_evil_review.rb +1 -1
  47. data/lib/rails_best_practices/reviews/dry_bundler_in_capistrano_review.rb +1 -1
  48. data/lib/rails_best_practices/reviews/hash_syntax_review.rb +16 -16
  49. data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +12 -12
  50. data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +10 -11
  51. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +25 -24
  52. data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +4 -4
  53. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +9 -10
  54. data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +10 -11
  55. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +24 -22
  56. data/lib/rails_best_practices/reviews/not_rescue_exception_review.rb +1 -1
  57. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +1 -2
  58. data/lib/rails_best_practices/reviews/not_use_time_ago_in_words_review.rb +1 -1
  59. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +8 -8
  60. data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +35 -32
  61. data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +4 -4
  62. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +20 -17
  63. data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +12 -10
  64. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +38 -18
  65. data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +11 -11
  66. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +77 -74
  67. data/lib/rails_best_practices/reviews/review.rb +2 -1
  68. data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +2 -3
  69. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +12 -12
  70. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +14 -10
  71. data/lib/rails_best_practices/reviews/use_model_association_review.rb +15 -15
  72. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +24 -22
  73. data/lib/rails_best_practices/reviews/use_observer_review.rb +28 -28
  74. data/lib/rails_best_practices/reviews/use_parentheses_in_method_def_review.rb +6 -6
  75. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +63 -60
  76. data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +9 -8
  77. data/lib/rails_best_practices/reviews/use_scope_access_review.rb +16 -14
  78. data/lib/rails_best_practices/reviews/use_turbo_sprockets_rails3_review.rb +2 -1
  79. data/lib/rails_best_practices/version.rb +1 -1
  80. data/rails_best_practices.gemspec +48 -49
  81. data/spec/fixtures/lib/rails_best_practices/plugins/reviews/not_use_rails_root_review.rb +1 -2
  82. data/spec/rails_best_practices/analyzer_spec.rb +73 -42
  83. data/spec/rails_best_practices/core/check_spec.rb +5 -5
  84. data/spec/rails_best_practices/core/checks_loader_spec.rb +3 -3
  85. data/spec/rails_best_practices/core/configs_spec.rb +1 -1
  86. data/spec/rails_best_practices/core/controllers_spec.rb +1 -1
  87. data/spec/rails_best_practices/core/error_spec.rb +21 -18
  88. data/spec/rails_best_practices/core/except_methods_spec.rb +7 -7
  89. data/spec/rails_best_practices/core/gems_spec.rb +4 -4
  90. data/spec/rails_best_practices/core/helpers_spec.rb +1 -1
  91. data/spec/rails_best_practices/core/klasses_spec.rb +3 -3
  92. data/spec/rails_best_practices/core/mailers_spec.rb +1 -1
  93. data/spec/rails_best_practices/core/methods_spec.rb +6 -6
  94. data/spec/rails_best_practices/core/model_associations_spec.rb +10 -6
  95. data/spec/rails_best_practices/core/model_attributes_spec.rb +4 -4
  96. data/spec/rails_best_practices/core/models_spec.rb +1 -1
  97. data/spec/rails_best_practices/core/modules_spec.rb +5 -5
  98. data/spec/rails_best_practices/core/routes_spec.rb +5 -5
  99. data/spec/rails_best_practices/core/runner_spec.rb +9 -7
  100. data/spec/rails_best_practices/core_ext/erubis_spec.rb +10 -10
  101. data/spec/rails_best_practices/inline_disables/inline_disable_spec.rb +62 -0
  102. data/spec/rails_best_practices/lexicals/long_line_check_spec.rb +32 -31
  103. data/spec/rails_best_practices/lexicals/remove_tab_check_spec.rb +6 -6
  104. data/spec/rails_best_practices/lexicals/remove_trailing_whitespace_check_spec.rb +6 -6
  105. data/spec/rails_best_practices/prepares/config_prepare_spec.rb +2 -2
  106. data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +18 -10
  107. data/spec/rails_best_practices/prepares/gemfile_prepare_spec.rb +17 -17
  108. data/spec/rails_best_practices/prepares/helper_prepare_spec.rb +3 -3
  109. data/spec/rails_best_practices/prepares/initializer_prepare_spec.rb +3 -3
  110. data/spec/rails_best_practices/prepares/mailer_prepare_spec.rb +2 -2
  111. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +79 -43
  112. data/spec/rails_best_practices/prepares/route_prepare_spec.rb +141 -76
  113. data/spec/rails_best_practices/prepares/schema_prepare_spec.rb +2 -2
  114. data/spec/rails_best_practices/reviews/add_model_virtual_attribute_review_spec.rb +18 -12
  115. data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +28 -22
  116. data/spec/rails_best_practices/reviews/check_destroy_return_value_review_spec.rb +15 -13
  117. data/spec/rails_best_practices/reviews/check_save_return_value_review_spec.rb +31 -21
  118. data/spec/rails_best_practices/reviews/default_scope_is_evil_review_spec.rb +6 -6
  119. data/spec/rails_best_practices/reviews/dry_bundler_in_capistrano_review_spec.rb +5 -5
  120. data/spec/rails_best_practices/reviews/hash_syntax_review_spec.rb +13 -13
  121. data/spec/rails_best_practices/reviews/isolate_seed_data_review_spec.rb +7 -7
  122. data/spec/rails_best_practices/reviews/keep_finders_on_their_own_model_review_spec.rb +9 -9
  123. data/spec/rails_best_practices/reviews/law_of_demeter_review_spec.rb +29 -22
  124. data/spec/rails_best_practices/reviews/move_code_into_controller_review_spec.rb +6 -6
  125. data/spec/rails_best_practices/reviews/move_code_into_helper_review_spec.rb +11 -6
  126. data/spec/rails_best_practices/reviews/move_code_into_model_review_spec.rb +32 -22
  127. data/spec/rails_best_practices/reviews/move_finder_to_named_scope_review_spec.rb +7 -7
  128. data/spec/rails_best_practices/reviews/move_model_logic_into_model_review_spec.rb +9 -7
  129. data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +9 -9
  130. data/spec/rails_best_practices/reviews/not_rescue_exception_review_spec.rb +9 -9
  131. data/spec/rails_best_practices/reviews/not_use_default_route_review_spec.rb +5 -5
  132. data/spec/rails_best_practices/reviews/not_use_time_ago_in_words_review_spec.rb +7 -7
  133. data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +7 -7
  134. data/spec/rails_best_practices/reviews/protect_mass_assignment_review_spec.rb +24 -17
  135. data/spec/rails_best_practices/reviews/remove_empty_helpers_review_spec.rb +6 -6
  136. data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +47 -32
  137. data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +21 -14
  138. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +57 -53
  139. data/spec/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review_spec.rb +10 -8
  140. data/spec/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review_spec.rb +20 -14
  141. data/spec/rails_best_practices/reviews/restrict_auto_generated_routes_review_spec.rb +54 -31
  142. data/spec/rails_best_practices/reviews/simplify_render_in_controllers_review_spec.rb +9 -9
  143. data/spec/rails_best_practices/reviews/simplify_render_in_views_review_spec.rb +13 -13
  144. data/spec/rails_best_practices/reviews/use_before_filter_review_spec.rb +11 -9
  145. data/spec/rails_best_practices/reviews/use_model_association_review_spec.rb +7 -7
  146. data/spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb +35 -31
  147. data/spec/rails_best_practices/reviews/use_observer_review_spec.rb +6 -6
  148. data/spec/rails_best_practices/reviews/use_parentheses_in_method_def_review_spec.rb +10 -8
  149. data/spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb +31 -24
  150. data/spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb +15 -11
  151. data/spec/rails_best_practices/reviews/use_scope_access_review_spec.rb +14 -14
  152. data/spec/rails_best_practices/reviews/use_turbo_sprockets_rails3_review_spec.rb +61 -59
  153. metadata +21 -14
@@ -27,21 +27,20 @@ module RailsBestPractices
27
27
  first_argument = node.arguments.all.first
28
28
  second_argument = node.arguments.all[1]
29
29
  if @controller_names.last
30
- if :bare_assoc_hash == first_argument.sexp_type
30
+ if first_argument.sexp_type == :bare_assoc_hash
31
31
  action_names = [first_argument.hash_values.first.to_s]
32
- elsif :array == first_argument.sexp_type
32
+ elsif first_argument.sexp_type == :array
33
33
  action_names = first_argument.array_values.map(&:to_s)
34
- elsif :bare_assoc_hash == second_argument.try(:sexp_type) && second_argument.hash_value('to').present?
35
- if :string_literal == second_argument.hash_value('to').sexp_type
34
+ elsif second_argument.try(:sexp_type) == :bare_assoc_hash && second_argument.hash_value('to').present?
35
+ if second_argument.hash_value('to').sexp_type == :string_literal
36
36
  controller_name, action_name = second_argument.hash_value('to').to_s.split('#')
37
37
  action_names = [action_name]
38
38
  else
39
39
  action_names = [second_argument.hash_value('to').to_s]
40
40
  end
41
- elsif :symbol_literal == first_argument.sexp_type && second_argument.try(:sexp_type) && \
42
- :symbol_literal == second_argument.sexp_type
43
- action_names = node.arguments.all.select \
44
- { |arg| :symbol_literal == arg.sexp_type }.map(&:to_s)
41
+ elsif first_argument.sexp_type == :symbol_literal && second_argument.try(:sexp_type) &&
42
+ second_argument.sexp_type == :symbol_literal
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
@@ -49,18 +48,18 @@ module RailsBestPractices
49
48
  @routes.add_route(current_namespaces, current_controller_name, action_name)
50
49
  end
51
50
  else
52
- if :bare_assoc_hash == first_argument.sexp_type
51
+ if first_argument.sexp_type == :bare_assoc_hash
53
52
  route_node = first_argument.hash_values.first
54
53
  # do not parse redirect block
55
- if :method_add_arg != route_node.sexp_type
54
+ if route_node.sexp_type != :method_add_arg
56
55
  controller_name, action_name = route_node.to_s.split('#')
57
56
  @routes.add_route(current_namespaces, controller_name.underscore, action_name)
58
57
  end
59
- elsif :array == first_argument.sexp_type
58
+ elsif first_argument.sexp_type == :array
60
59
  first_argument.array_values.map(&:to_s).each do |action_node|
61
60
  @routes.add_route(current_namespaces, controller_name, action_node.to_s)
62
61
  end
63
- elsif :bare_assoc_hash == second_argument.try(:sexp_type)
62
+ elsif second_argument.try(:sexp_type) == :bare_assoc_hash
64
63
  if second_argument.hash_value('to').present?
65
64
  controller_name, action_name = second_argument.hash_value('to').to_s.split('#')
66
65
  else
@@ -78,12 +77,16 @@ module RailsBestPractices
78
77
  case options.sexp_type
79
78
  when :bare_assoc_hash
80
79
  if options.hash_value('controller').present?
81
- return if :regexp_literal == options.hash_value('controller').sexp_type
80
+ return if options.hash_value('controller').sexp_type == :regexp_literal
81
+
82
82
  controller_name = options.hash_value('controller').to_s
83
83
  action_name = options.hash_value('action').present? ? options.hash_value('action').to_s : '*'
84
84
  @routes.add_route(current_namespaces, controller_name, action_name)
85
85
  else
86
- route_node = options.hash_values.find { |value_node| :string_literal == value_node.sexp_type && 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
87
90
  if route_node.present?
88
91
  controller_name, action_name = route_node.to_s.split('#')
89
92
  @routes.add_route(current_namespaces, controller_name.underscore, action_name)
@@ -100,7 +103,10 @@ module RailsBestPractices
100
103
  options = node.arguments.all.last
101
104
  case options.sexp_type
102
105
  when :bare_assoc_hash
103
- route_node = options.hash_values.find { |value_node| :string_literal == value_node.sexp_type && 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
104
110
  if route_node.present?
105
111
  controller_name, action_name = route_node.to_s.split('#')
106
112
  @routes.add_route(current_namespaces, controller_name.underscore, action_name)
@@ -144,14 +150,15 @@ module RailsBestPractices
144
150
  if node.arguments.all.last.hash_value('module').present?
145
151
  @namespaces << node.arguments.all.last.hash_value('module').to_s
146
152
  end
147
- if node.arguments.all.last.hash_value('controller').present?
148
- @controller_name = [:scope, node.arguments.all.last.hash_value('controller').to_s]
149
- else
150
- @controller_name = @controller_name.try(:first) == :scope ? @controller_name : nil
151
- 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
152
159
  when 'with_options'
153
160
  argument = node.arguments.all.last
154
- if :bare_assoc_hash == argument.sexp_type && argument.hash_value('controller').present?
161
+ if argument.sexp_type == :bare_assoc_hash && argument.hash_value('controller').present?
155
162
  @controller_name = [:with_option, argument.hash_value('controller').to_s]
156
163
  end
157
164
  else
@@ -15,7 +15,7 @@ module RailsBestPractices
15
15
  end
16
16
 
17
17
  add_callback :start_command do |node|
18
- if 'create_table' == node.message.to_s
18
+ if node.message.to_s == 'create_table'
19
19
  @last_klazz = node.arguments.all.first.to_s.classify
20
20
  end
21
21
  end
@@ -46,46 +46,50 @@ 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.
51
- def assign(node)
52
- left_value = node.left_value
53
- right_value = node.right_value
54
- return unless :field == left_value.sexp_type && :call == right_value.sexp_type
55
- aref_node = right_value.grep_node(sexp_type: :aref)
56
- if aref_node
57
- assignments(left_value.receiver.to_s) << { message: left_value.message.to_s, arguments: aref_node.to_s }
58
- end
59
- end
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
+ def assign(node)
52
+ left_value = node.left_value
53
+ right_value = node.right_value
54
+ return unless left_value.sexp_type == :field && right_value.sexp_type == :call
60
55
 
61
- # check a call node with message "save" or "save!",
62
- # if there exists an attribute assignment for the receiver of this call node,
63
- # and if the arguments of this attribute assignments has duplicated entries (different message and same arguments),
64
- # then this node needs to add a virtual attribute.
65
- def call_assignment(node)
66
- if ['save', 'save!'].include? node.message.to_s
67
- receiver = node.receiver.to_s
68
- add_error "add model virtual attribute (for #{receiver})" if params_dup?(assignments(receiver).collect { |h| h[:arguments] })
69
- end
56
+ aref_node = right_value.grep_node(sexp_type: :aref)
57
+ if aref_node
58
+ assignments(left_value.receiver.to_s) << { message: left_value.message.to_s, arguments: aref_node.to_s }
70
59
  end
60
+ end
71
61
 
72
- # if the nodes are duplicated.
73
- def params_dup?(nodes)
74
- return false if nodes.nil?
75
- !dups(nodes).empty?
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
+ def call_assignment(node)
67
+ if ['save', 'save!'].include? node.message.to_s
68
+ receiver = node.receiver.to_s
69
+ add_error "add model virtual attribute (for #{receiver})" if params_dup?(
70
+ assignments(receiver).collect { |h| h[:arguments] }
71
+ )
76
72
  end
73
+ end
77
74
 
78
- # get the assignments of receiver.
79
- def assignments(receiver)
80
- @assignments[receiver] ||= []
81
- end
75
+ # if the nodes are duplicated.
76
+ def params_dup?(nodes)
77
+ return false if nodes.nil?
82
78
 
83
- # Get the duplicate entries from an Enumerable.
84
- #
85
- # @return [Enumerable] the duplicate entries.
86
- def dups(nodes)
87
- nodes.inject({}) { |h, v| h[v] = h[v].to_i + 1; h }.reject { |_k, v| v == 1 }.keys
88
- end
79
+ !dups(nodes).empty?
80
+ end
81
+
82
+ # get the assignments of receiver.
83
+ def assignments(receiver)
84
+ @assignments[receiver] ||= []
85
+ end
86
+
87
+ # Get the duplicate entries from an Enumerable.
88
+ #
89
+ # @return [Enumerable] the duplicate entries.
90
+ def dups(nodes)
91
+ nodes.each_with_object({}) { |v, h| h[v] = h[v].to_i + 1 }.reject { |_k, v| v == 1 }.keys
92
+ end
89
93
  end
90
94
  end
91
95
  end
@@ -11,7 +11,7 @@ module RailsBestPractices
11
11
  # Review process:
12
12
  # only check the command and command_calls nodes and at the end of review process,
13
13
  # if the receiver of command node is "create_table", then remember the table names
14
- # if the receiver of command_call node is "integer" or "string" and suffix with _id, then remember it as foreign key
14
+ # if the receiver of command_call node is "integer" or "string" or "bigint" and suffix with _id, then remember it as foreign key
15
15
  # if the receiver of command_call node is "string", the name of it is _type suffixed and there is an integer or string column _id suffixed, then remember it as polymorphic foreign key
16
16
  # if the receiver of command_call node is remembered as foreign key and it have argument non-false "index", then remember the index columns
17
17
  # if the receiver of command node is "add_index", then remember the index columns
@@ -40,9 +40,9 @@ module RailsBestPractices
40
40
  # if the message of command_call node is "create_table", then remember the table name.
41
41
  # if the message of command_call node is "add_index", then remember it as index columns.
42
42
  add_callback :start_command_call do |node|
43
- if %w[integer string].include? node.message.to_s
43
+ if %w[integer string bigint].include? node.message.to_s
44
44
  remember_foreign_key_columns(node)
45
- elsif 'index' == node.message.to_s
45
+ elsif node.message.to_s == 'index'
46
46
  remember_index_columns_inside_table(node)
47
47
  end
48
48
  end
@@ -75,119 +75,125 @@ 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
90
- def remember_index_columns_outside_table(node)
91
- table_name = node.arguments.all.first.to_s
92
- index_column = node.arguments.all[1].to_object
89
+ # remember the node as index columns, when used outside a table
90
+ # block, i.e.
91
+ # add_index :table_name, :column_name
92
+ def remember_index_columns_outside_table(node)
93
+ table_name = node.arguments.all.first.to_s
94
+ index_column = node.arguments.all[1].to_object
93
95
 
94
- @index_columns[table_name] ||= []
95
- @index_columns[table_name] << index_column
96
- end
96
+ @index_columns[table_name] ||= []
97
+ @index_columns[table_name] << index_column
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, ...]
101
- def remember_index_columns_inside_table(node)
102
- table_name = @table_name
103
- index_column = node.arguments.all.first.to_object
100
+ # remember the node as index columns, when used inside a table
101
+ # block, i.e.
102
+ # t.index [:column_name, ...]
103
+ def remember_index_columns_inside_table(node)
104
+ table_name = @table_name
105
+ index_column = node.arguments.all.first.to_object
104
106
 
105
- @index_columns[table_name] ||= []
106
- @index_columns[table_name] << index_column
107
- end
107
+ @index_columns[table_name] ||= []
108
+ @index_columns[table_name] << index_column
109
+ end
108
110
 
109
- # remember table nodes
110
- def remember_table_nodes(node)
111
- @table_name = node.arguments.all.first.to_s
112
- @table_nodes[@table_name] = node
113
- end
111
+ # remember table nodes
112
+ def remember_table_nodes(node)
113
+ @table_name = node.arguments.all.first.to_s
114
+ @table_nodes[@table_name] = node
115
+ end
114
116
 
115
- # remember foreign key columns
116
- def remember_foreign_key_columns(node)
117
- table_name = @table_name
118
- foreign_key_column = node.arguments.all.first.to_s
119
- @foreign_keys[table_name] ||= []
120
- if foreign_key_column =~ /(.*?)_id$/
121
- if @foreign_keys[table_name].delete("#{$1}_type")
122
- @foreign_keys[table_name] << ["#{$1}_id", "#{$1}_type"]
117
+ # remember foreign key columns
118
+ def remember_foreign_key_columns(node)
119
+ table_name = @table_name
120
+ foreign_key_column = node.arguments.all.first.to_s
121
+ @foreign_keys[table_name] ||= []
122
+ if foreign_key_column =~ /(.*?)_id$/
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"]
123
126
  else
124
- @foreign_keys[table_name] << foreign_key_column
127
+ foreign_key_column
125
128
  end
126
- foreign_id_column = foreign_key_column
127
- elsif foreign_key_column =~ /(.*?)_type$/
128
- if @foreign_keys[table_name].delete("#{$1}_id")
129
- @foreign_keys[table_name] << ["#{$1}_id", "#{$1}_type"]
129
+ foreign_id_column = foreign_key_column
130
+ elsif foreign_key_column =~ /(.*?)_type$/
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"]
130
134
  else
131
- @foreign_keys[table_name] << foreign_key_column
135
+ foreign_key_column
132
136
  end
133
- foreign_id_column = "#{$1}_id"
134
- end
137
+ foreign_id_column = "#{Regexp.last_match(1)}_id"
138
+ end
135
139
 
136
- if foreign_id_column
137
- index_node = node.arguments.all.last.hash_value('index')
138
- if index_node.present? && ('false' != index_node.to_s)
139
- @index_columns[table_name] ||= []
140
- @index_columns[table_name] << foreign_id_column
141
- end
140
+ if foreign_id_column
141
+ index_node = node.arguments.all.last.hash_value('index')
142
+ if index_node.present? && (index_node.to_s != 'false')
143
+ @index_columns[table_name] ||= []
144
+ @index_columns[table_name] << foreign_id_column
142
145
  end
143
146
  end
147
+ end
144
148
 
145
- # remove the non foreign keys without corresponding tables.
146
- def remove_table_not_exist_foreign_keys
147
- @foreign_keys.each do |table, foreign_keys|
148
- foreign_keys.delete_if do |key|
149
- if key =~ /_id$/
150
- class_name = Prepares.model_associations.get_association_class_name(table, key[0..-4])
151
- class_name ? !@table_nodes[class_name.gsub('::', '').tableize] : !@table_nodes[key[0..-4].pluralize]
152
- end
149
+ # remove the non foreign keys without corresponding tables.
150
+ def remove_table_not_exist_foreign_keys
151
+ @foreign_keys.each do |table, foreign_keys|
152
+ foreign_keys.delete_if do |key|
153
+ if key.is_a?(String) && key =~ /_id$/
154
+ class_name = Prepares.model_associations.get_association_class_name(table, key[0..-4])
155
+ class_name ? !@table_nodes[class_name.gsub('::', '').tableize] : !@table_nodes[key[0..-4].pluralize]
153
156
  end
154
157
  end
155
158
  end
159
+ end
156
160
 
157
- # remove the non foreign keys with only _type column.
158
- def remove_only_type_foreign_keys
159
- @foreign_keys.each { |_table, foreign_keys|
160
- foreign_keys.delete_if { |key| key =~ /_type$/ }
161
- }
161
+ # remove the non foreign keys with only _type column.
162
+ def remove_only_type_foreign_keys
163
+ @foreign_keys.each do |_table, foreign_keys|
164
+ foreign_keys.delete_if { |key| key.is_a?(String) && key =~ /_type$/ }
162
165
  end
166
+ end
163
167
 
164
- # combine polymorphic foreign keys, e.g.
165
- # [tagger_id], [tagger_type] => [tagger_id, tagger_type]
166
- def combine_polymorphic_foreign_keys
167
- @index_columns.each { |_table, foreign_keys|
168
- foreign_id_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_id/ }
169
- foreign_type_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_type/ }
170
- 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' }
172
- foreign_keys.delete(id_key)
173
- foreign_keys.delete(type_key)
174
- foreign_keys << id_key + type_key
175
- end
176
- }
168
+ # combine polymorphic foreign keys, e.g.
169
+ # [tagger_id], [tagger_type] => [tagger_id, tagger_type]
170
+ def combine_polymorphic_foreign_keys
171
+ @index_columns.each do |_table, foreign_keys|
172
+ foreign_id_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_id/ }
173
+ foreign_type_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_type/ }
174
+ foreign_id_keys.each do |id_key|
175
+ next unless type_key =
176
+ foreign_type_keys.detect { |type_key| type_key.first == id_key.first.sub(/_id/, '') + '_type' }
177
+
178
+ foreign_keys.delete(id_key)
179
+ foreign_keys.delete(type_key)
180
+ foreign_keys << id_key + type_key
181
+ end
177
182
  end
183
+ end
178
184
 
179
- # check if the table's column is indexed.
180
- def not_indexed?(table, column)
181
- index_columns = @index_columns[table]
182
- !index_columns || index_columns.none? { |e| greater_than_or_equal(Array(e), Array(column)) }
183
- end
185
+ # check if the table's column is indexed.
186
+ def not_indexed?(table, column)
187
+ index_columns = @index_columns[table]
188
+ !index_columns || index_columns.none? { |e| greater_than_or_equal(Array(e), Array(column)) }
189
+ end
184
190
 
185
- # check if more_array is greater than less_array or equal to less_array.
186
- def greater_than_or_equal(more_array, less_array)
187
- more_size = more_array.size
188
- less_size = less_array.size
189
- (more_array - less_array).size == more_size - less_size
190
- end
191
+ # check if more_array is greater than less_array or equal to less_array.
192
+ def greater_than_or_equal(more_array, less_array)
193
+ more_size = more_array.size
194
+ less_size = less_array.size
195
+ (more_array - less_array).size == more_size - less_size
196
+ end
191
197
  end
192
198
  end
193
199
  end
@@ -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
@@ -36,7 +45,8 @@ module RailsBestPractices
36
45
 
37
46
  def return_value_is_used?(node)
38
47
  return false unless @used_return_value_of
39
- node == @used_return_value_of or @used_return_value_of.include?(node)
48
+
49
+ (node == @used_return_value_of) || @used_return_value_of.include?(node)
40
50
  end
41
51
 
42
52
  def model_classnames