rails_best_practices 1.5.3 → 1.6.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.
- data/Gemfile.lock +1 -1
- data/README.md +11 -7
- data/assets/result.html.erb +6 -4
- data/lib/rails_best_practices/analyzer.rb +45 -42
- data/lib/rails_best_practices/command.rb +36 -9
- data/lib/rails_best_practices/core.rb +2 -0
- data/lib/rails_best_practices/core/check.rb +52 -18
- data/lib/rails_best_practices/core/error.rb +8 -0
- data/lib/rails_best_practices/core/helpers.rb +8 -0
- data/lib/rails_best_practices/core/modules.rb +39 -0
- data/lib/rails_best_practices/core/runner.rb +14 -3
- data/lib/rails_best_practices/prepares.rb +9 -0
- data/lib/rails_best_practices/prepares/controller_prepare.rb +15 -3
- data/lib/rails_best_practices/prepares/helper_prepare.rb +41 -0
- data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -1
- data/lib/rails_best_practices/prepares/model_prepare.rb +1 -1
- data/lib/rails_best_practices/reviews.rb +1 -0
- data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +2 -2
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +13 -3
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +47 -0
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +3 -3
- data/lib/rails_best_practices/version.rb +1 -1
- data/rails_best_practices.yml +1 -0
- data/spec/rails_best_practices/analyzer_spec.rb +3 -3
- data/spec/rails_best_practices/core/error_spec.rb +10 -1
- data/spec/rails_best_practices/core/helpers_spec.rb +5 -0
- data/spec/rails_best_practices/core/modules_spec.rb +26 -0
- data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +32 -1
- data/spec/rails_best_practices/prepares/helper_prepare_spec.rb +41 -0
- data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +11 -11
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +15 -15
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +84 -0
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +31 -31
- 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,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
|
120
|
-
filename = "rails_best_practices.
|
121
|
-
content = "class RailsBestPractices::
|
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::
|
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
|
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 models and model associations.
|
7
7
|
class ModelPrepare < Core::Check
|
8
|
-
include Core::Check::
|
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
|
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
|
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
|
8
|
-
include
|
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
|
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
|
16
|
-
include
|
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
|
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
|
data/rails_best_practices.yml
CHANGED
@@ -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
|
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,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(
|
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
|