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.
Files changed (35) hide show
  1. data/.gitignore +0 -1
  2. data/.travis.yml +1 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +45 -0
  5. data/README.md +2 -0
  6. data/assets/result.html.erb +78 -0
  7. data/lib/rails_best_practices.rb +5 -4
  8. data/lib/rails_best_practices/core.rb +2 -0
  9. data/lib/rails_best_practices/core/check.rb +27 -1
  10. data/lib/rails_best_practices/core/controllers.rb +7 -0
  11. data/lib/rails_best_practices/core/methods.rb +26 -0
  12. data/lib/rails_best_practices/core/runner.rb +3 -1
  13. data/lib/rails_best_practices/core_ext/sexp.rb +33 -8
  14. data/lib/rails_best_practices/prepares.rb +13 -0
  15. data/lib/rails_best_practices/prepares/controller_prepare.rb +74 -0
  16. data/lib/rails_best_practices/prepares/mailer_prepare.rb +3 -2
  17. data/lib/rails_best_practices/prepares/model_prepare.rb +12 -10
  18. data/lib/rails_best_practices/reviews.rb +1 -0
  19. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +1 -1
  20. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +1 -1
  21. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +1 -1
  22. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +149 -0
  23. data/lib/rails_best_practices/version.rb +1 -1
  24. data/rails_best_practices.gemspec +3 -3
  25. data/rails_best_practices.yml +1 -0
  26. data/spec/rails_best_practices/core/controllers_spec.rb +5 -0
  27. data/spec/rails_best_practices/core/methods_spec.rb +22 -0
  28. data/spec/rails_best_practices/core_ext/sexp_spec.rb +28 -1
  29. data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +92 -0
  30. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +22 -0
  31. data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +4 -4
  32. data/spec/rails_best_practices/reviews/restrict_auto_generated_routes_review_spec.rb +334 -0
  33. data/spec/spec_helper.rb +6 -0
  34. metadata +27 -13
  35. data/assets/result.html.haml +0 -63
data/.gitignore CHANGED
@@ -6,7 +6,6 @@
6
6
  pkg/**
7
7
  *.gem
8
8
  .bundle
9
- Gemfile.lock
10
9
  rdoc/**
11
10
  doc/**
12
11
  .yardoc/**
data/.travis.yml ADDED
@@ -0,0 +1 @@
1
+ rvm: 1.9.2
data/Gemfile CHANGED
@@ -1,2 +1,4 @@
1
1
  source "http://rubygems.org"
2
2
  gemspec
3
+
4
+ gem "ripper", :platform => :mri_18
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) %>&amp;line=<%= error.line_number %>'><%= error.filename %></a>
64
+ <% elsif @mvim %>
65
+ <a href='mvim://open/?url=file://<%= File.expand_path(error.filename) %>&amp;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>
@@ -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
- template = File.read(File.join(File.dirname(__FILE__), "..", "assets", "result.html.haml"))
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
- file.puts Haml::Engine.new(template).render(Object.new, :errors => @runner.errors, :textmate => @options["with-textmate"], :mvim => @options["with-mvim"])
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
- ROUTE_FILE = /config\/routes\.rb/
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,7 @@
1
+ # encoding: utf-8
2
+ module RailsBestPractices
3
+ module Core
4
+ class Controllers < Array
5
+ end
6
+ end
7
+ 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
- while true
595
- array_size += 1
596
- first_node = s(:args_new) == first_node[1] ? first_node[2] : first_node[1]
597
- if :args_add != first_node.sexp_type
598
- if :array == first_node.sexp_type
599
- array_size += first_node.array_size
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
- arguments.all.map(&:to_s)
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