rails_best_practices 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/Gemfile.lock +1 -1
  2. data/README.md +14 -12
  3. data/lib/rails_best_practices.rb +7 -21
  4. data/lib/rails_best_practices/core.rb +1 -0
  5. data/lib/rails_best_practices/core/check.rb +156 -6
  6. data/lib/rails_best_practices/core/checking_visitor.rb +2 -2
  7. data/lib/rails_best_practices/core/methods.rb +1 -0
  8. data/lib/rails_best_practices/core/nil.rb +10 -0
  9. data/lib/rails_best_practices/core/routes.rb +33 -0
  10. data/lib/rails_best_practices/core/runner.rb +3 -1
  11. data/lib/rails_best_practices/core_ext/sexp.rb +11 -0
  12. data/lib/rails_best_practices/prepares.rb +5 -2
  13. data/lib/rails_best_practices/prepares/controller_prepare.rb +8 -14
  14. data/lib/rails_best_practices/prepares/mailer_prepare.rb +2 -7
  15. data/lib/rails_best_practices/prepares/model_prepare.rb +3 -8
  16. data/lib/rails_best_practices/prepares/route_prepare.rb +142 -0
  17. data/lib/rails_best_practices/prepares/schema_prepare.rb +3 -8
  18. data/lib/rails_best_practices/reviews.rb +1 -0
  19. data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +3 -8
  20. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +9 -12
  21. data/lib/rails_best_practices/reviews/dry_bundler_in_capistrano_review.rb +3 -8
  22. data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +3 -8
  23. data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +2 -8
  24. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +3 -4
  25. data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +2 -8
  26. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +3 -8
  27. data/lib/rails_best_practices/reviews/move_code_into_model_review.rb +3 -8
  28. data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +2 -8
  29. data/lib/rails_best_practices/reviews/move_model_logic_into_model_review.rb +3 -8
  30. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +3 -8
  31. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +3 -8
  32. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +3 -9
  33. data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +3 -8
  34. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +57 -0
  35. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +6 -92
  36. data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +3 -8
  37. data/lib/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review.rb +3 -8
  38. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +3 -8
  39. data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +3 -8
  40. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +3 -8
  41. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +3 -8
  42. data/lib/rails_best_practices/reviews/use_model_association_review.rb +3 -8
  43. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +3 -8
  44. data/lib/rails_best_practices/reviews/use_observer_review.rb +3 -8
  45. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +2 -4
  46. data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +2 -8
  47. data/lib/rails_best_practices/reviews/use_scope_access_review.rb +3 -8
  48. data/lib/rails_best_practices/version.rb +1 -1
  49. data/rails_best_practices.yml +1 -0
  50. data/spec/rails_best_practices/core/check_spec.rb +2 -2
  51. data/spec/rails_best_practices/core/checking_visitor_spec.rb +12 -32
  52. data/spec/rails_best_practices/core/nil_spec.rb +12 -0
  53. data/spec/rails_best_practices/core/routes_spec.rb +10 -0
  54. data/spec/rails_best_practices/core_ext/sexp_spec.rb +14 -0
  55. data/spec/rails_best_practices/prepares/route_prepare_spec.rb +502 -0
  56. data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +14 -1
  57. data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +20 -4
  58. data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +120 -0
  59. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +1 -1
  60. metadata +113 -123
@@ -16,16 +16,15 @@ module RailsBestPractices
16
16
  # and outer the call node, it is also a call node,
17
17
  # then it violate the law of demeter.
18
18
  class LawOfDemeterReview < Review
19
+ interesting_nodes :call
20
+ interesting_files ALL_FILES
21
+
19
22
  ASSOCIATION_METHODS = %w(belongs_to has_one)
20
23
 
21
24
  def url
22
25
  "http://rails-bestpractices.com/posts/15-the-law-of-demeter"
23
26
  end
24
27
 
25
- def interesting_nodes
26
- [:call]
27
- end
28
-
29
28
  # check the call node,
30
29
  #
