rails_best_practices 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -1
- data/.travis.yml +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +45 -0
- data/README.md +2 -0
- data/assets/result.html.erb +78 -0
- data/lib/rails_best_practices.rb +5 -4
- data/lib/rails_best_practices/core.rb +2 -0
- data/lib/rails_best_practices/core/check.rb +27 -1
- data/lib/rails_best_practices/core/controllers.rb +7 -0
- data/lib/rails_best_practices/core/methods.rb +26 -0
- data/lib/rails_best_practices/core/runner.rb +3 -1
- data/lib/rails_best_practices/core_ext/sexp.rb +33 -8
- data/lib/rails_best_practices/prepares.rb +13 -0
- data/lib/rails_best_practices/prepares/controller_prepare.rb +74 -0
- data/lib/rails_best_practices/prepares/mailer_prepare.rb +3 -2
- data/lib/rails_best_practices/prepares/model_prepare.rb +12 -10
- data/lib/rails_best_practices/reviews.rb +1 -0
- data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +1 -1
- data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +1 -1
- data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +1 -1
- data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +149 -0
- data/lib/rails_best_practices/version.rb +1 -1
- data/rails_best_practices.gemspec +3 -3
- data/rails_best_practices.yml +1 -0
- data/spec/rails_best_practices/core/controllers_spec.rb +5 -0
- data/spec/rails_best_practices/core/methods_spec.rb +22 -0
- data/spec/rails_best_practices/core_ext/sexp_spec.rb +28 -1
- data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +92 -0
- data/spec/rails_best_practices/prepares/model_prepare_spec.rb +22 -0
- data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +4 -4
- data/spec/rails_best_practices/reviews/restrict_auto_generated_routes_review_spec.rb +334 -0
- data/spec/spec_helper.rb +6 -0
- metadata +27 -13
- data/assets/result.html.haml +0 -63
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm: 1.9.2
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rails_best_practices (1.1.0)
|
5
|
+
activesupport
|
6
|
+
colored
|
7
|
+
erubis
|
8
|
+
i18n
|
9
|
+
progressbar
|
10
|
+
sexp_processor
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: http://rubygems.org/
|
14
|
+
specs:
|
15
|
+
activesupport (3.0.7)
|
16
|
+
colored (1.2)
|
17
|
+
diff-lcs (1.1.2)
|
18
|
+
erubis (2.7.0)
|
19
|
+
haml (3.1.3)
|
20
|
+
i18n (0.5.0)
|
21
|
+
progressbar (0.9.1)
|
22
|
+
rake (0.8.7)
|
23
|
+
ripper (1.0.2)
|
24
|
+
rspec (2.4.0)
|
25
|
+
rspec-core (~> 2.4.0)
|
26
|
+
rspec-expectations (~> 2.4.0)
|
27
|
+
rspec-mocks (~> 2.4.0)
|
28
|
+
rspec-core (2.4.0)
|
29
|
+
rspec-expectations (2.4.0)
|
30
|
+
diff-lcs (~> 1.1.2)
|
31
|
+
rspec-mocks (2.4.0)
|
32
|
+
sexp_processor (3.0.5)
|
33
|
+
watchr (0.7)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
ruby
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
bundler
|
40
|
+
haml
|
41
|
+
rails_best_practices!
|
42
|
+
rake
|
43
|
+
ripper
|
44
|
+
rspec
|
45
|
+
watchr
|
data/README.md
CHANGED
@@ -132,6 +132,7 @@ Now you can customize this configuration file, the default configuration is as f
|
|
132
132
|
SimplifyRenderInControllersCheck: {}
|
133
133
|
RemoveEmptyHelpersCheck: {}
|
134
134
|
RemoveTabCheck: {}
|
135
|
+
RestrictAutoGeneratedRoutesCheck: { }
|
135
136
|
|
136
137
|
You can remove or comment one review to disable it, and you can change the options.
|
137
138
|
|
@@ -152,6 +153,7 @@ RESTful Conventions
|
|
152
153
|
1. Overuse route customizations
|
153
154
|
2. Needless deep nesting
|
154
155
|
3. Not use default route
|
156
|
+
4. Restrict auto-generated routes
|
155
157
|
|
156
158
|
Model
|
157
159
|
|
@@ -0,0 +1,78 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset='UTF-8' />
|
5
|
+
<title>Output of rails_best_practices</title>
|
6
|
+
|
7
|
+
<style type="text/css">
|
8
|
+
body {
|
9
|
+
color: #333;
|
10
|
+
background: #eee;
|
11
|
+
padding: 0 20px;
|
12
|
+
}
|
13
|
+
h1 {
|
14
|
+
color: ##4E4E4E;
|
15
|
+
}
|
16
|
+
table {
|
17
|
+
background: white;
|
18
|
+
border: 1px solid #666;
|
19
|
+
border-collapse: collapse;
|
20
|
+
margin: 20px 0;
|
21
|
+
font-size: 14px;
|
22
|
+
}
|
23
|
+
table th, table td {
|
24
|
+
padding: 4px;
|
25
|
+
border: 1px solid #D0D0D0;
|
26
|
+
}
|
27
|
+
table th {
|
28
|
+
background-color: #DFC;
|
29
|
+
color: #337022;
|
30
|
+
}
|
31
|
+
table td.filename {
|
32
|
+
color: #ED1556;
|
33
|
+
}
|
34
|
+
table tr:hover {
|
35
|
+
background-color: #FFFFC0;
|
36
|
+
}
|
37
|
+
</style>
|
38
|
+
</head>
|
39
|
+
<body>
|
40
|
+
<h1>rails_best_practices output</h1>
|
41
|
+
<h2>
|
42
|
+
Please go to
|
43
|
+
<a href='http://rails-bestpractices.com' target='_blank'>http://rails-bestpractices.com</a>
|
44
|
+
to see more useful Rails Best Practices.
|
45
|
+
</h2>
|
46
|
+
<h2>
|
47
|
+
<% if @errors.empty? %>
|
48
|
+
No error found. Cool!
|
49
|
+
<% else %>
|
50
|
+
Found <%= @errors.size %> errors.
|
51
|
+
<% end %>
|
52
|
+
</h2>
|
53
|
+
<table>
|
54
|
+
<tr>
|
55
|
+
<th>Filename</th>
|
56
|
+
<th>Line Number</th>
|
57
|
+
<th>Warning Message</th>
|
58
|
+
</tr>
|
59
|
+
<% @errors.each do |error| %>
|
60
|
+
<tr>
|
61
|
+
<td class='filename'>
|
62
|
+
<% if @textmate %>
|
63
|
+
<a href='txmt://open/?url=file://<%= File.expand_path(error.filename) %>&line=<%= error.line_number %>'><%= error.filename %></a>
|
64
|
+
<% elsif @mvim %>
|
65
|
+
<a href='mvim://open/?url=file://<%= File.expand_path(error.filename) %>&line=<%= error.line_number %>'><%= error.filename %></a>
|
66
|
+
<% else %>
|
67
|
+
<%= error.filename %>
|
68
|
+
<% end %>
|
69
|
+
</td>
|
70
|
+
<td class='line'><%= error.line_number %></td>
|
71
|
+
<td class='message'>
|
72
|
+
<a href='<%= error.url %>' target='_blank'><%= error.message %></a>
|
73
|
+
</td>
|
74
|
+
</tr>
|
75
|
+
<% end %>
|
76
|
+
</table>
|
77
|
+
</body>
|
78
|
+
</html>
|
data/lib/rails_best_practices.rb
CHANGED
@@ -25,7 +25,6 @@
|
|
25
25
|
require 'rubygems'
|
26
26
|
require 'progressbar'
|
27
27
|
require 'colored'
|
28
|
-
require 'haml'
|
29
28
|
require 'rails_best_practices/lexicals'
|
30
29
|
require 'rails_best_practices/prepares'
|
31
30
|
require 'rails_best_practices/reviews'
|
@@ -113,7 +112,7 @@ module RailsBestPractices
|
|
113
112
|
# @return [Array] all files for prepare process
|
114
113
|
def prepare_files
|
115
114
|
@prepare_files ||= begin
|
116
|
-
['app/models', 'app/mailers', 'db/schema.rb'].inject([]) { |files, name|
|
115
|
+
['app/models', 'app/mailers', 'db/schema.rb', 'app/controllers'].inject([]) { |files, name|
|
117
116
|
files += expand_dirs_to_files(File.join(@path, name))
|
118
117
|
}.compact
|
119
118
|
end
|
@@ -223,10 +222,12 @@ module RailsBestPractices
|
|
223
222
|
end
|
224
223
|
|
225
224
|
def output_html_errors
|
226
|
-
|
225
|
+
require 'erubis'
|
226
|
+
template = File.read(File.join(File.dirname(__FILE__), "..", "assets", "result.html.erb"))
|
227
227
|
|
228
228
|
File.open("rails_best_practices_output.html", "w+") do |file|
|
229
|
-
|
229
|
+
eruby = Erubis::Eruby.new(template)
|
230
|
+
file.puts eruby.evaluate(:errors => @runner.errors, :textmate => @options["with-textmate"], :mvim => @options["with-mvim"])
|
230
231
|
end
|
231
232
|
end
|
232
233
|
end
|
@@ -8,6 +8,8 @@ require 'rails_best_practices/core/models'
|
|
8
8
|
require 'rails_best_practices/core/model_associations'
|
9
9
|
require 'rails_best_practices/core/model_attributes'
|
10
10
|
require 'rails_best_practices/core/mailers'
|
11
|
+
require 'rails_best_practices/core/methods'
|
12
|
+
require 'rails_best_practices/core/controllers'
|
11
13
|
|
12
14
|
require 'rails_best_practices/core_ext/sexp'
|
13
15
|
require 'rails_best_practices/core_ext/enumerable'
|
@@ -9,7 +9,7 @@ module RailsBestPractices
|
|
9
9
|
MAILER_FILES = /models\/.*mailer\.rb$|mailers\/.*mailer\.rb/
|
10
10
|
VIEW_FILES = /views\/.*\.(erb|haml)$/
|
11
11
|
PARTIAL_VIEW_FILES = /views\/.*\/_.*\.(erb|haml)$/
|
12
|
-
|
12
|
+
ROUTE_FILES = /config\/routes(.*)?\.rb/
|
13
13
|
SCHEMA_FILE = /db\/schema\.rb/
|
14
14
|
HELPER_FILES = /helpers.*\.rb$/
|
15
15
|
|
@@ -83,6 +83,32 @@ module RailsBestPractices
|
|
83
83
|
super
|
84
84
|
end
|
85
85
|
end
|
86
|
+
|
87
|
+
module Classable
|
88
|
+
# remember module name.
|
89
|
+
def start_module(node)
|
90
|
+
modules << node.module_name
|
91
|
+
end
|
92
|
+
|
93
|
+
# end of the module.
|
94
|
+
def end_module(node)
|
95
|
+
modules.pop
|
96
|
+
end
|
97
|
+
|
98
|
+
# get the class name with module name.
|
99
|
+
def class_name(node)
|
100
|
+
class_name = node.class_name.to_s
|
101
|
+
if modules.empty?
|
102
|
+
class_name
|
103
|
+
else
|
104
|
+
modules.map { |modu| "#{modu}::" }.join("") + class_name
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def modules
|
109
|
+
@moduels ||= []
|
110
|
+
end
|
111
|
+
end
|
86
112
|
end
|
87
113
|
end
|
88
114
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module RailsBestPractices
|
3
|
+
module Core
|
4
|
+
class Methods
|
5
|
+
def initialize
|
6
|
+
@methods = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_method(model_name, method_name)
|
10
|
+
@methods[model_name] ||= []
|
11
|
+
@methods[model_name] << method_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_methods(model_name)
|
15
|
+
@methods[model_name] ||= []
|
16
|
+
@methods[model_name].to_a
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_method?(model_name, method_name)
|
20
|
+
@methods[model_name] ||= []
|
21
|
+
@methods[model_name].include? method_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -126,6 +126,8 @@ module RailsBestPractices
|
|
126
126
|
content = Haml::Engine.new(content).precompiled
|
127
127
|
# remove \xxx characters
|
128
128
|
content.gsub!(/\\\d{3}/, '')
|
129
|
+
rescue LoadError
|
130
|
+
raise "In order to parse #{filename}, please install the haml gem"
|
129
131
|
rescue Haml::Error
|
130
132
|
# do nothing, just ignore the wrong haml files.
|
131
133
|
end
|
@@ -149,7 +151,7 @@ module RailsBestPractices
|
|
149
151
|
|
150
152
|
# load all prepares.
|
151
153
|
def load_prepares
|
152
|
-
[Prepares::ModelPrepare.new, Prepares::MailerPrepare.new, Prepares::SchemaPrepare.new]
|
154
|
+
[Prepares::ModelPrepare.new, Prepares::MailerPrepare.new, Prepares::SchemaPrepare.new, Prepares::ControllerPrepare.new]
|
153
155
|
end
|
154
156
|
|
155
157
|
# load all reviews according to configuration.
|
@@ -120,6 +120,21 @@ class Sexp
|
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
|
+
# Get the module name of the module node.
|
124
|
+
#
|
125
|
+
# s(:module,
|
126
|
+
# s(:const_ref, s(:@const, "Admin", s(1, 7))),
|
127
|
+
# s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
|
128
|
+
# )
|
129
|
+
# => s(:const_ref, s(:@const, "Admin", s(1, 7))),
|
130
|
+
#
|
131
|
+
# @return [Sexp] module name node
|
132
|
+
def module_name
|
133
|
+
if :module == sexp_type
|
134
|
+
self[1]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
123
138
|
# Get the class name of the class node.
|
124
139
|
#
|
125
140
|
# s(:class,
|
@@ -234,6 +249,8 @@ class Sexp
|
|
234
249
|
self[4]
|
235
250
|
when :method_add_arg
|
236
251
|
self[2].arguments
|
252
|
+
when :method_add_block
|
253
|
+
self[1].arguments
|
237
254
|
when :arg_paren
|
238
255
|
self[1]
|
239
256
|
when :array
|
@@ -591,14 +608,16 @@ class Sexp
|
|
591
608
|
if :array == sexp_type
|
592
609
|
first_node = self[1]
|
593
610
|
array_size = 0
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
if :
|
599
|
-
|
611
|
+
if first_node
|
612
|
+
while true
|
613
|
+
array_size += 1
|
614
|
+
first_node = s(:args_new) == first_node[1] ? first_node[2] : first_node[1]
|
615
|
+
if :args_add != first_node.sexp_type
|
616
|
+
if :array == first_node.sexp_type
|
617
|
+
array_size += first_node.array_size
|
618
|
+
end
|
619
|
+
break
|
600
620
|
end
|
601
|
-
break
|
602
621
|
end
|
603
622
|
end
|
604
623
|
array_size
|
@@ -619,7 +638,13 @@ class Sexp
|
|
619
638
|
def to_object
|
620
639
|
case sexp_type
|
621
640
|
when :array
|
622
|
-
|
641
|
+
if nil == self[1]
|
642
|
+
[]
|
643
|
+
else
|
644
|
+
arguments.all.map(&:to_s)
|
645
|
+
end
|
646
|
+
else
|
647
|
+
to_s
|
623
648
|
end
|
624
649
|
end
|
625
650
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'rails_best_practices/prepares/model_prepare'
|
3
3
|
require 'rails_best_practices/prepares/mailer_prepare'
|
4
4
|
require 'rails_best_practices/prepares/schema_prepare'
|
5
|
+
require 'rails_best_practices/prepares/controller_prepare'
|
5
6
|
|
6
7
|
module RailsBestPractices
|
7
8
|
module Prepares
|
@@ -23,6 +24,18 @@ module RailsBestPractices
|
|
23
24
|
def mailers
|
24
25
|
@mailers ||= Core::Mailers.new
|
25
26
|
end
|
27
|
+
|
28
|
+
def controllers
|
29
|
+
@controllers ||= Core::Controllers.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def controller_methods
|
33
|
+
@controller_methods ||= Core::Methods.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear
|
37
|
+
@models = @model_associations = @model_attributes = @mailers = @controllers = @controller_methods = nil
|
38
|
+
end
|
26
39
|
end
|
27
40
|
end
|
28
41
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rails_best_practices/core/check'
|
3
|
+
|
4
|
+
module RailsBestPractices
|
5
|
+
module Prepares
|
6
|
+
# Remember controllers and controller methods
|
7
|
+
class ControllerPrepare < Core::Check
|
8
|
+
include Core::Check::Classable
|
9
|
+
|
10
|
+
DEFAULT_ACTIONS = %w(index show new create edit update destroy)
|
11
|
+
|
12
|
+
def interesting_nodes
|
13
|
+
[:module, :class, :def, :command, :var_ref]
|
14
|
+
end
|
15
|
+
|
16
|
+
def interesting_files
|
17
|
+
CONTROLLER_FILES
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@controllers = Prepares.controllers
|
22
|
+
@methods = Prepares.controller_methods
|
23
|
+
@inherited_resources = false
|
24
|
+
end
|
25
|
+
|
26
|
+
# check class node to remember the class name.
|
27
|
+
# also check if the controller is inherit from InheritedResources::Base.
|
28
|
+
def start_class(node)
|
29
|
+
@class_name = class_name(node)
|
30
|
+
@controllers << @class_name
|
31
|
+
if "InheritedResources::Base" == node.base_class.to_s
|
32
|
+
@inherited_resources = true
|
33
|
+
@actions = DEFAULT_ACTIONS
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# remember the action names at the end of class node if the controller is a InheritedResources.
|
38
|
+
def end_class(node)
|
39
|
+
if @inherited_resources
|
40
|
+
@actions.each do |action|
|
41
|
+
@methods.add_method(@class_name, action)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# check if there is a DSL call inherit_resources.
|
47
|
+
def start_var_ref(node)
|
48
|
+
if "inherit_resources" == node.to_s
|
49
|
+
@inherited_resources = true
|
50
|
+
@actions = DEFAULT_ACTIONS
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# restrict actions for inherited_resources
|
55
|
+
def start_command(node)
|
56
|
+
if @inherited_resources && "actions" == node.message.to_s
|
57
|
+
@actions = node.arguments.all.map(&:to_s)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# check def node to remember all methods.
|
62
|
+
#
|
63
|
+
# the remembered methods (@methods) are like
|
64
|
+
# {
|
65
|
+
# "Post" => ["create", "destroy"],
|
66
|
+
# "Comment" => ["create"]
|
67
|
+
# }
|
68
|
+
def start_def(node)
|
69
|
+
method_name = node.method_name.to_s
|
70
|
+
@methods.add_method(@class_name, method_name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|