excellent 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/History.txt +69 -0
  2. data/README.rdoc +72 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/excellent +34 -0
  5. data/lib/simplabs/excellent.rb +16 -0
  6. data/lib/simplabs/excellent/checks.rb +33 -0
  7. data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +43 -0
  8. data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +39 -0
  9. data/lib/simplabs/excellent/checks/base.rb +62 -0
  10. data/lib/simplabs/excellent/checks/case_missing_else_check.rb +34 -0
  11. data/lib/simplabs/excellent/checks/class_line_count_check.rb +36 -0
  12. data/lib/simplabs/excellent/checks/class_name_check.rb +38 -0
  13. data/lib/simplabs/excellent/checks/control_coupling_check.rb +35 -0
  14. data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +48 -0
  15. data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +23 -0
  16. data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +48 -0
  17. data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +31 -0
  18. data/lib/simplabs/excellent/checks/flog_block_check.rb +40 -0
  19. data/lib/simplabs/excellent/checks/flog_check.rb +27 -0
  20. data/lib/simplabs/excellent/checks/flog_class_check.rb +40 -0
  21. data/lib/simplabs/excellent/checks/flog_method_check.rb +40 -0
  22. data/lib/simplabs/excellent/checks/for_loop_check.rb +42 -0
  23. data/lib/simplabs/excellent/checks/global_variable_check.rb +33 -0
  24. data/lib/simplabs/excellent/checks/line_count_check.rb +27 -0
  25. data/lib/simplabs/excellent/checks/method_line_count_check.rb +36 -0
  26. data/lib/simplabs/excellent/checks/method_name_check.rb +38 -0
  27. data/lib/simplabs/excellent/checks/module_line_count_check.rb +36 -0
  28. data/lib/simplabs/excellent/checks/module_name_check.rb +38 -0
  29. data/lib/simplabs/excellent/checks/name_check.rb +27 -0
  30. data/lib/simplabs/excellent/checks/nested_iterators_check.rb +34 -0
  31. data/lib/simplabs/excellent/checks/parameter_number_check.rb +38 -0
  32. data/lib/simplabs/excellent/checks/rails.rb +22 -0
  33. data/lib/simplabs/excellent/checks/rails/attr_accessible_check.rb +38 -0
  34. data/lib/simplabs/excellent/checks/rails/attr_protected_check.rb +39 -0
  35. data/lib/simplabs/excellent/checks/rails/custom_initialize_method_check.rb +37 -0
  36. data/lib/simplabs/excellent/checks/rails/instance_var_in_partial_check.rb +37 -0
  37. data/lib/simplabs/excellent/checks/rails/params_hash_in_view_check.rb +38 -0
  38. data/lib/simplabs/excellent/checks/rails/session_hash_in_view_check.rb +38 -0
  39. data/lib/simplabs/excellent/checks/rails/validations_check.rb +36 -0
  40. data/lib/simplabs/excellent/checks/singleton_variable_check.rb +33 -0
  41. data/lib/simplabs/excellent/command_line_runner.rb +37 -0
  42. data/lib/simplabs/excellent/extensions/sexp.rb +21 -0
  43. data/lib/simplabs/excellent/extensions/string.rb +28 -0
  44. data/lib/simplabs/excellent/formatters.rb +13 -0
  45. data/lib/simplabs/excellent/formatters/base.rb +49 -0
  46. data/lib/simplabs/excellent/formatters/html.rb +153 -0
  47. data/lib/simplabs/excellent/formatters/text.rb +40 -0
  48. data/lib/simplabs/excellent/parsing.rb +10 -0
  49. data/lib/simplabs/excellent/parsing/abc_measure.rb +52 -0
  50. data/lib/simplabs/excellent/parsing/block_context.rb +43 -0
  51. data/lib/simplabs/excellent/parsing/call_context.rb +52 -0
  52. data/lib/simplabs/excellent/parsing/case_context.rb +31 -0
  53. data/lib/simplabs/excellent/parsing/class_context.rb +99 -0
  54. data/lib/simplabs/excellent/parsing/code_processor.rb +165 -0
  55. data/lib/simplabs/excellent/parsing/conditional_context.rb +25 -0
  56. data/lib/simplabs/excellent/parsing/cvar_context.rb +28 -0
  57. data/lib/simplabs/excellent/parsing/cyclomatic_complexity_measure.rb +73 -0
  58. data/lib/simplabs/excellent/parsing/flog_measure.rb +192 -0
  59. data/lib/simplabs/excellent/parsing/for_loop_context.rb +15 -0
  60. data/lib/simplabs/excellent/parsing/gvar_context.rb +21 -0
  61. data/lib/simplabs/excellent/parsing/if_context.rb +38 -0
  62. data/lib/simplabs/excellent/parsing/ivar_context.rb +32 -0
  63. data/lib/simplabs/excellent/parsing/method_context.rb +50 -0
  64. data/lib/simplabs/excellent/parsing/module_context.rb +29 -0
  65. data/lib/simplabs/excellent/parsing/parser.rb +35 -0
  66. data/lib/simplabs/excellent/parsing/resbody_context.rb +39 -0
  67. data/lib/simplabs/excellent/parsing/scopeable.rb +34 -0
  68. data/lib/simplabs/excellent/parsing/sexp_context.rb +125 -0
  69. data/lib/simplabs/excellent/parsing/singleton_method_context.rb +55 -0
  70. data/lib/simplabs/excellent/parsing/until_context.rb +24 -0
  71. data/lib/simplabs/excellent/parsing/while_context.rb +24 -0
  72. data/lib/simplabs/excellent/rake.rb +1 -0
  73. data/lib/simplabs/excellent/rake/excellent_task.rb +61 -0
  74. data/lib/simplabs/excellent/runner.rb +143 -0
  75. data/lib/simplabs/excellent/warning.rb +53 -0
  76. data/spec/checks/abc_metric_method_check_spec.rb +122 -0
  77. data/spec/checks/assignment_in_conditional_check_spec.rb +90 -0
  78. data/spec/checks/case_missing_else_check_spec.rb +42 -0
  79. data/spec/checks/class_line_count_check_spec.rb +62 -0
  80. data/spec/checks/class_name_check_spec.rb +48 -0
  81. data/spec/checks/control_coupling_check_spec.rb +103 -0
  82. data/spec/checks/cyclomatic_complexity_block_check_spec.rb +47 -0
  83. data/spec/checks/cyclomatic_complexity_method_check_spec.rb +210 -0
  84. data/spec/checks/empty_rescue_body_check_spec.rb +170 -0
  85. data/spec/checks/flog_block_check_spec.rb +28 -0
  86. data/spec/checks/flog_class_check_spec.rb +28 -0
  87. data/spec/checks/flog_method_check_spec.rb +46 -0
  88. data/spec/checks/for_loop_check_spec.rb +52 -0
  89. data/spec/checks/global_variable_check_spec.rb +66 -0
  90. data/spec/checks/method_line_count_check_spec.rb +49 -0
  91. data/spec/checks/method_name_check_spec.rb +112 -0
  92. data/spec/checks/module_line_count_check_spec.rb +48 -0
  93. data/spec/checks/module_name_check_spec.rb +61 -0
  94. data/spec/checks/nested_iterators_check_spec.rb +44 -0
  95. data/spec/checks/parameter_number_check_spec.rb +97 -0
  96. data/spec/checks/rails/attr_accessible_check_spec.rb +79 -0
  97. data/spec/checks/rails/attr_protected_check_spec.rb +77 -0
  98. data/spec/checks/rails/custom_initialize_method_check_spec.rb +58 -0
  99. data/spec/checks/rails/instance_var_in_partial_check_spec.rb +40 -0
  100. data/spec/checks/rails/params_hash_in_view_check_spec.rb +40 -0
  101. data/spec/checks/rails/session_hash_in_view_check_spec.rb +40 -0
  102. data/spec/checks/rails/validations_check_spec.rb +81 -0
  103. data/spec/checks/singleton_variable_check_spec.rb +66 -0
  104. data/spec/extensions/string_spec.rb +13 -0
  105. data/spec/spec_helper.rb +13 -0
  106. metadata +189 -0