31
30
  # if the subject of the call node is also a call node,
@@ -12,6 +12,8 @@ module RailsBestPractices
12
12
  # Review process:
13
13
  # only check all view files to see if there are finders, then the finders should be moved to controller.
14
14
  class MoveCodeIntoControllerReview < Review
15
+ interesting_nodes :method_add_arg, :assign
16
+ interesting_files VIEW_FILES
15
17
 
16
18
  FINDERS = %w(find all first last)
17
19
 
@@ -19,14 +21,6 @@ module RailsBestPractices
19
21
  "http://rails-bestpractices.com/posts/24-move-code-into-controller"
20
22
  end
21
23
 
22
- def interesting_nodes
23
- [:method_add_arg, :assign]
24
- end
25
-
26
- def interesting_files
27
- VIEW_FILES
28
- end
29
-
30
24
  # check method_add_arg nodes.
31
25
  #
32
26
  # if the subject of the method_add_arg node is a constant,
@@ -19,18 +19,13 @@ module RailsBestPractices
19
19
  # and the size of the array is greater than array_count defined,
20
20
  # then the options_for_select method should be moved into helper.
21
21
  class MoveCodeIntoHelperReview < Review
22
+ interesting_nodes :method_add_arg
23
+ interesting_files VIEW_FILES
24
+
22
25
  def url
23
26
  "http://rails-bestpractices.com/posts/26-move-code-into-helper"
24
27
  end
25
28
 
26
- def interesting_nodes
27
- [:method_add_arg]
28
- end
29
-
30
- def interesting_files
31
- VIEW_FILES
32
- end
33
-
34
29
  def initialize(options = {})
35
30
  super()
36
31
  @array_count = options['array_count'] || 3
@@ -13,18 +13,13 @@ module RailsBestPractices
13
13
  # check if, unless, elsif there are multiple method calls or attribute assignments apply to one subject,
14
14
  # and the subject is a variable, then they should be moved into model.
15
15
  class MoveCodeIntoModelReview < Review
16
+ interesting_nodes :if, :unless, :elsif
17
+ interesting_files VIEW_FILES
18
+
16
19
  def url
17
20
  "http://rails-bestpractices.com/posts/25-move-code-into-model"
18
21
  end
19
22
 
20
- def interesting_nodes
21
- [:if, :unless, :elsif]
22
- end
23
-
24
- def interesting_files
25
- VIEW_FILES
26
- end
27
-
28
23
  def initialize(options={})
29
24
  super()
30
25
  @use_count = options['use_count'] || 2
@@ -15,6 +15,8 @@ module RailsBestPractices
15
15
  # and it has a hash argument,
16
16
  # then it is a complex finder, and should be moved to model's named scope.
17
17
  class MoveFinderToNamedScopeReview < Review
18
+ interesting_nodes :method_add_arg
19
+ interesting_files CONTROLLER_FILES
18
20
 
19
21
  FINDERS = %w(find all first last)
20
22
 
@@ -22,14 +24,6 @@ module RailsBestPractices
22
24
  "http://rails-bestpractices.com/posts/1-move-finder-to-named_scope"
23
25
  end
24
26
 
25
- def interesting_nodes
26
- [:method_add_arg]
27
- end
28
-
29
- def interesting_files
30
- CONTROLLER_FILES
31
- end
32
-
33
27
  # check method_add_ag node if its message is one of find, all, first or last,
34
28
  # and it has a hash argument,
35
29
  # then the call node is the finder that should be moved to model's named_scope.
@@ -15,18 +15,13 @@ module RailsBestPractices
15
15
  # and the subject is a variable,
16
16
  # then they are complex model logic, and they should be moved into model.
17
17
  class MoveModelLogicIntoModelReview < Review
18
+ interesting_nodes :def
19
+ interesting_files CONTROLLER_FILES
20
+
18
21
  def url
19
22
  "http://rails-bestpractices.com/posts/7-move-model-logic-into-the-model"
20
23
  end
21
24
 
