rails_best_practices 0.6.7 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. data/.gemtest +0 -0
  2. data/README.md +32 -22
  3. data/Rakefile +1 -0
  4. data/lib/rails_best_practices.rb +7 -5
  5. data/lib/rails_best_practices/core/check.rb +22 -1
  6. data/lib/rails_best_practices/core/checking_visitor.rb +16 -7
  7. data/lib/rails_best_practices/core/runner.rb +50 -11
  8. data/lib/rails_best_practices/core/visitable_sexp.rb +10 -3
  9. data/lib/rails_best_practices/lexicals.rb +2 -0
  10. data/lib/rails_best_practices/lexicals/remove_trailing_whitespace_check.rb +26 -0
  11. data/lib/rails_best_practices/reviews.rb +1 -0
  12. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +0 -3
  13. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +3 -3
  14. data/lib/rails_best_practices/reviews/review.rb +0 -15
  15. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +80 -0
  16. data/lib/rails_best_practices/reviews/use_observer_review.rb +0 -4
  17. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +0 -5
  18. data/lib/rails_best_practices/version.rb +1 -1
  19. data/rails_best_practices.yml +24 -22
  20. data/spec/rails_best_practices/core/checking_visitor_spec.rb +21 -1
  21. data/spec/rails_best_practices/core/visitable_sexp_spec.rb +10 -0
  22. data/spec/rails_best_practices/lexicals/remove_trailing_whitespace_check_spec.rb +39 -0
  23. data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +24 -0
  24. data/spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb +196 -0
  25. metadata +12 -4
data/.gemtest ADDED
File without changes
data/README.md CHANGED
@@ -83,28 +83,30 @@ to generate `rails_best_practices.yml` file.
83
83
 
84
84
  Now you can customize this configuration file, the default configuration is as follows:
85
85
 
86
- MoveFinderToNamedScopeReview: { }
87
- UseModelAssociationReview: { }
88
- UseScopeAccessReview: { }
89
- AddModelVirtualAttributeReview: { }
90
- ReplaceComplexCreationWithFactoryMethodReview: { attribute_assignment_count: 2 }
91
- MoveModelLogicIntoModelReview: { use_count: 4 }
92
- OveruseRouteCustomizationsReview: { customize_count: 3 }
93
- NeedlessDeepNestingReview: { nested_count: 2 }
94
- NotUseDefaultRouteReview: { }
95
- KeepFindersOnTheirOwnModelReview: { }
96
- LawOfDemeterReview: { }
97
- UseObserverReview: { }
98
- IsolateSeedDataReview: { }
99
- AlwaysAddDbIndexReview: { }
100
- UseBeforeFilterReview: { }
101
- MoveCodeIntoControllerReview: { }
102
- MoveCodeIntoModelReview: { use_count: 2 }
103
- MoveCodeIntoHelperReview: { array_count: 3 }
104
- ReplaceInstanceVariableWithLocalVariableReview: { }
105
- DryBundlerInCapistranoReview: { }
106
- UseSayWithTimeInMigrationsReview: { }
107
- UseQueryAttributeReview: { }
86
+ MoveFinderToNamedScopeCheck: { }
87
+ UseModelAssociationCheck: { }
88
+ UseScopeAccessCheck: { }
89
+ AddModelVirtualAttributeCheck: { }
90
+ ReplaceComplexCreationWithFactoryMethodCheck: { attribute_assignment_count: 2 }
91
+ MoveModelLogicIntoModelCheck: { use_count: 4 }
92
+ OveruseRouteCustomizationsCheck: { customize_count: 3 }
93
+ NeedlessDeepNestingCheck: { nested_count: 2 }
94
+ NotUseDefaultRouteCheck: { }
95
+ KeepFindersOnTheirOwnModelCheck: { }
96
+ LawOfDemeterCheck: { }
97
+ UseObserverCheck: { }
98
+ IsolateSeedDataCheck: { }
99
+ AlwaysAddDbIndexCheck: { }
100
+ UseBeforeFilterCheck: { }
101
+ MoveCodeIntoControllerCheck: { }
102
+ MoveCodeIntoModelCheck: { use_count: 2 }
103
+ MoveCodeIntoHelperCheck: { array_count: 3 }
104
+ ReplaceInstanceVariableWithLocalVariableCheck: { }
105
+ DryBundlerInCapistranoCheck: { }
106
+ UseSayWithTimeInMigrationsCheck: { }
107
+ UseQueryAttributeCheck: { }
108
+ RemoveTrailingWhitespaceCheck: { }
109
+ UseMultipartAlternativeAsContentTypeOfEmailCheck: {}
108
110
 
109
111
  You can remove or comment one review to disable it, and you can change the options.
110
112
 
@@ -133,6 +135,10 @@ Model
133
135
  3. Use Observer
134
136
  4. Use Query Attribute
135
137
 
138
+ Mailer
139
+
140
+ 1. Use multipart/alternative as content_type of email
141
+
136
142
  Migration
137
143
 
138
144
  1. Isolating Seed Data
@@ -154,6 +160,10 @@ Deployment
154
160
 
155
161
  1. Dry bundler in capistrano
156
162
 
163
+ Other
164
+
165
+ 1. Remove Trailing Whitespace
166
+
157
167
  Contribute
158
168
  ----------
159
169
 
data/Rakefile CHANGED
@@ -35,3 +35,4 @@ Rspec::Core::RakeTask.new('spec:progress') do |spec|
35
35
  end
36
36
 
37
37
  task :default => :spec
