rails_best_practices 1.5.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/Gemfile.lock +1 -1
  2. data/README.md +11 -7
  3. data/assets/result.html.erb +6 -4
  4. data/lib/rails_best_practices/analyzer.rb +45 -42
  5. data/lib/rails_best_practices/command.rb +36 -9
  6. data/lib/rails_best_practices/core.rb +2 -0
  7. data/lib/rails_best_practices/core/check.rb +52 -18
  8. data/lib/rails_best_practices/core/error.rb +8 -0
  9. data/lib/rails_best_practices/core/helpers.rb +8 -0
  10. data/lib/rails_best_practices/core/modules.rb +39 -0
  11. data/lib/rails_best_practices/core/runner.rb +14 -3
  12. data/lib/rails_best_practices/prepares.rb +9 -0
  13. data/lib/rails_best_practices/prepares/controller_prepare.rb +15 -3
  14. data/lib/rails_best_practices/prepares/helper_prepare.rb +41 -0
  15. data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -1
  16. data/lib/rails_best_practices/prepares/model_prepare.rb +1 -1
  17. data/lib/rails_best_practices/reviews.rb +1 -0
  18. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +2 -2
  19. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +13 -3
  20. data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +47 -0
  21. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +3 -3
  22. data/lib/rails_best_practices/version.rb +1 -1
  23. data/rails_best_practices.yml +1 -0
  24. data/spec/rails_best_practices/analyzer_spec.rb +3 -3
  25. data/spec/rails_best_practices/core/error_spec.rb +10 -1
  26. data/spec/rails_best_practices/core/helpers_spec.rb +5 -0
  27. data/spec/rails_best_practices/core/modules_spec.rb +26 -0
  28. data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +32 -1
  29. data/spec/rails_best_practices/prepares/helper_prepare_spec.rb +41 -0
  30. data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +11 -11
  31. data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +15 -15
  32. data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +84 -0
  33. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +31 -31
  34. metadata +35 -23
@@ -16,6 +16,14 @@ module RailsBestPractices
16
16
  @url = url
17
17
  end
18
18
 