22
- def interesting_nodes
23
- [:def]
24
- end
25
-
26
- def interesting_files
27
- CONTROLLER_FILES
28
- end
29
-
30
25
  def initialize(options = {})
31
26
  super()
32
27
  @use_count = options['use_count'] || 4
@@ -24,18 +24,13 @@ module RailsBestPractices
24
24
  # and the @counter is greater than @nested_count defined,
25
25
  # then it is a needless deep nesting.
26
26
  class NeedlessDeepNestingReview < Review
27
+ interesting_nodes :method_add_block
28
+ interesting_files ROUTE_FILES
29
+
27
30
  def url
28
31
  "http://rails-bestpractices.com/posts/11-needless-deep-nesting"
29
32
  end
30
33
 
31
- def interesting_nodes
32
- [:method_add_block]
33
- end
34
-
35
- def interesting_files
36
- ROUTE_FILES
37
- end
38
-
39
34
  def initialize(options = {})
40
35
  super()
41
36
  @counter = 0
@@ -19,18 +19,13 @@ module RailsBestPractices
19
19
  #
20
20
  # match ':controller(/:action(/:id(.:format)))'
21
21
  class NotUseDefaultRouteReview < Review
22
+ interesting_nodes :command_call, :command
23
+ interesting_files ROUTE_FILES
24
+
22
25
  def url
23
26
  "http://rails-bestpractices.com/posts/12-not-use-default-route-if-you-use-restful-design"
24
27
  end
25
28
 
26
- def interesting_nodes
27
- [:command_call, :command]
28
- end
29
-
30
- def interesting_files
31
- ROUTE_FILES
32
- end
33
-
34
29
  # check all command call nodes, compare with rails2 default route
35
30
  def start_command_call(node)
36
31
  if "map" == node.subject.to_s && "connect" == node.message.to_s &&
@@ -28,6 +28,8 @@ module RailsBestPractices
28
28
  # whose message is get, post, update or delete,
29
29
  # then these custom routes are overuse.
30
30
  class OveruseRouteCustomizationsReview < Review
31
+ interesting_nodes :command_call, :method_add_block
32
+ interesting_files ROUTE_FILES
31
33
 
32
34
  VERBS = %w(get post update delete)
33
35
 
@@ -35,14 +37,6 @@ module RailsBestPractices
35
37
  "http://rails-bestpractices.com/posts/10-overuse-route-customizations"
36
38
  end
37
39
 
38
- def interesting_nodes
39
- [:command_call, :method_add_block]
40
- end
41
-
42
- def interesting_files
43
- ROUTE_FILES
44
- end
45
-
46
40
  def initialize(options = {})
47
41
  super()
48
42
  @customize_count = options['customize_count'] || 3
@@ -84,7 +78,7 @@ module RailsBestPractices
84
78
  # it is just the count of member and collection custom routes.
85
79
  def member_and_collection_count_for_rails2(node)
86
80
  if "resources" == node.message.to_s
87
- hash_node = node.arguments.all[1]
81
+ hash_node = node.arguments.all.last
88
82
  if hash_node && :bare_assoc_hash == hash_node.sexp_type
89
83
  member_node = hash_node.hash_value("member")
90
84
  collection_node = hash_node.hash_value("collection")
@@ -12,18 +12,13 @@ module RailsBestPractices
12
12
  # Review process:
13
13
  # check all helper files, if the body of module is nil, then the helper file should be removed.
14
14
  class RemoveEmptyHelpersReview < Review
15
+ interesting_nodes :module
16
+ interesting_files HELPER_FILES
17
+
15
18
  def url
16
19
  "http://rails-bestpractices.com/posts/72-remove-empty-helpers"
17
20
  end
18
21
 
19
- def interesting_files
20
- HELPER_FILES
21
- end
22
-
23
- def interesting_nodes
24
- [:module]
25
- end
26
-
27
22
  # check the body of module node, if it is nil, then it should be removed.
28
23
  def start_module(node)