@@ -0,0 +1,29 @@
1
+ require 'simplabs/excellent/parsing/scopeable'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Parsing
8
+
9
+ class ModuleContext < SexpContext #:nodoc:
10
+
11
+ include Scopeable
12
+
13
+ attr_reader :methods
14
+ attr_reader :line_count
15
+
16
+ def initialize(exp, parent)
17
+ super
18
+ @name, @full_name = get_names
19
+ @methods = []
20
+ @line_count = count_lines
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'ruby_parser'
3
+ require 'facets'
4
+ require 'erb'
5
+
6
+ module Simplabs
7
+
8
+ module Excellent
9
+
10
+ module Parsing
11
+
12
+ class Parser #:nodoc:
13
+
14
+ def parse(content, filename)
15
+ return silent_parse(content, filename)
16
+ rescue Exception
17
+ #continue on errors
18
+ end
19
+
20
+ private
21
+
22
+ def silent_parse(content, filename)
23
+ @parser ||= RubyParser.new
24
+ content = ::ERB.new(content, nil, '-').src if filename =~ /\.erb$/
25
+ sexp = @parser.parse(content, filename)
26
+ sexp
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,39 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing
6
+
7
+ class ResbodyContext < SexpContext #:nodoc:
8
+
9
+ STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall, :call, :str, :lit, :hash, :false, :true, :nil]
10
+
11
+ def initialize(exp, parent)
12
+ super
13
+ @contains_statements = contains_statements?
14
+ end
15
+
16
+ def has_statements?
17
+ @contains_statements
18
+ end
19
+
20
+ private
21
+
22
+ def contains_statements?(exp = @exp)
23
+ return true if STATEMENT_NODES.include?(exp.node_type)
24
+ return true if assigning_other_than_exception_to_local_variable?(exp)
25
+ return true if (exp[1][0] == :array && exp[2][0] == :array rescue false)
26
+ return true if exp.children.any? { |child| contains_statements?(child) }
27
+ end
28
+
29
+ def assigning_other_than_exception_to_local_variable?(exp)
30
+ exp.node_type == :lasgn && exp[2].to_a != [:gvar, :$!]
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,34 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing
6
+
7
+ module Scopeable #:nodoc:
8
+
9
+ private
10
+
11
+ def get_names
12
+ if @exp[1].is_a?(Sexp)
13
+ name = @exp[1].pop.to_s.strip
14
+ [name, "#{extract_prefixes}#{name}"]
15
+ else
16
+ [@exp[1].to_s, nil]
17
+ end
18
+ end
19
+
20
+ def extract_prefixes(exp = @exp[1].deep_clone, prefix = '')
21
+ prefix = "#{exp.pop}::#{prefix}" if exp.last.is_a?(Symbol)
22
+ if exp.last.is_a?(Sexp)
23
+ prefix = extract_prefixes(exp.last, prefix)
24
+ end
25
+ prefix
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,125 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing
6
+
7
+ # For most nodes the Excellent processor processes, it will create the corresponding context that contains meta information of the processed
8
+ # node. This is the base class for all these contexts.
9
+ #
10
+ # === Example
11
+ #
12
+ # For a method like the following:
13
+ #
14
+ # module Shop
15
+ # class Basket
16
+ # def buy_product(product)
17
+ # other_method
18
+ # end
19
+ # end
20
+ # end
21
+ #
22
+ # four context will be generated:
23
+ #
24
+ # ModuleContext
25
+ # name: 'Shop'
26
+ # full_name: 'Shop'
27
+ # parent: nil
28
+ # ClassContext
29
+ # name: 'Basket'
30
+ # full_name: 'Shop::Basket'
31
+ # parent: ModuleContext
32
+ # MethodContext
33
+ # name: 'buy_product'
34
+ # full_name: 'Shop::Basket#buy_product'
35
+ # parent: ClassContext
36
+ # parameters: [:product]
37
+ # CallContext (other_method)
38
+ # name: nil
39
+ # full_name: nil
40
+ # parent: MethodContext
41
+ # method: :other_method
42
+ #
43
+ # === Custom Processors
44
+ #
45
+ # The Excelent processor will also invoke custom processor methods on the contexts if they are defined. To process call nodes in the context for
46
+ # example, you could simply define a +process_call+ method in the context that will be invoked with each call Sexp (S-expression, see
47
+ # http://en.wikipedia.org/wiki/S_expression) that is processed by the Excellent processor.
48
+ #
49
+ # def process_call(exp)
50
+ # super
51
+ # do_something()
52
+ # end
53
+ #
54
+ # Custom <b>processor methods must always call +super+</b> since there might be several processor methods defined in several modules that are in the
55
+ # included in the context and all of these have to be invoked. Also <b>processor methods must not modify the passed Sexp</b> since other processor
56
+ # methods also need the complete Sexp. If you have to modify the Sexp in a processor method, deep clone it:
57
+ #
58
+ # exp = exp.deep_clone
59
+ #
60
+ class SexpContext
61
+
62
+ # The parent context
63
+ attr_reader :parent
64
+
65
+ # The name of the code fragment the context is bound to (e.g. 'User' for a +class+)
66
+ attr_reader :name
67
+
68
+ # The file the code fragment was read from
69
+ attr_reader :file
70
+
71
+ # The line the code fragment is located at
72
+ attr_reader :line
73
+
74
+ # Initializes a SexpContext.
75
+ #
76
+ # Always call +super+ in inherited custom contexts!
77
+ #
78
+ # === Parameters
79
+ #
80
+ # * <tt>exp</tt> - The Sexp (S-expression, see http://en.wikipedia.org/wiki/S_expression) the context is created for.
81
+ # * <tt>parent</tt> - The parent context.
82
+ def initialize(exp, parent = nil)
83
+ @exp = exp
84
+ @parent = parent
85
+ @file = exp.file
86
+ @line = exp.line
87
+ @full_name = nil
88
+ end
89
+
90
+ # Gets the full name of the code fragment the context is bound to. For a method +name+ might be '+add_product+' while +full_name+ might be
91
+ # 'Basket#add_product'.
92
+ def full_name
93
+ return @full_name if @full_name
94
+ return @name if @parent.blank?
95
+ "#{@parent.full_name}::#{@name}"
96
+ end
97
+
98
+ def method_missing(method, *args) #:nodoc:
99
+ return if method.to_s =~ /^process_[a-zA-Z0-9_]+$/
100
+ super
101
+ end
102
+
103
+ private
104
+
105
+ def count_lines(node = @exp, line_numbers = [])
106
+ count = 0
107
+ line_numbers << node.line
108
+ node.children.each { |child| count += count_lines(child, line_numbers) }
109
+ line_numbers.uniq.length
110
+ end
111
+
112
+ def has_assignment?(exp = @exp[1])
113
+ found_assignment = false
114
+ found_assignment = found_assignment || exp.node_type == :lasgn
115
+ exp.children.each { |child| found_assignment = found_assignment || has_assignment?(child) }
116
+ found_assignment
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
125
+ end
@@ -0,0 +1,55 @@
1
+ require 'simplabs/excellent/parsing/cyclomatic_complexity_measure'
2
+ require 'simplabs/excellent/parsing/abc_measure'
3
+
4
+ module Simplabs
5
+
6
+ module Excellent
7
+
8
+ module Parsing
9
+
10
+ class SingletonMethodContext < MethodContext #:nodoc:
11
+
12
+ include CyclomaticComplexityMeasure
13
+ include AbcMeasure
14
+
15
+ attr_reader :parameters
16
+ attr_reader :calls
17
+
18
+ def initialize(exp, parent)
19
+ super
20
+ @name = exp[2].to_s
21
+ @full_name = get_full_name
22
+ @calls = Hash.new(0)
23
+ end
24
+
25
+ def full_name
26
+ return @full_name if @full_name
27
+ parent = @parent.is_a?(BlockContext) ? @parent.parent : @parent
28
+ return @name if parent.blank?
29
+ "#{parent.full_name}.#{@name}"
30
+ end
31
+
32
+ def record_call_to(exp)
33
+ @calls[exp] += 1
34
+ end
35
+
36
+ private
37
+
38
+ def get_full_name
39
+ if @exp[1].is_a?(Sexp)
40
+ if @exp[1].node_type == :call
41
+ return "#{@exp[1][2]}.#{@name}"
42
+ elsif @exp[1].node_type == :const
43
+ return "#{@exp[1][1]}.#{@name}"
44
+ end
45
+ end
46
+ nil
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,24 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing
6
+
7
+ class UntilContext < SexpContext #:nodoc:
8
+
9
+ def initialize(exp, parent)
10
+ super
11
+ @contains_assignment = has_assignment?
12
+ end
13
+
14
+ def tests_assignment?
15
+ @contains_assignment
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,24 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing
6
+
7
+ class WhileContext < SexpContext #:nodoc:
8
+
9
+ def initialize(exp, parent)
10
+ super
11
+ @contains_assignment = has_assignment?
12
+ end
13
+
14
+ def tests_assignment?
15
+ @contains_assignment
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1 @@
1
+ require 'simplabs/excellent/rake/excellent_task'
@@ -0,0 +1,61 @@
1
+ require 'rake'
2
+ require 'simplabs/excellent'
3
+
4
+ module Simplabs
5
+
6
+ module Excellent
7
+
8
+ module Rake #:nodoc:
9
+
10
+ # A special rake task for Excellent.
11
+ class ExcellentTask < ::Rake::TaskLib
12
+
13
+ # The Name of the task, defaults to <tt>:excellent</tt>.
14
+ attr_accessor :name
15
+
16
+ # Specifies whether to output HTML; defaults to false. Assign a file name to output HTML to that file.
17
+ attr_accessor :html
18
+
19
+ # The paths to process (specify file names or directories; will recursively process all ruby files if a directory is given).
20
+ attr_accessor :paths
21
+
22
+ # Initializes an ExcellentTask with the name +name+.
23
+ def initialize(name = :excellent)
24
+ @name = name
25
+ @paths = nil || []
26
+ @html = false
27
+ yield self if block_given?
28
+ define
29
+ end
30
+
31
+ def paths=(paths) #:nodoc:
32
+ if paths.is_a?(String)
33
+ @paths = [paths]
34
+ elsif paths.is_a?(Array)
35
+ @paths = paths
36
+ else
37
+ raise ArgumentError.new('Specify paths either as a String or as an Array!')
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def define
44
+ unless ::Rake.application.last_comment
45
+ desc 'Analyse the code with Excellent'
46
+ end
47
+ task name do
48
+ paths = @paths.join(' ')
49
+ format = @html ? " html:#{@html}" : ''
50
+ system("excellent#{format} #{paths}")
51
+ $stdout.puts("\nWrote Excellent result to #{@html}\n\n") if @html
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,143 @@
1
+ require 'pp'
2
+ require 'yaml'
3
+ require 'simplabs/excellent/parsing/parser'
4
+ require 'simplabs/excellent/parsing/code_processor'
5
+
6
+ module Simplabs
7
+
8
+ module Excellent
9
+
10
+ # The Runner is the interface to invoke parsing and processing of source code. You can pass either a String containing the code to process or the
11
+ # name of a file to read the code to process from.
12
+ class Runner
13
+
14
+ DEFAULT_CONFIG = {
15
+ :AssignmentInConditionalCheck => { },
16
+ :CaseMissingElseCheck => { },
17
+ :ClassLineCountCheck => { :threshold => 300 },
18
+ :ClassNameCheck => { :pattern => /^[A-Z][a-zA-Z0-9]*$/ },
19
+ :SingletonVariableCheck => { },
20
+ :GlobalVariableCheck => { },
21
+ :EmptyRescueBodyCheck => { },
22
+ :MethodLineCountCheck => { :line_count => 20 },
23
+ :MethodNameCheck => { :pattern => /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ },
24
+ :ModuleLineCountCheck => { :line_count => 300 },
25
+ :ModuleNameCheck => { :pattern => /^[A-Z][a-zA-Z0-9]*$/ },
26
+ :ParameterNumberCheck => { :parameter_count => 3 },
27
+ :'Rails::AttrProtectedCheck' => { },
28
+ :'Rails::AttrAccessibleCheck' => { },
29
+ :'Rails::InstanceVarInPartialCheck' => { },
30
+ :'Rails::ValidationsCheck' => { },
31
+ :'Rails::ParamsHashInViewCheck' => { },
32
+ :'Rails::SessionHashInViewCheck' => { },
33
+ :'Rails::CustomInitializeMethodCheck' => { }
34
+ }
35
+
36
+ attr_accessor :config #:nodoc:
37
+
38
+ # Initializes a Runner
39
+ #
40
+ # ==== Parameters
41
+ #
42
+ # * <tt>checks</tt> - The checks to apply - pass instances of the various check classes. If no checks are specified, all checks will be applied.
43
+ def initialize(*checks)
44
+ @config = DEFAULT_CONFIG
45
+ @checks = checks unless checks.empty?
46
+ @parser = Parsing::Parser.new
47
+ end
48
+
49
+ # Processes the +code+ and sets the file name of the warning to +filename+
50
+ #
51
+ # ==== Parameters
52
+ #
53
+ # * <tt>filename</tt> - The name of the file the code was read from.
54
+ # * <tt>code</tt> - The code to process (String).
55
+ def check(filename, code)
56
+ @checks ||= load_checks
57
+ @processor ||= Parsing::CodeProcessor.new(@checks)
58
+ node = parse(filename, code)
59
+ @processor.process(node)
60
+ end
61
+
62
+ # Processes the +code+, setting the file name of the warnings to '+dummy-file.rb+'
63
+ #
64
+ # ==== Parameters
65
+ #
66
+ # * <tt>code</tt> - The code to process (String).
67
+ def check_code(code)
68
+ check('dummy-file.rb', code)
69
+ end
70
+
71
+ # Processes the file +filename+. The code will be read from the file.
72
+ #
73
+ # ==== Parameters
74
+ #
75
+ # * <tt>filename</tt> - The name of the file to read the code from.
76
+ def check_file(filename)
77
+ check(filename, File.read(filename))
78
+ end
79
+
80
+ # Processes the passed +paths+
81
+ #
82
+ # ==== Parameters
83
+ #
84
+ # * <tt>paths</tt> - The paths to process (specify file names or directories; will recursively process all ruby files if a directory is given).
85
+ # * <tt>formatter</tt> - The formatter to use. If a formatter is specified, its +start+, +file+, +warning+ and +end+ methods will be called
86
+ def check_paths(paths, formatter = nil)
87
+ formatter.start if formatter
88
+ collect_files(paths).each do |path|
89
+ check_file(path)
90
+ format_file_and_warnings(formatter, path) if formatter
91
+ end
92
+ formatter.end if formatter
93
+ end
94
+
95
+ # Gets the warnings that were produced by the checks.
96
+ def warnings
97
+ @checks ||= []
98
+ @checks.collect { |check| check.warnings }.flatten
99
+ end
100
+
101
+ private
102
+
103
+ def parse(filename, code)
104
+ @parser.parse(code, filename)
105
+ end
106
+
107
+ def load_checks
108
+ check_objects = []
109
+ DEFAULT_CONFIG.each_pair do |key, value|
110
+ klass = eval("Simplabs::Excellent::Checks::#{key.to_s}")
111
+ check_objects << (value.empty? ? klass.new : klass.new(value))
112
+ end
113
+ check_objects
114
+ end
115
+
116
+ def collect_files(paths)
117
+ files = []
118
+ paths.each do |path|
119
+ if File.file?(path)
120
+ files << path
121
+ elsif File.directory?(path)
122
+ files += Dir.glob(File.join(path, '**/*.{rb,erb}'))
123
+ else
124
+ raise ArgumentError.new("#{path} is neither a File nor a directory!")
125
+ end
126
+ end
127
+ files
128
+ end
129
+
130
+ def format_file_and_warnings(formatter, filename)
131
+ warnings = @checks.map { |check| check.warnings_for(filename) }.flatten
132
+ if warnings.length > 0
133
+ formatter.file(filename) do |formatter|
134
+ warnings.sort { |x, y| x.line_number <=> y.line_number }.each { |warning| formatter.warning(warning) }
135
+ end
136
+ end
137
+ end
138
+
139
+ end
140
+
141
+ end
142
+
143
+ end