rails_best_practices 1.0.1 → 1.1.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/.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
|