29
24
  if s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil) == node.body
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+ require 'rails_best_practices/reviews/review'
3
+
4
+ module RailsBestPractices
5
+ module Reviews
6
+ class RemoveUnusedMethodsInControllersReview < Review
7
+ include Klassable
8
+ include Completeable
9
+ include Callable
10
+ include InheritedResourcesable
11
+
12
+ interesting_nodes :class
13
+ interesting_files CONTROLLER_FILES
14
+
15
+ EXCEPT_METHODS = %w(rescue_action)
16
+ INHERITED_RESOURCES_METHODS = %w(resource collection begin_of_association_chain build_resource)
17
+
18
+ def initialize(options={})
19
+ @controller_methods = Prepares.controller_methods
20
+ @routes = Prepares.routes
21
+ @inherited_resources = false
22
+ @except_methods = EXCEPT_METHODS + options['except_methods']
23
+ end
24
+
25
+ # mark custom inherited_resources methods as used.
26
+ def end_class(node)
27
+ if @inherited_resources
28
+ INHERITED_RESOURCES_METHODS.each do |method|
29
+ call_method(method)
30
+ end
31
+ end
32
+ end
33
+
34
+ # get all unused methods at the end of review process.
35
+ def on_complete
36
+ @routes.each do |route|
37
+ if "*" == route.action_name
38
+ action_names = @controller_methods.get_methods(route.controller_name_with_namespaces).map(&:method_name)
39
+ action_names.each { |action_name| call_method(action_name, route.controller_name_with_namespaces) }
40
+ else
41
+ call_method(route.action_name, route.controller_name_with_namespaces)
42
+ end
43
+ end
44
+ @controller_methods.get_all_unused_methods.each do |method|
45
+ if !@except_methods.include?(method.method_name)
46
+ add_error "remove unused methods (#{method.class_name}##{method.method_name})", method.file, method.line
47
+ end
48
+ end
49
+ end
50
+
51
+ protected
52
+ def methods
53
+ @controller_methods
54
+ end
55
+ end
56
+ end
57
+ end
@@ -14,12 +14,11 @@ module RailsBestPractices
14
14
  class RemoveUnusedMethodsInModelsReview < Review
15
15
  include Klassable
16
16
  include Completeable
17
+ include Callable
17
18
 
18
- EXCEPT_METHODS = %w(initialize validate to_xml to_json assign_attributes after_find after_initialize)
19
+ interesting_files ALL_FILES
19
20
 
20
- def interesting_nodes
21
- [:module, :class, :call, :fcall, :command, :command_call, :method_add_arg, :var_ref, :alias, :bare_assoc_hash]
22
- end
21
+ EXCEPT_METHODS = %w(initialize validate to_xml to_json assign_attributes after_find after_initialize)
23
22
 
24
23
  def initialize(options={})
25
24
  super()
@@ -27,75 +26,6 @@ module RailsBestPractices
27
26
  @except_methods = EXCEPT_METHODS + options['except_methods']
28
27
  end
29
28
 
30
- # remember the message of call node.
31
- def start_call(node)
32
- mark_used(node.message)
33
- end
34
-
35
- # remember the message of fcall node.
36
- def start_fcall(node)
37
- mark_used(node.message)
38
- end
39
-
40
- # remember name of var_ref node.
41
- def start_var_ref(node)
42
- mark_used(node)
43
- end
44
-
45
- # remember the message of command call node.
46
- def start_command_call(node)
47
- mark_used(node.message)
48
- end
49
-
50
- # remember the message of command node.
51
- # remember the argument of alias_method and alias_method_chain as well.
52
- def start_command(node)
53
- case node.message.to_s
54
- when "named_scope", "scope"
55
- # nothing
56
- when "alias_method"
57
- mark_used(node.arguments.all[1])
58
- when "alias_method_chain"
59
- method, feature = *node.arguments.all.map(&:to_s)
60
- call_method("#{method}_with_#{feature}")
61
- else
62
- mark_used(node.message)
63
- node.arguments.all.each { |argument| mark_used(argument) }
64
- end
65
- end
66
-
67
- # remember the old method of alias node.
68
- def start_alias(node)
69
- mark_used(node.old_method)
70
- end
71
-
72
- # remember hash values for hash key "methods".
73
- #
74
- # def to_xml(options = {})
75
- # super options.merge(:exclude => :visible, :methods => [:is_discussion_conversation])
76
- # end
77
- def start_bare_assoc_hash(node)
78
- if node.hash_keys.include? "methods"
79
- mark_used(node.hash_value("methods"))
80
- end
81
- end
82
-
83
- # remember the first argument for try and send method.
84
- def start_method_add_arg(node)
85
- case node.message.to_s
86
- when "try"
87
- method_name = node.arguments.all[0].to_s
88
- call_method(method_name)
89
- when "send"
90
- if [:symbol_literal, :string_literal].include?(node.arguments.all[0].sexp_type)
91
- method_name = node.arguments.all[0].to_s
92
- call_method(method_name)
93
- end
94
- else
95
- # nothing
96
- end
97
- end
98
-
99
29
  # get all unused methods at the end of review process.
100
30
  def on_complete
101
31
  @model_methods.get_all_unused_methods.each do |method|
@@ -105,25 +35,9 @@ module RailsBestPractices
105
35
  end
106
36
  end
107
37
 
108
- private
109
- def mark_used(method_node)
110
- if :bare_assoc_hash == method_node.sexp_type
111
- method_node.hash_values.each { |value_node| mark_used(value_node) }
112
- elsif :array == method_node.sexp_type
113
- method_node.array_values.each { |value_node| mark_used(value_node) }
114
- else
115
- method_name = method_node.to_s
116
- end
117
- call_method(method_name)
118
- end
119
-
120
- def call_method(method_name)
121
- if @model_methods.has_method?(current_class_name, method_name)
122
- @model_methods.get_method(current_class_name, method_name).mark_used
123
- end
124
- @model_methods.mark_parent_class_method_used(current_class_name, method_name)
125
- @model_methods.mark_subclasses_method_used(current_class_name, method_name)
126
- @model_methods.possible_public_used(method_name)
38
+ protected
39
+ def methods
40
+ @model_methods
127
41
  end
128
42
  end
129
43
  end
@@ -16,18 +16,13 @@ module RailsBestPractices
16
16
  # and after them there is a call node with message "save" or "save!",
17
17
  # then these attribute assignments are complex creation, should be replaced with factory method.
18
18
  class ReplaceComplexCreationWithFactoryMethodReview < Review
19
+ interesting_nodes :def
20
+ interesting_files CONTROLLER_FILES
21
+
19
22
  def url
20
23
  "http://rails-bestpractices.com/posts/6-replace-complex-creation-with-factory-method"
21
24
  end
22
25
 
23
- def interesting_nodes
24
- [:def]
25
- end
26
-
27
- def interesting_files
28
- CONTROLLER_FILES
29
- end
30
-
31
26
  def initialize(options = {})
32
27
  super()
33
28
  @assigns_count = options['attribute_assignment_count'] || 2
@@ -13,18 +13,13 @@ module RailsBestPractices
13
13
  # check all instance variable in partial view files,
14
14
  # if exist, then they should be replaced with local variable
15
15
  class ReplaceInstanceVariableWithLocalVariableReview < Review
16
+ interesting_nodes :var_ref
17
+ interesting_files PARTIAL_VIEW_FILES
18
+
16
19
  def url
17
20
  "http://rails-bestpractices.com/posts/27-replace-instance-variable-with-local-variable"
18
21
  end
19
22
 
20
- def interesting_nodes
21
- [:var_ref]
22
- end
23
-
24
- def interesting_files
25
- PARTIAL_VIEW_FILES
26
- end
27
-
28
23
  # check ivar node in partial view file,
29
24
  # it is an instance variable, and should be replaced with local variable.
30
25
  def start_var_ref(node)