38
+ task :test => :spec
@@ -26,6 +26,7 @@ require 'rubygems'
26
26
  require 'progressbar'
27
27
  require 'colored'
28
28
  require 'haml'
29
+ require 'rails_best_practices/lexicals'
29
30
  require 'rails_best_practices/prepares'
30
31
  require 'rails_best_practices/reviews'
31
32
  require 'rails_best_practices/core'
@@ -79,9 +80,8 @@ module RailsBestPractices
79
80
  plain_output("AlwaysAddDbIndexReview is disabled as there is no db/schema.rb file in your rails project.", 'blue')
80
81
  end
81
82
 
82
- @bar = ProgressBar.new('Analyzing', prepare_files.size + review_files.size)
83
- process("prepare")
84
- process("review")
83
+ @bar = ProgressBar.new('Analyzing', lexical_files.size + prepare_files.size + review_files.size)
84
+ ["lexical", "prepare", "review"].each { |process| send(:process, process) }
85
85
  @bar.finish
86
86
 
87
87
  if @options['format'] == 'html'
@@ -92,12 +92,12 @@ module RailsBestPractices
92
92
  exit @runner.errors.size
93
93
  end
94
94
 
95
- # process prepare or reivew.
95
+ # process lexical, prepare or reivew.
96
96
  #
97
97
  # get all files for the process, analyze each file,
98
98
  # and increment progress bar unless debug.
99
99
  #
100
- # @param [String] process the process name, prepare or review.
100
+ # @param [String] process the process name, lexical, prepare or review.
101
101
  def process(process)
102
102
  files = send("#{process}_files")
103
103
  files.each do |file|
@@ -137,6 +137,8 @@ module RailsBestPractices
137
137
  end
138
138
  end
139
139
 
140
+ alias :lexical_files :review_files
141
+
140
142
  # expand all files with extenstion rb, erb, haml and builder under the dirs
141
143
  #
142
144
  # @param [Array] dirs what directories to expand
@@ -9,11 +9,17 @@ module RailsBestPractices
9
9
  CONTROLLER_FILES = /_controller\.rb$/
10
10
  MIGRATION_FILES = /db\/migrate\/.*\.rb$/
11
11
  MODEL_FILES = /models\/.*\.rb$/
12
- MAILER_FILES = /models\/.*\.rb$|mailers\/.*\.rb/
12
+ MAILER_FILES = /models\/.*mailer\.rb$|mailers\/.*mailer\.rb/
13
13
  VIEW_FILES = /views\/.*\.(erb|haml)$/
14
14
  PARTIAL_VIEW_FILES = /views\/.*\/_.*\.(erb|haml)$/
15
15
  ROUTE_FILE = /config\/routes.rb/
16
16
 
17
+ attr_reader :errors
18
+
19
+ def initialize
20
+ @errors = []
21
+ end
22
+
17
23
  # default interesting nodes.
18
24
  def interesting_nodes
19
25
  []
@@ -46,6 +52,21 @@ module RailsBestPractices
46
52
  self.send("end_#{node.node_type}", node)
47
53
  end
48
54
 
55
+ # add error if source code violates rails best practice.
56
+ # error is the string message for violation of the rails best practice
57
+ # file is the filename of source code
58
+ # line is the line number of the source code which is reviewing
59
+ def add_error(error, file = @node.file, line = @node.line)
60
+ @errors << RailsBestPractices::Core::Error.new("#{file}", "#{line}", error, url)
61
+ end
62
+
63
+ # default url is empty.
64
+ #
65
+ # @return [String] the url of rails best practice
66
+ def url
67
+ ""
68
+ end
69
+
49
70
  # method_missing to catch all start and end process for each node type, like
50
71
  #
51
72
  # start_defn
@@ -3,25 +3,26 @@ module RailsBestPractices
3
3
  module Core
4
4
  # CheckingVisitor is a visitor class.
5
5
  #
6
- # it remembers all the checks for prepare and review processes according to interesting_prepare_nodes and interesting_review_nodes,
6
+ # it remembers all the checks for prepare and review processes according to interesting_nodes and interesting_nodes,
7
7
  # then recursively iterate all sexp nodes,
8
8
  #
9
9
  # for prepare process
10
- # if the node_type and the node filename match the interesting_prepare_nodes and interesting_prepare_files,
10
+ # if the node_type and the node filename match the interesting_prepare_nodes and interesting_files,
11
11
  # then run the prepare for that node.
12
12
  #
13
13
  # for review process
14
- # if the node_type and the node filename match the interesting_review_nodes and interesting_review_files,
14
+ # if the node_type and the node filename match the interesting_review_nodes and interesting_files,
15
15
  # then run the reivew for that node.
16
16
  class CheckingVisitor
17
17
  # remember all the checks for prepare and review processes according to interesting_nodes.
18
18
  #
19
- # @param [Array] prepares
20
- # @param [Array] reviews
21
- def initialize(prepares, reviews)
19
+ # @param [Hash] options
20
+ # {:lexicals => [], :prepares => [], :reviews => []}
21
+ def initialize(options={})
22
+ @lexicals = options[:lexicals]
22
23
  [:prepare, :review].each do |process|
23
24
  instance_variable_set("@#{process}_checks", {}) # @review_checks = {}
24
- eval("#{process}s").each do |check| # reviews.each do |check|
25
+ options["#{process}s".to_sym].each do |check| # options[:reviews].each do |check|
25
26
  check.send("interesting_nodes").each do |node| # check.interesting_nodes.each do |node|
26
27
  instance_variable_get("@#{process}_checks")[node] ||= [] # @review_checks[node] ||= []
