rails_best_practices 1.2.0 → 1.3.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 (29) hide show
  1. data/Gemfile.lock +2 -2
  2. data/README.md +1 -0
  3. data/assets/result.html.erb +24 -6
  4. data/lib/rails_best_practices.rb +19 -3
  5. data/lib/rails_best_practices/command.rb +4 -0
  6. data/lib/rails_best_practices/core/error.rb +1 -0
  7. data/lib/rails_best_practices/core/methods.rb +14 -2
  8. data/lib/rails_best_practices/core/runner.rb +2 -2
  9. data/lib/rails_best_practices/core_ext/sexp.rb +47 -2
  10. data/lib/rails_best_practices/prepares/controller_prepare.rb +9 -1
  11. data/lib/rails_best_practices/prepares/model_prepare.rb +15 -3
  12. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +58 -5
  13. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +32 -24
  14. data/lib/rails_best_practices/reviews/review.rb +2 -1
  15. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +3 -1
  16. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +1 -1
  17. data/lib/rails_best_practices/version.rb +1 -2
  18. data/rails_best_practices.yml +2 -2
  19. data/rake_rubies.sh +3 -4
  20. data/spec/rails_best_practices/core/runner_spec.rb +14 -5
  21. data/spec/rails_best_practices/core_ext/sexp_spec.rb +45 -0
  22. data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +11 -0
  23. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +35 -0
  24. data/spec/rails_best_practices/reviews/move_code_into_model_review_spec.rb +11 -2
  25. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +166 -0
  26. data/spec/rails_best_practices/reviews/restrict_auto_generated_routes_review_spec.rb +20 -0
  27. data/spec/rails_best_practices/reviews/simplify_render_in_views_review_spec.rb +11 -3
  28. data/spec/spec_helper.rb +2 -0
  29. metadata +3 -3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rails_best_practices (1.2.0)
4
+ rails_best_practices (1.3.0)
5
5
  activesupport
6
6
  colored
7
7
  erubis
@@ -12,7 +12,7 @@ PATH
12
12
  GEM
13
13
  remote: http://rubygems.org/
14
14
  specs:
15
- activesupport (3.0.7)
15
+ activesupport (3.0.10)
16
16
  colored (1.2)
17
17
  diff-lcs (1.1.2)
18
18
  erubis (2.7.0)
data/README.md CHANGED
@@ -23,6 +23,7 @@ By default rails_best_practices will do parse codes in vendor, spec, test and fe
23
23
  --without-color only output plain text without color
24
24
  --with-textmate open file by textmate in html format
25
25
  --with-mvim open file by mvim in html format
26
+ --with-git display git commit and git username in html format
26
27
  --vendor include vendor files
27
28
  --spec include spec files
28
29
  --test include test files
@@ -12,11 +12,14 @@
12
12
  h1 {
13
13
  color: ##4E4E4E;
14
14
  }
15
+ p {
16
+ margin: 5px 0;
17
+ }
15
18
  table {
16
19
  background: white;
17
20
  border: 1px solid #666;
18
21
  border-collapse: collapse;
19
- margin: 20px 0;
22
+ margin: 10px 0;
20
23
  font-size: 14px;
21
24
  }
22
25
  table th, table td {
@@ -33,26 +36,33 @@
33
36
  table tr:hover {
34
37
  background-color: #FFFFC0;
35
38
  }
39
+ ul {
40
+ clear: both;
41
+ display: inline-block;
42
+ padding: 0;
43
+ margin: 0;
44
+ }
36
45
  ul li {
37
46
  list-style: none;
38
47
  display: none;
48
+ float: left;
39
49
  }
40
50
  </style>
41
51
  </head>
42
52
  <body>
43
53
  <h1>rails_best_practices output</h1>
44
- <h2>
54
+ <p>
45
55
  Please go to
46
56
  <a href='http://rails-bestpractices.com' target='_blank'>http://rails-bestpractices.com</a>
47
57
  to see more useful Rails Best Practices.
48
- </h2>
49
- <h2>
58
+ </p>
59
+ <p>
50
60
  <% if @errors.empty? %>
51
61
  No error found. Cool!
52
62
  <% else %>
53
- Found <%= @errors.size %> errors.
63
+ Found <%= @errors.size %> warnings.
54
64
  <% end %>
55
- </h2>
65
+ </p>
56
66
  <ul>
57
67
  <% @error_types.each do |error_type| %>
58
68
  <li><input type="checkbox" value="<%= error_type.split(':').last %>" /><%= error_type.split(':').last %></li>
@@ -63,6 +73,10 @@
63
73
  <th>Filename</th>
64
74
  <th>Line Number</th>
65
75
  <th>Warning Message</th>
76
+ <% if @git %>
77
+ <th>Git Commit</th>
78
+ <th>Git Username</th>
79
+ <% end %>
66
80
  </tr>
67
81
  <% @errors.each do |error| %>
68
82
  <tr class="<%= error.type.split(':').last %>">
@@ -79,6 +93,10 @@
79
93
  <td class='message'>
80
94
  <a href='<%= error.url %>' target='_blank'><%= error.message %></a>
81
95
  </td>
96
+ <% if @git %>
97
+ <td class='git_commit'><%= error.git_commit %></td>
98
+ <td class='git_usename'><%= error.git_username %></td>
99
+ <% end %>
82
100
  </tr>
83
101
  <% end %>
84
102
  </table>
@@ -81,12 +81,13 @@ module RailsBestPractices
81
81
  plain_output("AlwaysAddDbIndexReview is disabled as there is no db/schema.rb file in your rails project.", 'blue')
82
82
  end
83
83
 
84
- @bar = ProgressBar.new('Analyzing', lexical_files.size + prepare_files.size + review_files.size)
84
+ @bar = ProgressBar.new('Source Codes', lexical_files.size + prepare_files.size + review_files.size)
85
85
  ["lexical", "prepare", "review"].each { |process| send(:process, process) }
86
86
  @runner.on_complete
87
87
  @bar.finish
88
88
 
89
89
  if @options['format'] == 'html'
90
+ load_git_info if @options["with-git"]
90
91
  output_html_errors
91
92
  else
92
93
  output_terminal_errors
@@ -148,7 +149,7 @@ module RailsBestPractices
148
149
  # @param [Array] dirs what directories to expand
149
150
  # @return [Array] all files expanded
150
151
  def expand_dirs_to_files *dirs
151
- extensions = ['rb', 'erb', 'rhtml', 'haml', 'builder']
152
+ extensions = ['rb', 'erb', 'rake', 'rhtml', 'haml', 'builder']
152
153
 
153
154
  dirs.flatten.map { |entry|
154
155
  next unless File.exist? entry
@@ -214,6 +215,21 @@ module RailsBestPractices
214
215
  end
215
216
  end
216
217
 
218
+ # load git commit and git username info.
219
+ def load_git_info
220
+ git_progressbar = ProgressBar.new('Git Info', @runner.errors.size)
221
+ @runner.errors.each do |error|
222
+ git_info = `cd #{@runner.class.base_path}; git blame #{error.filename[@runner.class.base_path.size..-1]} | sed -n #{error.line_number.split(',').first}p`
223
+ unless git_info == ""
224
+ git_commit, git_username = git_info.split(/\d{4}-\d{2}-\d{2}/).first.split("(")
225
+ error.git_commit = git_commit.split(" ").first.strip
226
+ error.git_username = git_username.strip
227
+ end
228
+ git_progressbar.inc unless @options['debug']
229
+ end
230
+ git_progressbar.finish
231
+ end
232
+
217
233
  # output errors with html format.
218
234
  def output_html_errors
219
235
  require 'erubis'
@@ -221,7 +237,7 @@ module RailsBestPractices
221
237
 
222
238
  File.open("rails_best_practices_output.html", "w+") do |file|
223
239
  eruby = Erubis::Eruby.new(template)
224
- file.puts eruby.evaluate(:errors => @runner.errors, :error_types => error_types, :textmate => @options["with-textmate"], :mvim => @options["with-mvim"])
240
+ file.puts eruby.evaluate(:errors => @runner.errors, :error_types => error_types, :textmate => @options["with-textmate"], :mvim => @options["with-mvim"], :git => @options["with-git"])
225
241
  end
226
242
  end
227
243
 
@@ -40,6 +40,10 @@ OptionParser.new do |opts|
40
40
  options["with-mvim"] = true
41
41
  end
42
42
 
43
+ opts.on("--with-git", "display git commit and username, only support html format") do
44
+ options["with-git"] = true
45
+ end
46
+
43
47
  ['vendor', 'spec', 'test', 'features'].each do |pattern|
44
48
  opts.on("--#{pattern}", "include #{pattern} files") do
45
49
  options[pattern] = true
@@ -6,6 +6,7 @@ module RailsBestPractices
6
6
  # it indicates the filenname, line number and error message for the violation.
7
7
  class Error
8
8
  attr_reader :filename, :line_number, :message, :type, :url
9
+ attr_accessor :git_commit, :git_username
9
10
 
10
11
  def initialize(filename, line_number, message, type, url = nil)
11
12
  @filename = filename
@@ -53,15 +53,27 @@ module RailsBestPractices
53
53
  #
54
54
  # @param [String] class name
55
55
  # @param [String] method name
56
- def mark_extend_class_method_used(class_name, method_name)
56
+ def mark_parent_class_method_used(class_name, method_name)
57
57
  klass = Prepares.klasses.find { |klass| klass.to_s == class_name }
58
58
  if klass && klass.extend_class_name
59
- mark_extend_class_method_used(klass.extend_class_name, method_name)
59
+ mark_parent_class_method_used(klass.extend_class_name, method_name)
60
60
  method = get_method(klass.extend_class_name, method_name)
61
61
  method.mark_used if method
62
62
  end
63
63
  end
64
64
 
65
+ # Mark sub classes' method as used.
66
+ #
67
+ # @param [String] class name
68
+ # @param [String] method name
69
+ def mark_subclasses_method_used(class_name, method_name)
70
+ Prepares.klasses.select { |klass| klass.extend_class_name == class_name }.each do |klass|
71
+ mark_subclasses_method_used(klass.to_s, method_name)
72
+ method = get_method(klass.to_s, method_name)
73
+ method.mark_used if method
74
+ end
75
+ end
76
+
65
77
  # remomber the method name, the method is probably be used for the class' public method.
66
78
  #
67
79
  # @param [String] method name
@@ -159,7 +159,7 @@ module RailsBestPractices
159
159
  content.gsub!(/\\\d{3}/, '')
160
160
  rescue LoadError
161
161
  raise "In order to parse #{filename}, please install the haml gem"
162
- rescue Haml::Error
162
+ rescue Haml::Error, SyntaxError
163
163
  # do nothing, just ignore the wrong haml files.
164
164
  end
165
165
  end
@@ -202,7 +202,7 @@ module RailsBestPractices
202
202
  # load all plugin reviews.
203
203
  def load_plugin_reviews
204
204
  begin
205
- plugins = "#{Runner.base_path}lib/rails_best_practices/plugins/reviews"
205
+ plugins = File.join(Runner.base_path, 'lib', 'rails_best_practices', 'plugins', 'reviews')
206
206
  if File.directory?(plugins)
207
207
  Dir[File.expand_path(File.join(plugins, "*.rb"))].each do |review|
208
208
  require review
@@ -22,8 +22,11 @@ class Sexp
22
22
  # => 2
23
23
  def line
24
24
  if [:def, :command, :command_call, :call, :fcall, :method_add_arg, :method_add_block,
25
- :var_ref, :const_ref, :const_path_ref, :class, :module, :if, :unless, :elsif, :binary].include? sexp_type
25
+ :var_ref, :const_ref, :const_path_ref, :class, :module, :if, :unless, :elsif, :binary,
26
+ :alias, :symbol_literal, :symbol, :aref].include? sexp_type
26
27
  self[1].line
28
+ elsif :array == sexp_type
29
+ array_values[0].line
27
30
  else
28
31
  self.last.first if self.last.is_a? Array
29
32
  end
@@ -294,7 +297,7 @@ class Sexp
294
297
  when :args_add_block, :array
295
298
  node = self[1]
296
299
  while true
297
- if :args_add == node.sexp_type
300
+ if [:args_add, :args_add_star].include? node.sexp_type
298
301
  nodes.unshift node[2]
299
302
  node = node[1]
300
303
  elsif :args_new == node.sexp_type
@@ -675,12 +678,48 @@ class Sexp
675
678
  if :array == sexp_type
676
679
  if nil == self[1]
677
680
  []
681
+ elsif :qwords_add == self[1].sexp_type
682
+ self[1].array_values
678
683
  else
679
684
  arguments.all
680
685
  end
686
+ elsif :qwords_add
687
+ values = []
688
+ node = self
689
+ while true
690
+ if :qwords_add == node.sexp_type
691
+ values.unshift node[2]
692
+ node = node[1]
693
+ elsif :qwords_new == node.sexp_type
694
+ break
695
+ end
696
+ end
697
+ values
681
698
  end
682
699
  end
683
700
 
701
+ # old method for alias node.
702
+ #
703
+ # s(:alias,
704
+ # s(:symbol_literal, s(:@ident, "new", s(1, 6))),
705
+ # s(:symbol_literal, s(:@ident, "old", s(1, 10)))
706
+ # )
707
+ # => s(:symbol_literal, s(:@ident, "old", s(1, 10))),
708
+ def old_method
709
+ self[2]
710
+ end
711
+
712
+ # new method for alias node.
713
+ #
714
+ # s(:alias,
715
+ # s(:symbol_literal, s(:@ident, "new", s(1, 6))),
716
+ # s(:symbol_literal, s(:@ident, "old", s(1, 10)))
717
+ # )
718
+ # => s(:symbol_literal, s(:@ident, "new", s(1, 6))),
719
+ def new_method
720
+ self[1]
721
+ end
722
+
684
723
  # To object.
685
724
  #
686
725
  # s(:array,
@@ -726,6 +765,12 @@ class Sexp
726
765
  else
727
766
  self[1].to_s
728
767
  end
768
+ when :qwords_add
769
+ if s(:qwords_new) == self[1]
770
+ self[2].to_s
771
+ else
772
+ self[1].to_s
773
+ end
729
774
  when :const_path_ref
730
775
  "#{self[1]}::#{self[2]}"
731
776
  when :@label
@@ -54,7 +54,15 @@ module RailsBestPractices
54
54
  # restrict actions for inherited_resources
55
55
  def start_command(node)
56
56
  if @inherited_resources && "actions" == node.message.to_s
57
- @actions = node.arguments.all.map(&:to_s)
57
+ if "all" == node.arguments.all[0].to_s
58
+ @actions = DEFAULT_ACTIONS
59
+ option_argument = node.arguments.all[1]
60
+ if :bare_assoc_hash == option_argument.sexp_type && option_argument.hash_value("except")
61
+ @actions -= option_argument.hash_value("except").to_object
62
+ end
63
+ else
64
+ @actions = node.arguments.all.map(&:to_s)
65
+ end
58
66
  end
59
67
  end
60
68
 
@@ -11,7 +11,7 @@ module RailsBestPractices
11
11
  ASSOCIATION_METHODS = %w(belongs_to has_one has_many has_and_belongs_to_many)
12
12
 
13
13
  def interesting_nodes
14
- [:module, :class, :def, :command, :var_ref]
14
+ [:module, :class, :def, :command, :var_ref, :alias]
15
15
  end
16
16
 
17
17
  def interesting_files
@@ -62,14 +62,26 @@ module RailsBestPractices
62
62
  # }
63
63
  # }
64
64
  def start_command(node)
65
- if %w(named_scope scope).include? node.message.to_s
65
+ case node.message.to_s
66
+ when *%w(named_scope scope alias_method)
66
67
  method_name = node.arguments.all[0].to_s
67
68
  @methods.add_method(current_class_name, method_name, {"file" => node.file, "line" => node.line}, current_access_control)
68
- elsif ASSOCIATION_METHODS.include? node.message.to_s
69
+ when "alias_method_chain"
70
+ method, feature = *node.arguments.all.map(&:to_s)
71
+ @methods.add_method(current_class_name, "#{method}_with_#{feature}", {"file" => node.file, "line" => node.line}, current_access_control)
72
+ @methods.add_method(current_class_name, "#{method}", {"file" => node.file, "line" => node.line}, current_access_control)
73
+ when *ASSOCIATION_METHODS
69
74
  remember_association(node)
75
+ else
70
76
  end
71
77
  end
72
78
 
79
+ # check alias node to remembr the alias methods.
80
+ def start_alias(node)
81
+ method_name = node.new_method.to_s
82
+ @methods.add_method(current_class_name, method_name, {"file" => node.file, "line" => node.line}, current_access_control)
83
+ end
84
+
73
85
  private
74
86
  # remember associations, with class to association names.
75
87
  def remember_association(node)
@@ -3,14 +3,22 @@ require 'rails_best_practices/reviews/review'
3
3
 
4
4
  module RailsBestPractices
5
5
  module Reviews
6
+ # Find out unused methods in models.
7
+ #
8
+ # Implemenation:
9
+ #
10
+ # Review process:
11
+ # remember all method calls,
12
+ # at end, check if all defined methods are called,
13
+ # if not, non called methods are unused.
6
14
  class RemoveUnusedMethodsInModelsReview < Review
7
15
  include Klassable
8
16
  include Completeable
9
17
 
10
- EXCEPT_METHODS = %w(initialize validate to_xml to_json)
18
+ EXCEPT_METHODS = %w(initialize validate to_xml to_json assign_attributes after_find after_initialize)
11
19
 
12
20
  def interesting_nodes
13
- [:module, :class, :call, :fcall, :command, :method_add_arg, :var_ref]
21
+ [:module, :class, :call, :fcall, :command, :command_call, :method_add_arg, :var_ref, :alias, :bare_assoc_hash]
14
22
  end
15
23
 
16
24
  def initialize(options={})
@@ -19,32 +27,76 @@ module RailsBestPractices
19
27
  @except_methods = EXCEPT_METHODS + options['except_methods']
20
28
  end
21
29
 
30
+ # remember the message of call node.
22
31
  def start_call(node)
23
32
  mark_used(node.message)
24
33
  end
25
34
 
35
+ # remember the message of fcall node.
26
36
  def start_fcall(node)
27
37
  mark_used(node.message)
28
38
  end
29
39
 
40
+ # remember name of var_ref node.
30
41
  def start_var_ref(node)
31
42
  mark_used(node)
32
43
  end
33
44
 
45
+ # remember the message of command call node.
46
+ def start_command_call(node)
47
+ mark_used(node.message)
48
+ end
49
+
50
+ # remember the message of command node.
51
+ # remember the argument of alias_method and alias_method_chain as well.
34
52
  def start_command(node)
35
- unless %w(named_scope scope).include? node.message.to_s
53
+ case node.message.to_s
54
+ when "named_scope", "scope"
55
+ # nothing
56
+ when "alias_method"
57
+ mark_used(node.arguments.all[1])
58
+ when "alias_method_chain"
59
+ method, feature = *node.arguments.all.map(&:to_s)
60
+ call_method("#{method}_with_#{feature}")
61
+ else
36
62
  mark_used(node.message)
37
63
  node.arguments.all.each { |argument| mark_used(argument) }
38
64
  end
39
65
  end
40
66
 
67
+ # remember the old method of alias node.
68
+ def start_alias(node)
69
+ mark_used(node.old_method)
70
+ end
71
+
72
+ # remember hash values for hash key "methods".
73
+ #
74
+ # def to_xml(options = {})
75
+ # super options.merge(:exclude => :visible, :methods => [:is_discussion_conversation])
76
+ # end
77
+ def start_bare_assoc_hash(node)
78
+ if node.hash_keys.include? "methods"
79
+ mark_used(node.hash_value("methods"))
80
+ end
81
+ end
82
+
83
+ # remember the first argument for try and send method.
41
84
  def start_method_add_arg(node)
42
- if "try" == node.message.to_s
85
+ case node.message.to_s
86
+ when "try"
43
87
  method_name = node.arguments.all[0].to_s
44
88
  call_method(method_name)
89
+ when "send"
90
+ if [:symbol_literal, :string_literal].include?(node.arguments.all[0].sexp_type)
91
+ method_name = node.arguments.all[0].to_s
92
+ call_method(method_name)
93
+ end
94
+ else
95
+ # nothing
45
96
  end
46
97
  end
47
98
 
99
+ # get all unused methods at the end of review process.
48
100
  def on_complete
49
101
  @model_methods.get_all_unused_methods.each do |method|
50
102
  if !@except_methods.include?(method.method_name) && method.method_name !~ /=$/
@@ -69,7 +121,8 @@ module RailsBestPractices
69
121
  if @model_methods.has_method?(current_class_name, method_name)
70
122
  @model_methods.get_method(current_class_name, method_name).mark_used
71
123
  end
72
- @model_methods.mark_extend_class_method_used(current_class_name, method_name)
124
+ @model_methods.mark_parent_class_method_used(current_class_name, method_name)
125
+ @model_methods.mark_subclasses_method_used(current_class_name, method_name)
73
126
  @model_methods.possible_public_used(method_name)
74
127
  end
75
128
  end
@@ -86,10 +86,10 @@ module RailsBestPractices
86
86
 
87
87
  # get the controller name.
88
88
  def controller_name(node)
89
- if node.arguments.all.size > 1
90
- options = node.arguments.all[1]
91
- if options.hash_keys.include?("controller")
92
- name = options.hash_value("controller").to_s
89
+ if option_with_hash(node)
90
+ option_node = node.arguments.all[1]
91
+ if hash_key_exist?(option_node,"controller")
92
+ name = option_node.hash_value("controller").to_s
93
93
  else
94
94
  name = node.arguments.all[0].to_s.tableize
95
95
  end
@@ -111,39 +111,47 @@ module RailsBestPractices
111
111
 
112
112
  # get the route actions that should be generated by resources call.
113
113
  def resources_methods(node)
114
- resources_methods = RESOURCES_METHODS
115
-
116
- if node.arguments.all.size > 1
117
- options = node.arguments.all[1]
118
- if options.hash_keys.include?("only")
119
- Array(options.hash_value("only").to_object)
120
- elsif options.hash_keys.include?("except")
121
- resources_methods - Array(options.hash_value("except").to_object)
114
+ methods = RESOURCES_METHODS
115
+
116
+ if option_with_hash(node)
117
+ option_node = node.arguments.all[1]
118
+ if hash_key_exist?(option_node, "only")
119
+ option_node.hash_value("only").to_s == "none" ? [] : Array(option_node.hash_value("only").to_object)
120
+ elsif hash_key_exist?(option_node, "except")
121
+ option_node.hash_value("except").to_s == "all" ? [] : (methods - Array(option_node.hash_value("except").to_object))
122
122
  else
123
- resources_methods
123
+ methods
124
124
  end
125
125
  else
126
- resources_methods
126
+ methods
127
127
  end
128
128
  end
129
129
 
130
130
  # get the route actions that should be generated by resource call.
131
131
  def resource_methods(node)
132
- resource_methods = RESOURCE_METHODS
133
-
134
- if node.arguments.all.size > 1
135
- options = node.arguments.all[1]
136
- if options.hash_keys.include?("only")
137
- Array(options.hash_value("only").to_object)
138
- elsif options.hash_keys.include?("except")
139
- resource_methods - Array(options.hash_value("except").to_object)
132
+ methods = RESOURCE_METHODS
133
+
134
+ if option_with_hash(node)
135
+ option_node = node.arguments.all[1]
136
+ if hash_key_exist?(option_node, "only")
137
+ option_node.hash_value("only").to_s == "none" ? [] : Array(option_node.hash_value("only").to_object)
138
+ elsif hash_key_exist?(option_node, "except")
139
+ option_node.hash_value("except").to_s == "all" ? [] : (methods - Array(option_node.hash_value("except").to_object))
140
140
  else
141
- resource_methods
141
+ methods
142
142
  end
143
143
  else
144
- resource_methods
144
+ methods
145
145
  end
146
146
  end
147
+
148
+ def option_with_hash(node)
149
+ node.arguments.all.size > 1 && :bare_assoc_hash == node.arguments.all[1].sexp_type
150
+ end
151
+
152
+ def hash_key_exist?(node, key)
153
+ node.hash_keys && node.hash_keys.include?(key)
154
+ end
147
155
  end
148
156
  end
149
157
  end
@@ -16,7 +16,8 @@ module RailsBestPractices
16
16
  # then save it to as key in @variable_use_count hash, and add the call count (hash value).
17
17
  def remember_variable_use_count(node)
18
18
  variable_node = variable(node)
19
- if variable_node && "self" != variable_node.to_s
19
+ if variable_node && "self" != variable_node.to_s && @last_variable_node != variable_node
20
+ @last_variable_node = variable_node
20
21
  variable_use_count[variable_node.to_s] ||= 0
21
22
  variable_use_count[variable_node.to_s] += 1
22
23
  end
@@ -31,7 +31,9 @@ module RailsBestPractices
31
31
  def start_command(node)
32
32
  if "render" == node.message.to_s
33
33
  hash_node = node.arguments.all[0]
34
- if hash_node && hash_node.hash_keys && hash_node.hash_keys.include?("partial")
34
+ if hash_node && :bare_assoc_hash == hash_node.sexp_type &&
35
+ hash_node.hash_keys.include?("partial") &&
36
+ !hash_node.hash_value("partial").to_s.include?('/')
35
37
  add_error 'simplify render in views'
36
38
  end
37
39
  end
@@ -27,7 +27,7 @@ module RailsBestPractices
27
27
 
28
28
  def initialize(options = {})
29
29
  super()
30
- @customize_count = options['customize_count'] || 1
30
+ @customize_count = options['customize_count'] || 2
31
31
  end
32
32
 
33
33
  # check class define node to see if there are method define nodes whose first code line are duplicated.
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module RailsBestPractices
3
- VERSION = "1.2.0"
3
+ VERSION = "1.3.0"
4
4
  end
5
-
@@ -12,7 +12,7 @@ LawOfDemeterCheck: { }
12
12
  UseObserverCheck: { }
13
13
  IsolateSeedDataCheck: { }
14
14
  AlwaysAddDbIndexCheck: { }
15
- UseBeforeFilterCheck: { customize_count: 1 }
15
+ UseBeforeFilterCheck: { customize_count: 2 }
16
16
  MoveCodeIntoControllerCheck: { }
17
17
  MoveCodeIntoModelCheck: { use_count: 2 }
18
18
  MoveCodeIntoHelperCheck: { array_count: 3 }
@@ -27,4 +27,4 @@ SimplifyRenderInControllersCheck: { }
27
27
  RemoveEmptyHelpersCheck: { }
28
28
  RemoveTabCheck: { }
29
29
  RestrictAutoGeneratedRoutesCheck: { }
30
- # RemoveUnusedMethodsInModelsCheck: { except_methods: [] }
30
+ RemoveUnusedMethodsInModelsCheck: { except_methods: [] }
data/rake_rubies.sh CHANGED
@@ -1,10 +1,9 @@
1
1
  #!/bin/bash
2
- rubies=( 1.8.7 1.9.2 ree )
2
+ rubies=( 1.9.2-p180 1.9.2-p290 1.9.3-p0 )
3
3
  gemset="rails_best_practices"
4
4
 
5
5
  for x in ${rubies[*]}
6
6
  do
7
- rvm use --create $x@$gemset
8
- bundle install
9
- rake spec:progress
7
+ echo $x@$gemset
8
+ rvm $x@$gemset do bundle exec rake spec:progress
10
9
  done
@@ -1,13 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe RailsBestPractices::Core::Runner do
4
+ describe "load_plugin_reviews" do
5
+ shared_examples_for 'load_plugin_reviews' do
6
+ it "should load plugins in lib/rails_best_practices/plugins/reviews" do
7
+ runner = RailsBestPractices::Core::Runner.new
8
+ runner.instance_variable_get('@reviews').map(&:class).should include(RailsBestPractices::Plugins::Reviews::NotUseRailsRootReview)
9
+ end
10
+ end
4
11
 
5
- before { RailsBestPractices::Core::Runner.base_path = 'spec/fixtures/' }
12
+ context "given a path that ends with a slash" do
13
+ before { RailsBestPractices::Core::Runner.base_path = 'spec/fixtures/' }
14
+ it_should_behave_like 'load_plugin_reviews'
15
+ end
6
16
 
7
- describe "load_plugin_reviews" do
8
- it "should load plugins in lib/rails_best_practices/plugins/reviews" do
9
- runner = RailsBestPractices::Core::Runner.new
10
- runner.instance_variable_get('@reviews').map(&:class).should include(RailsBestPractices::Plugins::Reviews::NotUseRailsRootReview)
17
+ context "given a path that does not end with a slash" do
18
+ before { RailsBestPractices::Core::Runner.base_path = 'spec/fixtures' }
19
+ it_should_behave_like 'load_plugin_reviews'
11
20
  end
12
21
  end
13
22
  end
@@ -8,6 +8,7 @@ describe Sexp do
8
8
  def test
9
9
  ActiveRecord::Base.connection
10
10
  end
11
+ alias :test_new :test
11
12
  end
12
13
  EOF
13
14
  @node = parse_content(content)
@@ -28,6 +29,10 @@ describe Sexp do
28
29
  it "should return const path line" do
29
30
  @node.grep_node(:sexp_type => :const_path_ref).line.should == 3
30
31
  end
32
+
33
+ it "should return alias line" do
34
+ @node.grep_node(:sexp_type => :alias).line.should == 5
35
+ end
31
36
  end
32
37
 
33
38
  describe "grep_nodes" do
@@ -239,6 +244,11 @@ describe Sexp do
239
244
  node = parse_content("puts 'hello', 'world'").grep_node(:sexp_type => :args_add_block)
240
245
  node.all.map(&:to_s).should == ["hello", "world"]
241
246
  end
247
+
248
+ it "no error for args_add_star" do
249
+ node = parse_content("send(:\"\#{route}_url\", *args)").grep_node(:sexp_type => :args_add_block)
250
+ lambda { node.all }.should_not raise_error
251
+ end
242
252
  end
243
253
 
244
254
  describe "conditional_statement" do
@@ -410,6 +420,41 @@ describe Sexp do
410
420
  node = parse_content("[]").grep_node(:sexp_type => :array)
411
421
  node.array_values.should == []
412
422
  end
423
+
424
+ it "should get array value with qwords" do
425
+ node = parse_content("%w(first_name last_name)").grep_node(:sexp_type => :qwords_add)
426
+ node.array_values.map(&:to_s).should == ["first_name", "last_name"]
427
+ end
428
+ end
429
+
430
+ describe "alias" do
431
+ context "method" do
432
+ before do
433
+ @node = parse_content("alias new old").grep_node(:sexp_type => :alias)
434
+ end
435
+
436
+ it "should get old_method" do
437
+ @node.old_method.to_s.should == "old"
438
+ end
439
+
440
+ it "should get new_method" do
441
+ @node.new_method.to_s.should == "new"
442
+ end
443
+ end
444
+
445
+ context "symbol" do
446
+ before do
447
+ @node = parse_content("alias :new :old").grep_node(:sexp_type => :alias)
448
+ end
449
+
450
+ it "should get old_method" do
451
+ @node.old_method.to_s.should == "old"
452
+ end
453
+
454
+ it "should get new_method" do
455
+ @node.new_method.to_s.should == "new"
456
+ end
457
+ end
413
458
  end
414
459
 
415
460
  describe "to_object" do
@@ -89,6 +89,17 @@ describe RailsBestPractices::Prepares::ControllerPrepare do
89
89
  methods.get_methods("PostsController").map(&:method_name).should == ["index", "show"]
90
90
  end
91
91
 
92
+ it "extend inherited_resources with all actions" do
93
+ content =<<-EOF
94
+ class PostsController < InheritedResources::Base
95
+ actions :all, except: [:show]
96
+ end
97
+ EOF
98
+ runner.prepare('app/controllers/posts_controller.rb', content)
99
+ methods = RailsBestPractices::Prepares.controller_methods
100
+ methods.get_methods("PostsController").map(&:method_name).should == ["index", "new", "create", "edit", "update", "destroy"]
101
+ end
102
+
92
103
  it "DSL inherit_resources" do
93
104
  content =<<-EOF
94
105
  class PostsController
@@ -181,6 +181,41 @@ describe RailsBestPractices::Prepares::ModelPrepare do
181
181
  end
182
182
  end
183
183
 
184
+ context "alias" do
185
+ it "should treat alias as method" do
186
+ content =<<-EOF
187
+ class Post < ActiveRecord::Base
188
+ alias :new :old
189
+ end
190
+ EOF
191
+ runner.prepare("app/models/post.rb", content)
192
+ methods = RailsBestPractices::Prepares.model_methods
193
+ methods.get_methods("Post").map(&:method_name).should == ["new"]
194
+ end
195
+
196
+ it "should treat alias_method as method" do
197
+ content =<<-EOF
198
+ class Post < ActiveRecord::Base
199
+ alias_method :new, :old
200
+ end
201
+ EOF
202
+ runner.prepare("app/models/post.rb", content)
203
+ methods = RailsBestPractices::Prepares.model_methods
204
+ methods.get_methods("Post").map(&:method_name).should == ["new"]
205
+ end
206
+
207
+ it "should treat alias_method_chain as method" do
208
+ content =<<-EOF
209
+ class Post < ActiveRecord::Base
210
+ alias_method_chain :method, :feature
211
+ end
212
+ EOF
213
+ runner.prepare("app/models/post.rb", content)
214
+ methods = RailsBestPractices::Prepares.model_methods
215
+ methods.get_methods("Post").map(&:method_name).should == ["method_with_feature", "method"]
216
+ end
217
+ end
218
+
184
219
  context "no error" do
185
220
  it "should raised for finder_sql option" do
186
221
  content =<<-EOF
@@ -5,7 +5,7 @@ describe RailsBestPractices::Reviews::MoveCodeIntoModelReview do
5
5
 
6
6
  it "should move code into model" do
7
7
  content =<<-EOF
8
- <% if current_user && (current_user == @post.user || @post.editors.include?(current_user)) %>
8
+ <% if current_user && @post.user && (current_user == @post.user || @post.editors.include?(current_user)) %>
9
9
  <%= link_to 'Edit this post', edit_post_url(@post) %>
10
10
  <% end %>
11
11
  EOF
@@ -16,7 +16,7 @@ describe RailsBestPractices::Reviews::MoveCodeIntoModelReview do
16
16
 
17
17
  it "should move code into model with haml" do
18
18
  content =<<-EOF
19
- - if current_user && (current_user == @post.user || @post.editors.include?(current_user))
19
+ - if current_user && @post.user && (current_user == @post.user || @post.editors.include?(current_user))
20
20
  = link_to 'Edit this post', edit_post_url(@post)
21
21
  EOF
22
22
  runner.review('app/views/posts/show.html.haml', content)
@@ -46,4 +46,13 @@ describe RailsBestPractices::Reviews::MoveCodeIntoModelReview do
46
46
  runner.review('app/views/posts/show.html.erb', content)
47
47
  runner.should have(0).errors
48
48
  end
49
+
50
+ it "should not move code into model for multiple calls on same variable node" do
51
+ content =<<-EOF
52
+ <% if !job.company.blank? && job.company.title? %>
53
+ <% end %>
54
+ EOF
55
+ runner.review('app/views/jobs/show.html.erb', content)
56
+ runner.should have(0).errors
57
+ end
49
58
  end
@@ -259,6 +259,68 @@ describe RailsBestPractices::Reviews::RemoveUnusedMethodsInModelsReview do
259
259
  runner.on_complete
260
260
  runner.should have(0).errors
261
261
  end
262
+
263
+ it "should not remove unused methods for send" do
264
+ content =<<-EOF
265
+ class Post < ActiveRecord::Base
266
+ def find(user_id); end
267
+ end
268
+ EOF
269
+ runner.prepare('app/models/post.rb', content)
270
+ runner.review('app/models/post.rb', content)
271
+ content =<<-EOF
272
+ class PostsController < ApplicationController
273
+ def find
274
+ Post.new.send(:find, current_user.id)
275
+ end
276
+ end
277
+ EOF
278
+ runner.review('app/controllers/posts_controller.rb', content)
279
+ runner.on_complete
280
+ runner.should have(0).errors
281
+ end
282
+
283
+ it "should remove unused methods for send string_embexpre" do
284
+ content =<<-EOF
285
+ class Post < ActiveRecord::Base
286
+ def find_first; end
287
+ end
288
+ EOF
289
+ runner.prepare('app/models/post.rb', content)
290
+ runner.review('app/models/post.rb', content)
291
+ content =<<-EOF
292
+ class PostsController < ApplicationController
293
+ def find
294
+ type = "first"
295
+ Post.new.send("find_\#{type}")
296
+ end
297
+ end
298
+ EOF
299
+ runner.review('app/controllers/posts_controller.rb', content)
300
+ runner.on_complete
301
+ runner.should have(1).errors
302
+ end
303
+
304
+ it "should remove unused methods for send variable" do
305
+ content =<<-EOF
306
+ class Post < ActiveRecord::Base
307
+ def first; end
308
+ end
309
+ EOF
310
+ runner.prepare('app/models/post.rb', content)
311
+ runner.review('app/models/post.rb', content)
312
+ content =<<-EOF
313
+ class PostsController < ApplicationController
314
+ def find
315
+ type = "first"
316
+ Post.new.send(type)
317
+ end
318
+ end
319
+ EOF
320
+ runner.review('app/controllers/posts_controller.rb', content)
321
+ runner.on_complete
322
+ runner.should have(1).errors
323
+ end
262
324
  end
263
325
 
264
326
  context "protected" do
@@ -384,4 +446,108 @@ describe RailsBestPractices::Reviews::RemoveUnusedMethodsInModelsReview do
384
446
  runner.errors[0].to_s.should == "app/models/post.rb:2 - remove unused methods (Post#active)"
385
447
  end
386
448
  end
449
+
450
+ context "alias" do
451
+ it "should not remove unused method with alias" do
452
+ content =<<-EOF
453
+ class Post < ActiveRecord::Base
454
+ def old; end
455
+ alias new old
456
+ end
457
+ EOF
458
+ runner.prepare("app/models/post.rb", content)
459
+ runner.review("app/models/post.rb", content)
460
+ content =<<-EOF
461
+ class PostsController < ApplicationController
462
+ def show
463
+ @post.new
464
+ end
465
+ end
466
+ EOF
467
+ runner.review("app/controllers/posts_controller.rb", content)
468
+ runner.on_complete
469
+ runner.should have(0).errors
470
+ end
471
+
472
+ it "should not remove unused method with symbol alias" do
473
+ content =<<-EOF
474
+ class Post < ActiveRecord::Base
475
+ def old; end
476
+ alias :new :old
477
+ end
478
+ EOF
479
+ runner.prepare("app/models/post.rb", content)
480
+ runner.review("app/models/post.rb", content)
481
+ content =<<-EOF
482
+ class PostsController < ApplicationController
483
+ def show
484
+ @post.new
485
+ end
486
+ end
487
+ EOF
488
+ runner.review("app/controllers/posts_controller.rb", content)
489
+ runner.on_complete
490
+ runner.should have(0).errors
491
+ end
492
+
493
+ it "should not remove unused method with alias_method" do
494
+ content =<<-EOF
495
+ class Post < ActiveRecord::Base
496
+ def old; end
497
+ alias_method :new, :old
498
+ end
499
+ EOF
500
+ runner.prepare("app/models/post.rb", content)
501
+ runner.review("app/models/post.rb", content)
502
+ content =<<-EOF
503
+ class PostsController < ApplicationController
504
+ def show
505
+ @post.new
506
+ end
507
+ end
508
+ EOF
509
+ runner.review("app/controllers/posts_controller.rb", content)
510
+ runner.on_complete
511
+ runner.should have(0).errors
512
+ end
513
+
514
+ it "should not remove unused method with alias_method_chain" do
515
+ content =<<-EOF
516
+ class Post < ActiveRecord::Base
517
+ def method_with_feature; end
518
+ alias_method_chain :method, :feature
519
+ end
520
+ EOF
521
+ runner.prepare("app/models/post.rb", content)
522
+ runner.review("app/models/post.rb", content)
523
+ content =<<-EOF
524
+ class PostsController < ApplicationController
525
+ def show
526
+ @post.method
527
+ end
528
+ end
529
+ EOF
530
+ runner.review("app/controllers/posts_controller.rb", content)
531
+ runner.on_complete
532
+ runner.should have(0).errors
533
+ end
534
+ end
535
+
536
+ context "methods hash" do
537
+ it "should not remove unused method with methods hash" do
538
+ content =<<-EOF
539
+ class Post < ActiveRecord::Base
540
+ def to_xml(options = {})
541
+ super options.merge(:exclude => :visible, :methods => [:is_discussion_conversation])
542
+ end
543
+
544
+ def is_discussion_conversation; end
545
+ end
546
+ EOF
547
+ runner.prepare("app/models/post.rb", content)
548
+ runner.review("app/models/post.rb", content)
549
+ runner.on_complete
550
+ runner.should have(0).errors
551
+ end
552
+ end
387
553
  end
@@ -55,6 +55,26 @@ describe RailsBestPractices::Reviews::RestrictAutoGeneratedRoutesReview do
55
55
  runner.should have(0).errors
56
56
  end
57
57
 
58
+ it "should not restrict auto-generated routes with :only => :none" do
59
+ content =<<-EOF
60
+ ActionController::Routing::Routes.draw do |map|
61
+ map.resources :posts, :only => :none
62
+ end
63
+ EOF
64
+ runner.review('config/routes.rb', content)
65
+ runner.should have(0).errors
66
+ end
67
+
68
+ it "should not restrict auto-generated routes with :except => :all" do
69
+ content =<<-EOF
70
+ ActionController::Routing::Routes.draw do |map|
71
+ map.resources :posts, :except => :all
72
+ end
73
+ EOF
74
+ runner.review('config/routes.rb', content)
75
+ runner.should have(0).errors
76
+ end
77
+
58
78
  describe "specify a controller" do
59
79
  it "should restrict auto-generated routes" do
60
80
  content =<<-EOF
@@ -14,7 +14,7 @@ describe RailsBestPractices::Reviews::SimplifyRenderInViewsReview do
14
14
 
15
15
  it "should simplify render partial with object" do
16
16
  content =<<-EOF
17
- <%= render :partial => 'posts/post', :object => @post %>
17
+ <%= render :partial => 'post', :object => @post %>
18
18
  EOF
19
19
  runner.review('app/views/posts/index.html.erb', content)
20
20
  runner.should have(1).errors
@@ -32,7 +32,7 @@ describe RailsBestPractices::Reviews::SimplifyRenderInViewsReview do
32
32
 
33
33
  it "should simplify render partial with local variables" do
34
34
  content =<<-EOF
35
- <%= render :partial => 'comments/comment', :locals => { :parent => post } %>
35
+ <%= render :partial => 'comment', :locals => { :parent => post } %>
36
36
  EOF
37
37
  runner.review('app/views/posts/index.html.erb', content)
38
38
  runner.should have(1).errors
@@ -66,7 +66,15 @@ describe RailsBestPractices::Reviews::SimplifyRenderInViewsReview do
66
66
 
67
67
  it "should not simplify render partial with local variables" do
68
68
  content =<<-EOF
69
- <%= render 'comments/comment', :parent => post %>
69
+ <%= render 'comment', :parent => post %>
70
+ EOF
71
+ runner.review('app/views/posts/index.html.erb', content)
72
+ runner.should have(0).errors
73
+ end
74
+
75
+ it "should not simplify render partial with complex partial" do
76
+ content =<<-EOF
77
+ <%= render :partial => 'shared/post', :object => @post %>
70
78
  EOF
71
79
  runner.review('app/views/posts/index.html.erb', content)
72
80
  runner.should have(0).errors
data/spec/spec_helper.rb CHANGED
@@ -8,6 +8,8 @@ RSpec.configure do |config|
8
8
  config.after do
9
9
  RailsBestPractices::Prepares.clear
10
10
  end
11
+ config.filter_run :focus => true
12
+ config.run_all_when_everything_filtered = true
11
13
  end
12
14
 
13
15
  def parse_content(content)
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: rails_best_practices
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.2.0
5
+ version: 1.3.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Richard Huang
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-10-16 00:00:00 +08:00
13
+ date: 2011-11-13 00:00:00 +08:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -298,7 +298,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
298
298
  requirements:
299
299
  - - ">="
300
300
  - !ruby/object:Gem::Version
301
- hash: 1140723947848304714
301
+ hash: 3527788243321139328
302
302
  segments:
303
303
  - 0
304
304
  version: "0"