19
+ def short_filename
20
+ File.expand_path(filename)[File.expand_path(Core::Runner.base_path).size..-1].sub(/^\//, '')
21
+ end
22
+
23
+ def first_line_number
24
+ line_number.split(',').first
25
+ end
26
+
19
27
  def to_s
20
28
  "#{@filename}:#{@line_number} - #{@message}"
21
29
  end
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ module RailsBestPractices
3
+ module Core
4
+ # Helper moduels.
5
+ class Helpers < Modules
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ module RailsBestPractices
3
+ module Core
4
+ # Module container
5
+ class Modules < Array
6
+ # add module decendant.
7
+ #
8
+ # @param [String] module name
9
+ # @param [String] decendant name
10
+ def add_module_decendant(module_name, decendant)
11
+ mod = find { |mod| mod.to_s == module_name }
12
+ mod.add_decendant(decendant) if mod
13
+ end
14
+ end
15
+
16
+ # Module info include module name and module spaces.
17
+ class Mod
18
+ attr_reader :decendants
19
+
20
+ def initialize(module_name, modules)
21
+ @module_name = module_name
22
+ @modules = modules
23
+ @decendants = []
24
+ end
25
+
26
+ def add_decendant(decendant)
27
+ @decendants << decendant
28
+ end
29
+
30
+ def to_s
31
+ if @modules.empty?
32
+ @module_name
33
+ else
34
+ @modules.map { |modu| "#{modu}::" }.join("") + @module_name
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -115,10 +115,21 @@ module RailsBestPractices
115
115
  (@reviews + @lexicals).collect {|check| check.errors}.flatten
116
116
  end
117
117
 
118
+ def after_lexical; end
119
+
120
+ # provide a handler after all files reviewed.
121
+ def after_prepare
122
+ filename = "rails_best_practices.after_prepare"
123
+ content = "class RailsBestPractices::AfterPrepare; end"
124
+ node = parse_ruby(filename, content)
125
+ node.file = filename
126
+ node.prepare(@checker)
127
+ end
128
+
118
129
  # provide a handler after all files reviewed.
119
- def on_complete
120
- filename = "rails_best_practices.complete"
121
- content = "class RailsBestPractices::Complete; end"
130
+ def after_review
131
+ filename = "rails_best_practices.after_review"
132
+ content = "class RailsBestPractices::AfterReview; end"
122
133
  node = parse_ruby(filename, content)
123
134
  node.file = filename
124
135
  node.review(@checker)
@@ -4,6 +4,7 @@ require 'rails_best_practices/prepares/mailer_prepare'
4
4
  require 'rails_best_practices/prepares/schema_prepare'
5
5
  require 'rails_best_practices/prepares/controller_prepare'
6
6
  require 'rails_best_practices/prepares/route_prepare'
7
+ require 'rails_best_practices/prepares/helper_prepare'
7
8
 
8
9
  module RailsBestPractices
9
10
  module Prepares
@@ -40,6 +41,14 @@ module RailsBestPractices
40
41
  @controller_methods ||= Core::Methods.new
41
42
  end
42
43
 
44
+ def helpers
45
+ @helpers ||= Core::Helpers.new
46
+ end
47
+
48
+ def helper_methods
49
+ @helper_methods ||= Core::Methods.new
50
+ end
51
+
43
52
  def routes
44
53
  @routes ||= Core::Routes.new
45
54
  end
@@ -5,9 +5,10 @@ module RailsBestPractices
5
5
  module Prepares
6
6
  # Remember controllers and controller methods
7
7
  class ControllerPrepare < Core::Check
8
- include Core::Check::Klassable
8
+ include Core::Check::Classable
9
9
  include Core::Check::InheritedResourcesable
10
10
  include Core::Check::Accessable
11
+ include Core::Check::Afterable
11
12
 
12
13
  interesting_nodes :class, :var_ref, :command, :def
13
14
  interesting_files CONTROLLER_FILES
@@ -17,6 +18,7 @@ module RailsBestPractices
17
18
  def initialize
18
19
  @controllers = Prepares.controllers
19
20
  @methods = Prepares.controller_methods
21
+ @helpers = Prepares.helpers
20
22
  @inherited_resources = false
21
23
  end
22
24
 
@@ -47,11 +49,13 @@ module RailsBestPractices
47
49
 
48
50
  # restrict actions for inherited_resources
49
51
  def start_command(node)
50
- if @inherited_resources && "actions" == node.message.to_s
52
+ if "include" == node.message.to_s
53
+ @helpers.add_module_decendant(node.arguments.all.first.to_s, current_class_name)
54
+ elsif @inherited_resources && "actions" == node.message.to_s
51
55
  if "all" == node.arguments.all.first.to_s
52
56
  @actions = DEFAULT_ACTIONS
53
57
  option_argument = node.arguments.all[1]
54
- if :bare_assoc_hash == option_argument.sexp_type && option_argument.hash_value("except")
58
+ if option_argument && :bare_assoc_hash == option_argument.sexp_type && option_argument.hash_value("except")
55
59
  @actions -= option_argument.hash_value("except").to_object
56
60
  end
57
61
  else
@@ -76,6 +80,14 @@ module RailsBestPractices
76
80
  method_name = node.method_name.to_s
77
81
  @methods.add_method(current_class_name, method_name, {"file" => node.file, "line" => node.line}, current_access_control)
78
82
  end
83
+
84
+ # ask Reviews::RemoveUnusedMoethodsInHelperReview to check the controllers who include helpers.
85
+ def after_prepare
86
+ decendants = @helpers.map(&:decendants).flatten
87
+ if decendants.present?
88
+ Reviews::RemoveUnusedMethodsInHelpersReview.interesting_files *decendants.map { |decendant| %r|#{decendant.underscore}| }
89
+ end
90
+ end
79
91
  end
80
92
  end
81
93
  end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ require 'rails_best_practices/core/check'
3
+
4
+ module RailsBestPractices
5
+ module Prepares
6
+ # Remember helper methods.
7
+ class HelperPrepare < Core::Check
8
+ include Core::Check::Moduleable
9
+ include Core::Check::Accessable
10
+
11
+ interesting_nodes :def, :command
12
+ interesting_files HELPER_FILES, CONTROLLER_FILES
13
+
14
+ def initialize
15
+ @helpers = Prepares.helpers
16
+ @methods = Prepares.helper_methods
17
+ end
18
+
19
+ # check module node to remember the module name.
20
+ def start_module(node)
21
+ @helpers << Core::Mod.new(current_module_name, [])
22
+ end
23
+
24
+ # check def node to remember all methods.
25
+ #
26
+ # the remembered methods (@methods) are like
27
+ # {
28
+ # "PostsHelper" => {
29
+ # "create_time" => {"file" => "app/helpers/posts_helper.rb", "line" => 10, "unused" => false},
30
+ # "update_time" => {"file" => "app/helpers/posts_helper.rb", "line" => 10, "unused" => false}
31
+ # }
32
+ # }
33
+ def start_def(node)
34
+ if node.file =~ HELPER_FILES
35
+ method_name = node.method_name.to_s
36
+ @methods.add_method(current_module_name, method_name, {"file" => node.file, "line" => node.line}, current_access_control)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -5,7 +5,7 @@ module RailsBestPractices
5
5
  module Prepares
6
6
  # Remember the mailer names.
7
7
  class MailerPrepare < Core::Check
8
- include Core::Check::Klassable
8
+ include Core::Check::Classable
9
9
 
10
10
  interesting_nodes :class
11
11
  interesting_files MAILER_FILES, MODEL_FILES
@@ -5,7 +5,7 @@ module RailsBestPractices
5
5
  module Prepares
6
6
  # Remember models and model associations.
7
7
  class ModelPrepare < Core::Check
8
- include Core::Check::Klassable
8
+ include Core::Check::Classable
9
9
  include Core::Check::Accessable
10
10
 
11
11
  interesting_nodes :class, :def, :command, :var_ref, :alias
@@ -28,3 +28,4 @@ require 'rails_best_practices/reviews/remove_empty_helpers_review'
28
28
  require 'rails_best_practices/reviews/restrict_auto_generated_routes_review'
29
29
  require 'rails_best_practices/reviews/remove_unused_methods_in_models_review'
30
30
  require 'rails_best_practices/reviews/remove_unused_methods_in_controllers_review'
31
+ require 'rails_best_practices/reviews/remove_unused_methods_in_helpers_review'
@@ -24,7 +24,7 @@ module RailsBestPractices
24
24
  # if there are any foreign keys not existed in index columns,
25
25
  # then the foreign keys should add db index.
26
26
  class AlwaysAddDbIndexReview < Review
27
- include Completeable
27
+ include Afterable
28
28
 
29
29
  interesting_nodes :command, :command_call
30
30
  interesting_files SCHEMA_FILE
@@ -73,7 +73,7 @@ module RailsBestPractices
73
73
  # compare foreign keys and index columns,
74
74
  # if there are any foreign keys not existed in index columns,
75
75
  # then we should add db index for that foreign keys.
76
- def on_complete
76
+ def after_review
77
77
  remove_only_type_foreign_keys
78
78
  @foreign_keys.each do |table, foreign_key|
79
79
  table_node = @table_nodes[table]
@@ -3,9 +3,19 @@ require 'rails_best_practices/reviews/review'
3
3
 
4
4
  module RailsBestPractices
5
5
  module Reviews
6
+ # Find out unused methods in controllers.
7
+ #
8
+ # Implementation:
9
+ #
10
+ # Review process:
11
+ # remember all method calls in controllers,
12
+ # if they are not defined in routes,
13
+ # and they are not called in controllers,
14
+ # then they are the unused methods in controllers.
6
15
  class RemoveUnusedMethodsInControllersReview < Review
7
- include Klassable
8
- include Completeable
16
+ include Classable
17
+ include Moduleable
18
+ include Afterable
9
19
  include Callable
10
20
  include Exceptable
11
21
  include InheritedResourcesable
@@ -66,7 +76,7 @@ module RailsBestPractices
66
76
  alias :start_method_add_arg :start_command
67
77
 
68
78
  # get all unused methods at the end of review process.
69
- def on_complete
79
+ def after_review
70
80
  @routes.each do |route|
71
81
  if "*" == route.action_name
72
82
  action_names = @controller_methods.get_methods(route.controller_name_with_namespaces).map(&:method_name)
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ require 'rails_best_practices/reviews/review'
3
+
4
+ module RailsBestPractices
5
+ module Reviews
6
+ # Find out unused methods in helpers.
7
+ #
8
+ # Implementation:
9
+ #
10
+ # Review process:
11
+ # remember all method calls in helpers.
12
+ # if they are not called in views or helpers,
13
+ # then they are unused methods in helpers.
14
+ class RemoveUnusedMethodsInHelpersReview < Review
15
+ include Moduleable
16
+ include Afterable
17
+ include Callable
18
+ include Exceptable
19
+
20
+ interesting_files HELPER_FILES, VIEW_FILES
21
+
22
+ def initialize(options={})
23
+ super
24
+ @helper_methods = Prepares.helper_methods
25
+ self.class.interesting_files Prepares.helpers.map(&:decendants)
26
+ end
27
+
28
+ # get all unused methods at the end of review process
29
+ def after_review
30
+ @helper_methods.get_all_unused_methods.each do |method|
31
+ if !excepted?(method)
32
+ add_error "remove unused methods (#{method.class_name}##{method.method_name})", method.file, method.line
33
+ end
34
+ end
35
+ end
36
+
37
+ protected
38
+ def methods
39
+ @helper_methods
40
+ end
41
+
42
+ def internal_except_methods
43
+ []
44
+ end
45
+ end
46
+ end
47
+ end
@@ -12,8 +12,8 @@ module RailsBestPractices
12
12
  # at end, check if all defined methods are called,
13
13
  # if not, non called methods are unused.
14
14
  class RemoveUnusedMethodsInModelsReview < Review
15
- include Klassable
16
- include Completeable
15
+ include Classable
16
+ include Afterable
17
17
  include Callable
18
18
  include Exceptable
19
19
 
@@ -41,7 +41,7 @@ module RailsBestPractices
41
41
  end
42
42
 
43
43
  # get all unused methods at the end of review process.
44
- def on_complete
44
+ def after_review
45
45
  @model_methods.get_all_unused_methods.each do |method|
46
46
  if !excepted?(method) && method.method_name !~ /=$/
47
47
  add_error "remove unused methods (#{method.class_name}##{method.method_name})", method.file, method.line
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module RailsBestPractices
3
- VERSION = "1.5.3"
3
+ VERSION = "1.6.0"
4
4
  end
@@ -29,3 +29,4 @@ RemoveTabCheck: { }
29
29
  RestrictAutoGeneratedRoutesCheck: { }
30
30
  RemoveUnusedMethodsInModelsCheck: { except_methods: [] }
31
31
  RemoveUnusedMethodsInControllersCheck: { except_methods: [] }
32
+ RemoveUnusedMethodsInHelpersCheck: { except_methods: [] }
@@ -11,9 +11,9 @@ describe RailsBestPractices::Analyzer do
11
11
  end
12
12
 
13
13
  describe "file_sort" do
14
- it "should get models first, then mailers" do
15
- files = ["app/controllers/users_controller.rb", "app/mailers/user_mailer.rb", "app/models/user.rb", "app/views/users/index.html.haml", "lib/user.rb"]
16
- subject.file_sort(files).should == ["app/models/user.rb", "app/mailers/user_mailer.rb", "app/controllers/users_controller.rb", "app/views/users/index.html.haml", "lib/user.rb"]
14
+ it "should get models first, mailers, helpers and then others" do
15
+ files = ["app/controllers/users_controller.rb", "app/mailers/user_mailer.rb", "app/helpers/users_helper.rb", "app/models/user.rb", "app/views/users/index.html.haml", "lib/user.rb"]
16
+ subject.file_sort(files).should == ["app/models/user.rb", "app/mailers/user_mailer.rb", "app/helpers/users_helper.rb", "app/controllers/users_controller.rb", "app/views/users/index.html.haml", "lib/user.rb"]
17
17
  end
18
18
  end
19
19
 
@@ -2,6 +2,15 @@ require 'spec_helper'
2
2
 
3
3
  describe RailsBestPractices::Core::Error do
4
4
  it "should return error with filename, line number and message" do
5
- RailsBestPractices::Core::Error.new("app/models/user.rb", 100, "not good", "BogusReview").to_s.should == "app/models/user.rb:100 - not good"
5
+ RailsBestPractices::Core::Error.new("app/models/user.rb", "100", "not good", "BogusReview").to_s.should == "app/models/user.rb:100 - not good"
6
+ end
7
+
8
+ it "should return short filename" do
9
+ RailsBestPractices::Core::Runner.base_path = "../rails-bestpractices.com"
10
+ RailsBestPractices::Core::Error.new("../rails-bestpractices.com/app/models/user.rb", "100", "not good", "BogusReview").short_filename.should == "app/models/user.rb"
11
+ end
12
+
13
+ it "should return first line number" do
14
+ RailsBestPractices::Core::Error.new("app/models/user.rb", "50,70,100", "not good", "BogusReview").first_line_number.should == "50"
6
15
  end
7
16
  end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsBestPractices::Core::Helpers do
4
+ it { should be_a_kind_of RailsBestPractices::Core::Modules }
5
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsBestPractices::Core::Modules do
4
+ it { should be_a_kind_of Array }
5
+
6
+ context "Modules" do
7
+ before do
8
+ @mod = RailsBestPractices::Core::Mod.new("PostsHelper", [])
9
+ end
10
+ subject { RailsBestPractices::Core::Modules.new.tap { |modules| modules << @mod } }
11
+ it "should add decendant to the corresponding module" do
12
+ @mod.should_receive(:add_decendant).with("PostsController")
13
+ subject.add_module_decendant("PostsHelper", "PostsController")
14
+ end
15
+ end
16
+
17
+ context "Mod" do
18
+ subject {
19
+ RailsBestPractices::Core::Mod.new("UsersHelper", ["Admin"]).tap do |mod|
20
+ mod.add_decendant("Admin::UsersController")
21
+ end
22
+ }
23
+ its(:to_s) { should == "Admin::UsersHelper" }
24
+ its(:decendants) { should == ["Admin::UsersController"] }
25
+ end
26
+ end
@@ -1,7 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe RailsBestPractices::Prepares::ControllerPrepare do
4
- let(:runner) { RailsBestPractices::Core::Runner.new(:prepares => RailsBestPractices::Prepares::ControllerPrepare.new) }
4
+ let(:runner) { RailsBestPractices::Core::Runner.new(
5
+ :prepares => [RailsBestPractices::Prepares::ControllerPrepare.new, RailsBestPractices::Prepares::HelperPrepare.new]
6
+ ) }
5
7
 
6
8
  before :each do
7
9
  runner.whiny = true
@@ -100,6 +102,17 @@ describe RailsBestPractices::Prepares::ControllerPrepare do
100
102
  methods.get_methods("PostsController").map(&:method_name).should == ["index", "new", "create", "edit", "update", "destroy"]
101
103
  end
102
104
 
105
+ it "extend inherited_resources with all actions with no arguments" do
106
+ content =<<-EOF
107
+ class PostsController < InheritedResources::Base
108
+ actions :all
109
+ end
110
+ EOF
111
+ runner.prepare('app/controllers/posts_controller.rb', content)
112
+ methods = RailsBestPractices::Prepares.controller_methods
113
+ methods.get_methods("PostsController").map(&:method_name).should == ["index", "show", "new", "create", "edit", "update", "destroy"]
114
+ end
115
+
103
116
  it "DSL inherit_resources" do
104
117
  content =<<-EOF
105
118
  class PostsController
@@ -112,4 +125,22 @@ describe RailsBestPractices::Prepares::ControllerPrepare do
112
125
  end
113
126
  end
114
127
  end
128
+
129
+ context "helpers" do
130
+ it "should add helper decendant" do
131
+ content =<<-EOF
132
+ module PostsHelper
133
+ end
134
+ EOF
135
+ runner.prepare('app/helpers/posts_helper.rb', content)
136
+ content =<<-EOF
137
+ class PostsController
138
+ include PostsHelper
139
+ end
140
+ EOF
141
+ runner.prepare('app/controllers/posts_controller.rb', content)
142
+ helpers = RailsBestPractices::Prepares.helpers
143
+ helpers.first.decendants.should == ["PostsController"]
144
+ end
145
+ end
115
146
  end