27
28
  instance_variable_get("@#{process}_checks")[node] << check # @review_checks[node] << check
@@ -31,6 +32,14 @@ module RailsBestPractices
31
32
  end
32
33
  end
33
34
 
35
+ # for lexical process
36
+ # check the content of files one by one.
37
+ def lexical(filename, content)
38
+ @lexicals.each do |lexical|
39
+ lexical.check(filename, content)
40
+ end
41
+ end
42
+
34
43
  # for prepare process
35
44
  # if the node_type and the node filename match the interesting_nodes and interesting_files,
36
45
  # then run the prepare for that node.
@@ -40,13 +40,30 @@ module RailsBestPractices
40
40
 
41
41
  prepares = Array(options[:prepares])
42
42
  reviews = Array(options[:reviews])
43
+ @lexicals = load_lexicals
43
44
  @prepares = prepares.empty? ? load_prepares : prepares
44
45
  @reviews = reviews.empty? ? load_reviews : reviews
45
46
 
46
- @checker ||= CheckingVisitor.new(@prepares, @reviews)
47
+ @checker ||= CheckingVisitor.new(:prepares => @prepares, :reviews => @reviews, :lexicals => @lexicals)
47
48
  @debug = false
48
49
  end
49
50
 
51
+ # lexical analysis the file.
52
+ #
53
+ # @param [String] filename name of the file
54
+ # @param [String] content content of the file
55
+ def lexical(filename, content)
56
+ puts filename if @debug
57
+ @checker.lexical(filename, content)
58
+ end
59
+
60
+ # lexical analysis the file.
61
+ #
62
+ # @param [String] filename
63
+ def lexical_file(filename)
64
+ lexical(filename, File.read(filename))
65
+ end
66
+
50
67
  # prepare and review a file's content with filename.
51
68
  # the file may be a ruby, erb or haml file.
52
69
  #
@@ -67,11 +84,11 @@ module RailsBestPractices
67
84
  EOS
68
85
  end
69
86
 
70
- # get all errors from reviews.
87
+ # get all errors from lexicals and reviews.
71
88
  #
72
- # @return [Array] all errors from reviews
89
+ # @return [Array] all errors from lexicals and reviews
73
90
  def errors
74
- @reviews.collect {|review| review.errors}.flatten
91
+ (@reviews + @lexicals).collect {|check| check.errors}.flatten
75
92
  end
76
93
 
77
94
  private
@@ -111,6 +128,20 @@ module RailsBestPractices
111
128
  content
112
129
  end
113
130
 
131
+ # load all lexical checks.
132
+ def load_lexicals
133
+ checks_from_config.inject([]) { |active_checks, check|
134
+ begin
135
+ check_name, options = *check
136
+ klass = RailsBestPractices::Lexicals.const_get(check_name)
137
+ active_checks << (options.empty? ? klass.new : klass.new(options))
138
+ rescue
139
+ # the check does not exist in the Lexicals namepace.
140
+ end
141
+ active_checks
142
+ }
143
+ end
144
+
114
145
  # load all prepares.
115
146
  def load_prepares
116
147
  [RailsBestPractices::Prepares::ModelPrepare.new, RailsBestPractices::Prepares::MailerPrepare.new]
@@ -118,13 +149,21 @@ module RailsBestPractices
118
149
 
119
150
  # load all reviews according to configuration.
120
151
  def load_reviews
121
- check_objects = []
122
- checks = YAML.load_file @config
123
- checks.each do |check|
124
- klass = RailsBestPractices::Reviews.const_get(check[0].gsub(/Check/, 'Review'))
125
- check_objects << (check[1].empty? ? klass.new : klass.new(check[1]))
126
- end
127
- check_objects
152
+ checks_from_config.inject([]) { |active_checks, check|
153
+ begin
154
+ check_name, options = *check
155
+ klass = RailsBestPractices::Reviews.const_get(check_name.gsub(/Check/, 'Review'))
156
+ active_checks << (options.empty? ? klass.new : klass.new(options))
157
+ rescue
158
+ # the check does not exist in the Reviews namepace.
159
+ end
160
+ active_checks
161
+ }
162
+ end
163
+
164
+ # read the checks from yaml config.
165
+ def checks_from_config
166
+ @checks ||= YAML.load_file @config
128
167
  end
129
168
  end
130
169
  end
@@ -400,6 +400,8 @@ class Sexp
400
400
  # @return [String] to_s
401
401
  def to_s(options={})
402
402
  case node_type
403
+ when :true, :false, :nil
404
+ self[0].to_s
403
405
  when :ivar
404
406
  options[:remove_at] ? self[1].to_s[1..-1] : self[1].to_s
405
407
  when :lvar, :str, :lit, :const
@@ -408,12 +410,17 @@ class Sexp
408
410
  "[\"#{self.children.collect(&:to_s).join('", "')}\"]"
409
411
  when :hash
410
412
  key_value = false # false is key, true is value
411
- result = ['{"']
413
+ result = ['{']
412
414
  children.each do |child|
413
- result << "#{child.to_s}#{key_value ? '", "' : '" => "'}"
415
+ if [:true, :false, :nil, :array, :hash].include? child.node_type
416
+ result << "#{child}"
417
+ else
418
+ result << "\"#{child}\""
419
+ end
420
+ result << (key_value ? ", " : " => ")
414
421
  key_value = !key_value
415
422
  end
416
- result.join("").sub(/, "$/, '') + '}'
423
+ result.join("").sub(/, $/, '') + '}'
417
424
  when :colon2
418
425
  "#{self[1]}::#{self[2]}"
419
426
  else
@@ -0,0 +1,2 @@
1
+ # encoding: utf-8
2
+ require 'rails_best_practices/lexicals/remove_trailing_whitespace_check'
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ require 'rails_best_practices/core/check'
3
+
4
+ module RailsBestPractices
5
+ module Lexicals
6
+ # Make sure there are no trailing whitespace in codes.
7
+ #
8
+ # See the best practice details here http://rails-bestpractices.com/posts/60-remove-trailing-whitespace
9
+ class RemoveTrailingWhitespaceCheck < Core::Check
10
+ def url
11
+ "http://rails-bestpractices.com/posts/60-remove-trailing-whitespace"
12
+ end
13
+
14
+ # check if the content of file contain a trailing whitespace.
15
+ #
16
+ # @param [String] filename name of the file
17
+ # @param [String] content content of the file
18
+ def check(filename, content)
19
+ if content =~ / +\n/m
20
+ line_no = $`.count("\n") + 1
21
+ add_error("remove trailing whitespace", filename, line_no)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -21,3 +21,4 @@ require 'rails_best_practices/reviews/replace_instance_variable_with_local_varia
21
21
  require 'rails_best_practices/reviews/dry_bundler_in_capistrano_review'
22
22
  require 'rails_best_practices/reviews/use_say_with_time_in_migrations_review'
23
23
  require 'rails_best_practices/reviews/use_query_attribute_review'
24
+ require 'rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review'
@@ -9,9 +9,6 @@ module RailsBestPractices
9
9
  #
10
10
  # Implementation:
11
11
  #
12
- # Prepare process:
13
- # only review all model files to save model names and association names.
14
- #
15
12
  # Review process:
16
13
  # check all method calls to see if there is method call to the association object.
17
14
  # if there is a call node whose subject is an object of model (compare by name),
@@ -113,13 +113,13 @@ module RailsBestPractices
113
113
  # if so, it is the needless deep nesting.
114
114
  def recursively_check(node)
115
115
  if :iter == node.node_type && :resources == node.subject.message
116
+ options = eval(node.subject.arguments[2].to_s)
117
+ return if options["shallow"] == true
116
118
  @counter += 1
117
119
  recursively_check(node.body)
118
120
  @counter -= 1
119
121
  elsif :block == node.node_type
120
- node.children.each do |child_node|
121
- recursively_check(child_node)
122
- end
122
+ node.children.each { |child_node| recursively_check(child_node) }
123
123
  elsif :call == node.node_type && [:resources, :resource].include?(node.message)
124
124
  add_error "needless deep nesting (nested_count > #{@nested_count})", node.file, node.line if @counter >= @nested_count
125
125
  end
@@ -6,21 +6,6 @@ module RailsBestPractices
6
6
  module Reviews
7
7
  # A Review class that takes charge of reviewing one rails best practice.
8
8
  class Review < Core::Check
9
- attr_reader :errors
10
-
11
- def initialize
12
- super
13
- @errors = []
14
- end
15
-
16
- # add error if source code violates rails best practice.
17
- # error is the string message for violation of the rails best practice
18
- # file is the filename of source code
19
- # line is the line number of the source code which is reviewing
20
- def add_error(error, file = @node.file, line = @node.line)
21
- @errors << RailsBestPractices::Core::Error.new("#{file}", "#{line}", error, url)
22
- end
23
-
24
9
  # remember use count for the local or instance variable in the call or attrasgn node.
25
10
  #
26
11
  # find the local variable or instance variable in the call or attrasgn node,
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+ require 'rails_best_practices/core/runner'
3
+ require 'rails_best_practices/reviews/review'
4
+
5
+ module RailsBestPractices
6
+ module Reviews
7
+ # Make sure to use multipart/alternative as content_type of email.
8
+ #
9
+ # See the best practice details here http://rails-bestpractices.com/posts/41-use-multipart-alternative-as-content_type-of-email.
10
+ #
11
+ # Implementation:
12
+ #
13
+ # Review process:
14
+ # check class node to remember the class name,
15
+ # and check the method definition nodes to see if the corresponding mailer views exist or not.
16
+ class UseMultipartAlternativeAsContentTypeOfEmailReview < Review
17
+ def url
18
+ "http://rails-bestpractices.com/posts/41-use-multipart-alternative-as-content_type-of-email"
19
+ end
20
+
21
+ def interesting_nodes
22
+ [:class, :defn]
23
+ end
24
+
25
+ def interesting_files
26
+ MAILER_FILES
27
+ end
28
+
29
+ # check class node to remember the ActionMailer class name.
30
+ def start_class(node)
31
+ @klazz_name = node.class_name
32
+ end
33
+
34
+ # check defn node and find if the corresponding views exist or not?
35
+ def start_defn(node)
36
+ name = node.method_name
37
+ return unless deliver_method?(name)
38
+ if !rails2_mailer_views_exist?(name) && !rails3_mailer_views_exist?(name)
39
+ add_error("use multipart/alternative as content_type of email")
40
+ end
41
+ end
42
+
43
+ private
44
+ # check if rails2's syntax mailer views exist or not according to the method name.
45
+ # @param [String] name method name in action_mailer
46
+ def rails2_mailer_views_exist?(name)
47
+ (exist?("#{name}.text.plain.erb") && exist?("#{name}.text.html.erb")) ||
48
+ (exist?("#{name}.text.plain.haml") && exist?("#{name}.text.html.haml")) ||
49
+ (exist?("#{name}.text.plain.rhtml") && exist?("#{name}.text.html.rhtml"))
50
+ end
51
+
52
+ # check if rails3's syntax mailer views exist or not according to the method name.
53
+ #
54
+ # @param [String] name method name in action_mailer
55
+ def rails3_mailer_views_exist?(name)
56
+ (exist?("#{name}.text.erb") && exist?("#{name}.html.erb")) ||
57
+ (exist?("#{name}.text.haml") && exist?("#{name}.html.haml"))
58
+ end
59
+
60
+ # check if the filename existed in the mailer directory.
61
+ def exist?(filename)
62
+ File.exist? File.join(mailer_directory, filename)
63
+ end
64
+
65
+ # check if the method is a deliver_method.
66
+ #
67
+ # @param [String] name the name of the method
68
+ def deliver_method?(name)
69
+ Dir.entries(mailer_directory).find { |filename| filename.index name.to_s }
70
+ rescue
71
+ false
72
+ end
73
+
74
+ # the view directory of mailer.
75
+ def mailer_directory
76
+ File.join(Core::Runner.base_path, "app/views/#{@klazz_name.to_s.underscore}")
77
+ end
78
+ end
79
+ end
80
+ end
@@ -11,10 +11,6 @@ module RailsBestPractices
11
11
  #
12
12
  # Implementation:
13
13
  #
14
- # Prepare process:
15
- # check all class nodes to see if they are the subclass of ActionMailer::Base,
16
- # if so, remember the class name.
17
- #
18
14
  # Review process:
19
15
  # check all call nodes to see if they are callback definitions, like after_create, before_destroy,
20
16
  # if so, remember the callback methods.
@@ -9,11 +9,6 @@ module RailsBestPractices
9
9
  #
10
10
  # Implementation:
11
11
  #
12
- # Prepare process:
13
- # only check all model files to save model names and association names,
14
- # model names are saved as only when subject of call method is equal to one of the model name, then the call method may use query attribute instead,
15
- # association names are saved as association attributes should not be detected as query attributes.
16
- #
17
12
  # Review process:
18
13
  # check all method calls within conditional statements, like @user.login.nil?
19
14
  # if their subjects are one of the model names
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
  module RailsBestPractices
3
- VERSION = "0.6.7"
3
+ VERSION = "0.7.0"
4
4
  end
5
5
 
@@ -1,22 +1,24 @@
1
- MoveFinderToNamedScopeReview: { }
2
- UseModelAssociationReview: { }
3
- UseScopeAccessReview: { }
4
- AddModelVirtualAttributeReview: { }
5
- ReplaceComplexCreationWithFactoryMethodReview: { attribute_assignment_count: 2 }
6
- MoveModelLogicIntoModelReview: { use_count: 4 }
7
- OveruseRouteCustomizationsReview: { customize_count: 3 }
8
- NeedlessDeepNestingReview: { nested_count: 2 }
9
- NotUseDefaultRouteReview: { }
10
- KeepFindersOnTheirOwnModelReview: { }
11
- LawOfDemeterReview: { }
12
- UseObserverReview: { }
13
- IsolateSeedDataReview: { }
14
- AlwaysAddDbIndexReview: { }
15
- UseBeforeFilterReview: { }
16
- MoveCodeIntoControllerReview: { }
17
- MoveCodeIntoModelReview: { use_count: 2 }
18
- MoveCodeIntoHelperReview: { array_count: 3 }
19
- ReplaceInstanceVariableWithLocalVariableReview: { }
20
- DryBundlerInCapistranoReview: { }
21
- UseSayWithTimeInMigrationsReview: { }
22
- UseQueryAttributeReview: { }
1
+ MoveFinderToNamedScopeCheck: { }
2
+ UseModelAssociationCheck: { }
3
+ UseScopeAccessCheck: { }
4
+ AddModelVirtualAttributeCheck: { }
5
+ ReplaceComplexCreationWithFactoryMethodCheck: { attribute_assignment_count: 2 }
6
+ MoveModelLogicIntoModelCheck: { use_count: 4 }
7
+ OveruseRouteCustomizationsCheck: { customize_count: 3 }
8
+ NeedlessDeepNestingCheck: { nested_count: 2 }
9
+ NotUseDefaultRouteCheck: { }
10
+ KeepFindersOnTheirOwnModelCheck: { }
11
+ LawOfDemeterCheck: { }
12
+ UseObserverCheck: { }
13
+ IsolateSeedDataCheck: { }
14
+ AlwaysAddDbIndexCheck: { }
15
+ UseBeforeFilterCheck: { }
16
+ MoveCodeIntoControllerCheck: { }
17
+ MoveCodeIntoModelCheck: { use_count: 2 }
18
+ MoveCodeIntoHelperCheck: { array_count: 3 }
19
+ ReplaceInstanceVariableWithLocalVariableCheck: { }
20
+ DryBundlerInCapistranoCheck: { }
21
+ UseSayWithTimeInMigrationsCheck: { }
22
+ UseQueryAttributeCheck: { }
23
+ RemoveTrailingWhitespaceCheck: { }
24
+ UseMultipartAlternativeAsContentTypeOfEmailCheck: {}
@@ -1,6 +1,10 @@
1
1
  # encoding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
+ class TestLexical1
5
+ end
6
+ class TestLexical2
7
+ end
4
8
  class TestPrepare1
5
9
  def interesting_nodes
6
10
  [:call]
@@ -42,11 +46,27 @@ class TestReview2
42
46
  end
43
47
 
44
48
  describe RailsBestPractices::Core::CheckingVisitor do
49
+ let(:lexical1) { TestLexical1.new }
50
+ let(:lexical2) { TestLexical2.new }
45
51
  let(:prepare1) { TestPrepare1.new }
46
52
  let(:prepare2) { TestPrepare2.new }
47
53
  let(:review1) { TestReview1.new }
48
54
  let(:review2) { TestReview2.new }
49
- let(:visitor) { RailsBestPractices::Core::CheckingVisitor.new([prepare1, prepare2], [review1, review2]) }
55
+ let(:visitor) {
56
+ RailsBestPractices::Core::CheckingVisitor.new(
57
+ :lexicals => [lexical1, lexical2],
58
+ :prepares => [prepare1, prepare2],
59
+ :reviews => [review1, review2]
60
+ )
61
+ }
62
+
63
+ it "should lexical check" do
64
+ filename = "app/models/user.rb"
65
+ content = "class User; end"
66
+ lexical1.should_receive(:check).with(filename, content)
67
+ lexical2.should_receive(:check).with(filename, content)
68
+ visitor.lexical(filename, content)
69
+ end
50
70
 
51
71
  it "should prepare model associations" do
52
72
  node = stub(:node_type => :call, :children => [], :file => "app/models/user.rb")
@@ -254,6 +254,16 @@ describe Sexp do
254
254
  node.to_s.should == '{"first_name" => "Richard", "last_name" => "Huang"}'
255
255
  end
256
256
 
257
+ it "should get to_s for hash with true/false" do
258
+ node = parser.parse("{:shallow => true, :index => false}")
259
+ node.to_s.should == '{"shallow" => true, "index" => false}'
260
+ end
261
+
262
+ it "should get to_s for array and hash mixed" do
263
+ node = parser.parse("{:only => [:show, :index]}")
264
+ node.to_s.should == '{"only" => ["show", "index"]}'
265
+ end
266
+
257
267
  it "should get to_s for colon2" do
258
268
  node = parser.parse("RailsBestPractices::Core")
259
269
  node.to_s.should == "RailsBestPractices::Core"
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsBestPractices::Lexicals::RemoveTrailingWhitespaceCheck do
4
+ let(:runner) { RailsBestPractices::Core::Runner.new(:lexicals => RailsBestPractices::Lexicals::RemoveTrailingWhitespaceCheck.new) }
5
+
6
+ it "should remove trailing whitespace" do
7
+ content =<<-EOF
8
+ class User < ActiveRecord::Base
9
+ has_many :projects
10
+ end
11
+ EOF
12
+ content.gsub!("\n", " \n")
13
+ runner.lexical('app/models/user.rb', content)
14
+ runner.should have(1).errors
15
+ runner.errors[0].to_s.should == "app/models/user.rb:1 - remove trailing whitespace"
16
+ end
17
+
18
+ it "should remove whitespace with third line" do
19
+ content =<<-EOF
20
+ class User < ActiveRecord::Base
21
+ has_many :projects
22
+ end
23
+ EOF
24
+ content.gsub!("d\n", "d \n")
25
+ runner.lexical('app/models/user.rb', content)
26
+ runner.should have(1).errors
27
+ runner.errors[0].to_s.should == "app/models/user.rb:3 - remove trailing whitespace"
28
+ end
29
+
30
+ it "should not remove trailing whitespace" do
31
+ content =<<-EOF
32
+ class User < ActiveRecord::Base
33
+ has_many :projects
34
+ end
35
+ EOF
36
+ runner.lexical('app/models/user.rb', content)
37
+ runner.should have(0).errors
38
+ end
39
+ end
@@ -17,6 +17,18 @@ describe RailsBestPractices::Reviews::NeedlessDeepNestingReview do
17
17
  runner.errors[0].to_s.should == "config/routes.rb:3 - needless deep nesting (nested_count > 2)"
18
18
  end
19
19
 
20
+ it "should not needless deep nesting for shallow" do
21
+ content = <<-EOF
22
+ map.resources :posts, :shallow => true do |post|
23
+ post.resources :comments do |comment|
24
+ comment.resources :favorites
25
+ end
26
+ end
27
+ EOF
28
+ runner.review('config/routes.rb', content)
29
+ runner.should have(0).errors
30
+ end
31
+
20
32
  it "should needless deep nesting with resource" do
21
33
  content = <<-EOF
22
34
  map.resources :posts do |post|
@@ -84,6 +96,18 @@ describe RailsBestPractices::Reviews::NeedlessDeepNestingReview do
84
96
  runner.errors[0].to_s.should == "config/routes.rb:4 - needless deep nesting (nested_count > 2)"
85
97
  end
86
98
 
99
+ it "should not needless deep nesting for shallow" do
100
+ content = <<-EOF
101
+ resources :posts, :shallow => true do
102
+ resources :comments do
103
+ resources :favorites
104
+ end
105
+ end
106
+ EOF
107
+ runner.review('config/routes.rb', content)
108
+ runner.should have(0).errors
109
+ end
110
+
87
111
  it "should needless deep nesting with resource" do
88
112
  content = <<-EOF
89
113
  resources :posts do
@@ -0,0 +1,196 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsBestPractices::Reviews::UseMultipartAlternativeAsContentTypeOfEmailReview do
4
+ let(:runner) { RailsBestPractices::Core::Runner.new(:reviews => RailsBestPractices::Reviews::UseMultipartAlternativeAsContentTypeOfEmailReview.new) }
5
+
6
+ context "rails2" do
7
+ before :each do
8
+ RailsBestPractices::Core::Runner.stub!(:base_path).and_return(".")
9
+ end
10
+
11
+ it "should use mulipart/alternative as content_type of email" do
12
+ content =<<-EOF
13
+ class ProjectMailer < ActionMailer::Base
14
+ def send_email(email)
15
+ subject email.subject
16
+ from email.from
17
+ recipients email.recipients
18
+ sent_on Time.now
19
+ body :email => email
20
+ end
21
+ end
22
+ EOF
23
+ Dir.stub!(:entries).with("./app/views/project_mailer").and_return(["send_email.html.erb"])
24
+ runner.review('app/mailers/project_mailer.rb', content)
25
+ runner.should have(1).errors
26
+ runner.errors[0].to_s.should == "app/mailers/project_mailer.rb:2 - use multipart/alternative as content_type of email"
27
+ end
28
+
29
+ it "should not use mulipart/alternative as content_type of email by erb" do
30
+ content =<<-EOF
31
+ class ProjectMailer < ActionMailer::Base
32
+ def send_email(email)
33
+ subject email.subject
34
+ from email.from
35
+ recipients email.recipients
36
+ sent_on Time.now
37
+ body :email => email
38
+ end
39
+ end
40
+ EOF
41
+ Dir.stub!(:entries).with("./app/views/project_mailer").and_return(["send_email.html.erb"])
42
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.erb").and_return(true)
43
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.erb").and_return(true)
44
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.erb").and_return(false)
45
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.erb").and_return(false)
46
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.haml").and_return(false)
47
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.haml").and_return(false)
48
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.haml").and_return(false)
49
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.haml").and_return(false)
50
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.rhtml").and_return(false)
51
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.rhtml").and_return(false)
52
+ runner.review('app/mailers/project_mailer.rb', content)
53
+ runner.should have(0).errors
54
+ end
55
+
56
+ it "should not use mulipart/alternative as content_type of email by haml" do
57
+ content =<<-EOF
58
+ class ProjectMailer < ActionMailer::Base
59
+ def send_email(email)
60
+ subject email.subject
61
+ from email.from
62
+ recipients email.recipients
63
+ sent_on Time.now
64
+ body :email => email
65
+ end
66
+ end
67
+ EOF
68
+ Dir.stub!(:entries).with("./app/views/project_mailer").and_return(["send_email.html.erb"])
69
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.erb").and_return(false)
70
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.erb").and_return(false)
71
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.erb").and_return(false)
72
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.erb").and_return(false)
73
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.haml").and_return(true)
74
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.haml").and_return(true)
75
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.haml").and_return(false)
76
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.haml").and_return(false)
77
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.rhtml").and_return(false)
78
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.rhtml").and_return(false)
79
+ runner.review('app/mailers/project_mailer.rb', content)
80
+ runner.should have(0).errors
81
+ end
82
+
83
+ it "should not use mulipart/alternative as content_type of email by rhtml" do
84
+ content =<<-EOF
85
+ class ProjectMailer < ActionMailer::Base
86
+ def send_email(email)
87
+ subject email.subject
88
+ from email.from
89
+ recipients email.recipients
90
+ sent_on Time.now
91
+ body :email => email
92
+ end
93
+ end
94
+ EOF
95
+ Dir.stub!(:entries).with("./app/views/project_mailer").and_return(["send_email.html.erb"])
96
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.erb").and_return(false)
97
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.erb").and_return(false)
98
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.erb").and_return(false)
99
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.erb").and_return(false)
100
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.haml").and_return(false)
101
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.haml").and_return(false)
102
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.haml").and_return(false)
103
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.haml").and_return(false)
104
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.rhtml").and_return(true)
105
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.rhtml").and_return(true)
106
+ runner.review('app/mailers/project_mailer.rb', content)
107
+ runner.should have(0).errors
108
+ end
109
+
110
+ it "should not use mulipart/alternative as content_type of email for non deliver method" do
111
+ content =<<-EOF
112
+ class ProjectMailer < ActionMailer::Base
113
+ def no_deliver
114
+ end
115
+ end
116
+ EOF
117
+ Dir.stub!(:entries).with("./app/views/project_mailer").and_return(["send_email.html.erb"])
118
+ runner.review('app/mailers/project_mailer.rb', content)
119
+ runner.should have(0).errors
120
+ end
121
+ end
122
+
123
+ context "rails3" do
124
+ it "should use mulipart/alternative as content_type of email" do
125
+ content =<<-EOF
126
+ class ProjectMailer < ActionMailer::Base
127
+ def send_email(email)
128
+ subject email.subject
129
+ from email.from
130
+ recipients email.recipients
131
+ sent_on Time.now
132
+ body :email => email
133
+ end
134
+ end
135
+ EOF
136
+ Dir.stub!(:entries).with("./app/views/project_mailer").and_return(["send_email.html.erb"])
137
+ runner.review('app/mailers/project_mailer.rb', content)
138
+ runner.should have(1).errors
139
+ runner.errors[0].to_s.should == "app/mailers/project_mailer.rb:2 - use multipart/alternative as content_type of email"
140
+ end
141
+
142
+ it "should not use mulipart/alternative as content_type of email by erb" do
143
+ content =<<-EOF
144
+ class ProjectMailer < ActionMailer::Base
145
+ def send_email(email)
146
+ subject email.subject
147
+ from email.from
148
+ recipients email.recipients
149
+ sent_on Time.now
150
+ body :email => email
151
+ end
152
+ end
153
+ EOF
154
+ Dir.stub!(:entries).with("./app/views/project_mailer").and_return(["send_email.html.erb"])
155
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.erb").and_return(false)
156
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.erb").and_return(false)
157
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.erb").and_return(true)
158
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.erb").and_return(true)
159
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.haml").and_return(false)
160
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.haml").and_return(false)
161
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.haml").and_return(false)
162
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.haml").and_return(false)
163
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.rhtml").and_return(false)
164
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.rhtml").and_return(false)
165
+ runner.review('app/mailers/project_mailer.rb', content)
166
+ runner.should have(0).errors
167
+ end
168
+
169
+ it "should not use mulipart/alternative as content_type of email by haml" do
170
+ content =<<-EOF
171
+ class ProjectMailer < ActionMailer::Base
172
+ def send_email(email)
173
+ subject email.subject
174
+ from email.from
175
+ recipients email.recipients
176
+ sent_on Time.now
177
+ body :email => email
178
+ end
179
+ end
180
+ EOF
181
+ Dir.stub!(:entries).with("./app/views/project_mailer").and_return(["send_email.html.erb"])
182
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.erb").and_return(false)
183
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.erb").and_return(false)
184
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.erb").and_return(false)
185
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.erb").and_return(false)
186
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.haml").and_return(false)
187
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.haml").and_return(false)
188
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.haml").and_return(true)
189
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.html.haml").and_return(true)
190
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.plain.rhtml").and_return(false)
191
+ File.stub!(:exist?).with("./app/views/project_mailer/send_email.text.html.rhtml").and_return(false)
192
+ runner.review('app/mailers/project_mailer.rb', content)
193
+ runner.should have(0).errors
194
+ end
195
+ end
196
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_best_practices
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 6
9
8
  - 7
10
- version: 0.6.7
9
+ - 0
10
+ version: 0.7.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Richard Huang
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-14 00:00:00 +08:00
18
+ date: 2011-02-15 00:00:00 +08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -196,6 +196,7 @@ extensions: []
196
196
  extra_rdoc_files: []
197
197
 
198
198
  files:
199
+ - .gemtest
199
200
  - .gitignore
200
201
  - .rspec.example
201
202
  - .rvmrc.example
@@ -219,6 +220,8 @@ files:
219
220
  - lib/rails_best_practices/core/visitable_sexp.rb
220
221
  - lib/rails_best_practices/core_ext/enumerable.rb
221
222
  - lib/rails_best_practices/core_ext/nil_class.rb
223
+ - lib/rails_best_practices/lexicals.rb
224
+ - lib/rails_best_practices/lexicals/remove_trailing_whitespace_check.rb
222
225
  - lib/rails_best_practices/prepares.rb
223
226
  - lib/rails_best_practices/prepares/mailer_prepare.rb
224
227
  - lib/rails_best_practices/prepares/model_prepare.rb
@@ -242,6 +245,7 @@ files:
242
245
  - lib/rails_best_practices/reviews/review.rb
243
246
  - lib/rails_best_practices/reviews/use_before_filter_review.rb
244
247
  - lib/rails_best_practices/reviews/use_model_association_review.rb
248
+ - lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb
245
249
  - lib/rails_best_practices/reviews/use_observer_review.rb
246
250
  - lib/rails_best_practices/reviews/use_query_attribute_review.rb
247
251
  - lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb
@@ -256,6 +260,7 @@ files:
256
260
  - spec/rails_best_practices/core/visitable_sexp_spec.rb
257
261
  - spec/rails_best_practices/core_ext/enumerable_spec.rb
258
262
  - spec/rails_best_practices/core_ext/nil_class_spec.rb
263
+ - spec/rails_best_practices/lexicals/remove_trailing_whitespace_check_spec.rb
259
264
  - spec/rails_best_practices/prepares/mailer_prepare_spec.rb
260
265
  - spec/rails_best_practices/prepares/model_prepare_spec.rb
261
266
  - spec/rails_best_practices/reviews/add_model_virtual_attribute_review_spec.rb
@@ -277,6 +282,7 @@ files:
277
282
  - spec/rails_best_practices/reviews/review_spec.rb
278
283
  - spec/rails_best_practices/reviews/use_before_filter_review_spec.rb
279
284
  - spec/rails_best_practices/reviews/use_model_association_review_spec.rb
285
+ - spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb
280
286
  - spec/rails_best_practices/reviews/use_observer_review_spec.rb
281
287
  - spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb
282
288
  - spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb
@@ -340,6 +346,7 @@ test_files:
340
346
  - spec/rails_best_practices/core/visitable_sexp_spec.rb
341
347
  - spec/rails_best_practices/core_ext/enumerable_spec.rb
342
348
  - spec/rails_best_practices/core_ext/nil_class_spec.rb
349
+ - spec/rails_best_practices/lexicals/remove_trailing_whitespace_check_spec.rb
343
350
  - spec/rails_best_practices/prepares/mailer_prepare_spec.rb
344
351
  - spec/rails_best_practices/prepares/model_prepare_spec.rb
345
352
  - spec/rails_best_practices/reviews/add_model_virtual_attribute_review_spec.rb
@@ -361,6 +368,7 @@ test_files:
361
368
  - spec/rails_best_practices/reviews/review_spec.rb
362
369
  - spec/rails_best_practices/reviews/use_before_filter_review_spec.rb
363
370
  - spec/rails_best_practices/reviews/use_model_association_review_spec.rb
371
+ - spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb
364
372
  - spec/rails_best_practices/reviews/use_observer_review_spec.rb
365
373
  - spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb
366
374
  - spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb