jruby-lint 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +12 -0
  3. data/Guardfile +12 -0
  4. data/History.txt +4 -0
  5. data/README.md +62 -0
  6. data/Rakefile +24 -0
  7. data/bin/jrlint +5 -0
  8. data/jruby-lint.gemspec +38 -0
  9. data/lib/jruby/lint.rb +19 -0
  10. data/lib/jruby/lint/ast.rb +56 -0
  11. data/lib/jruby/lint/checkers.rb +24 -0
  12. data/lib/jruby/lint/checkers/fork_exec.rb +47 -0
  13. data/lib/jruby/lint/checkers/gem.rb +45 -0
  14. data/lib/jruby/lint/checkers/gemspec.rb +41 -0
  15. data/lib/jruby/lint/checkers/object_space.rb +25 -0
  16. data/lib/jruby/lint/checkers/thread_critical.rb +26 -0
  17. data/lib/jruby/lint/cli.rb +61 -0
  18. data/lib/jruby/lint/collectors.rb +75 -0
  19. data/lib/jruby/lint/collectors/bundler.rb +9 -0
  20. data/lib/jruby/lint/collectors/gemspec.rb +9 -0
  21. data/lib/jruby/lint/collectors/rake.rb +9 -0
  22. data/lib/jruby/lint/collectors/ruby.rb +14 -0
  23. data/lib/jruby/lint/finding.rb +15 -0
  24. data/lib/jruby/lint/github.crt +76 -0
  25. data/lib/jruby/lint/libraries.rb +107 -0
  26. data/lib/jruby/lint/project.rb +56 -0
  27. data/lib/jruby/lint/reporters.rb +35 -0
  28. data/lib/jruby/lint/version.rb +5 -0
  29. data/spec/fixtures/C-Extension-Alternatives.html +557 -0
  30. data/spec/jruby/lint/ast_spec.rb +23 -0
  31. data/spec/jruby/lint/checkers_spec.rb +135 -0
  32. data/spec/jruby/lint/cli_spec.rb +66 -0
  33. data/spec/jruby/lint/collectors_spec.rb +34 -0
  34. data/spec/jruby/lint/finding_spec.rb +31 -0
  35. data/spec/jruby/lint/libraries_spec.rb +56 -0
  36. data/spec/jruby/lint/project_spec.rb +77 -0
  37. data/spec/jruby/lint/reporters_spec.rb +42 -0
  38. data/spec/jruby/lint/version_spec.rb +6 -0
  39. data/spec/spec_helper.rb +30 -0
  40. metadata +194 -0
@@ -0,0 +1,26 @@
1
+ module JRuby::Lint
2
+ module Checkers
3
+ class ThreadCritical
4
+ include Checker
5
+
6
+ METHODS = %w(critical critical=)
7
+
8
+ def visitCallNode(node)
9
+ if METHODS.include?(node.name)
10
+ begin
11
+ if node.receiver_node.node_type.to_s == "CONSTNODE" && node.receiver_node.name == "Thread"
12
+ add_finding(collector, node)
13
+ end
14
+ rescue
15
+ end
16
+ end
17
+ end
18
+ alias visitAttrAssignNode visitCallNode
19
+
20
+ def add_finding(collector, node)
21
+ collector.findings << Finding.new("Use of Thread.critical is discouraged. Use a Mutex instead.",
22
+ [:threads, :warning], node.position)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,61 @@
1
+ module JRuby
2
+ module Lint
3
+ class CLI
4
+ def initialize(args)
5
+ process_options(args)
6
+ end
7
+
8
+ def process_options(args)
9
+ require 'optparse'
10
+ require 'ostruct'
11
+ @options = OpenStruct.new
12
+ OptionParser.new do |opts|
13
+ opts.banner = "Usage: jrlint [options] [files]"
14
+ opts.separator ""
15
+ opts.separator "Options:"
16
+
17
+ opts.on('-C', '--chdir DIRECTORY', "Change working directory") do |v|
18
+ Dir.chdir(v)
19
+ end
20
+
21
+ opts.on("-e", '--eval SCRIPT', "Lint an inline script") do |v|
22
+ @options.eval ||= []
23
+ @options.eval << v
24
+ end
25
+
26
+ opts.on_tail("-v", "--version", "Print version and exit") do
27
+ require 'jruby/lint/version'
28
+ puts "JRuby-Lint version #{VERSION}"
29
+ exit
30
+ end
31
+
32
+ opts.on_tail("-h", "--help", "This message") do
33
+ puts opts
34
+ exit
35
+ end
36
+ end.parse!(args)
37
+
38
+ @options.files = args.empty? ? nil : args
39
+ end
40
+
41
+ def run
42
+ require 'jruby/lint'
43
+ require 'benchmark'
44
+ project = JRuby::Lint::Project.new(@options)
45
+
46
+ puts "JRuby-Lint version #{JRuby::Lint::VERSION}"
47
+ time = Benchmark.realtime { project.run }
48
+ term = @options.eval ? 'expression' : 'file'
49
+ puts "Processed #{project.files.size} #{term}#{project.files.size == 1 ? '' : 's'} in #{'%0.02f' % time} seconds"
50
+
51
+ if project.findings.empty?
52
+ puts "OK"
53
+ exit
54
+ else
55
+ puts "Found #{project.findings.size} items"
56
+ exit 1
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,75 @@
1
+ module JRuby::Lint
2
+ class Collector
3
+ attr_accessor :checkers, :findings, :project, :contents, :file
4
+
5
+ def initialize(project = nil, file = nil)
6
+ @checkers = Checker.loaded_checkers.map(&:new)
7
+ @checkers.each {|c| c.collector = self }
8
+ @findings = []
9
+ @project, @file = project, file || '<inline-script>'
10
+ end
11
+
12
+ class CheckersVisitor < AST::Visitor
13
+ attr_reader :checkers
14
+
15
+ def initialize(ast, checkers)
16
+ super(ast)
17
+ @checkers = checkers
18
+ end
19
+
20
+ def visit(method, node)
21
+ after_hooks = []
22
+ checkers.each do |ch|
23
+ begin
24
+ if ch.respond_to?(method)
25
+ res = ch.send(method, node)
26
+ after_hooks << res if res.respond_to?(:call)
27
+ end
28
+ rescue => e
29
+ ch.collector.findings << Finding.new("Exception while traversing: #{e.message}",
30
+ [:internal, :debug], node.position)
31
+ end
32
+ end
33
+ super
34
+ ensure
35
+ begin
36
+ after_hooks.each {|h| h.call }
37
+ rescue
38
+ end
39
+ end
40
+ end
41
+
42
+ def run
43
+ begin
44
+ CheckersVisitor.new(ast, checkers).traverse
45
+ rescue SyntaxError => e
46
+ file, line, message = e.message.split(/:\s*/, 3)
47
+ findings << Finding.new(message, [:syntax, :error], file, line)
48
+ end
49
+ end
50
+
51
+ def ast
52
+ @ast ||= JRuby.parse(contents, file, true)
53
+ end
54
+
55
+ def contents
56
+ @contents || File.read(@file)
57
+ end
58
+
59
+ def self.inherited(base)
60
+ self.all << base
61
+ end
62
+
63
+ def self.all
64
+ @collectors ||= []
65
+ end
66
+ end
67
+
68
+ module Collectors
69
+ end
70
+ end
71
+
72
+ require 'jruby/lint/collectors/ruby'
73
+ require 'jruby/lint/collectors/bundler'
74
+ require 'jruby/lint/collectors/rake'
75
+ require 'jruby/lint/collectors/gemspec'
@@ -0,0 +1,9 @@
1
+ module JRuby::Lint
2
+ module Collectors
3
+ class Bundler < Collector
4
+ def self.detect?(f)
5
+ File.basename(f) == 'Gemfile'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module JRuby::Lint
2
+ module Collectors
3
+ class Gemspec < Collector
4
+ def self.detect?(f)
5
+ File.extname(f) == '.gemspec'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module JRuby::Lint
2
+ module Collectors
3
+ class Rake < Collector
4
+ def self.detect?(f)
5
+ File.basename(f) == 'Rakefile' || File.extname(f) == '.rake'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ module JRuby::Lint
2
+ module Collectors
3
+ class Ruby < Collector
4
+ def initialize(project, file, contents = nil)
5
+ super(project, file)
6
+ @contents = contents
7
+ end
8
+
9
+ def self.detect?(f)
10
+ File.extname(f) == '.rb'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module JRuby::Lint
2
+ class Finding < Struct.new(:message, :tags, :file, :line)
3
+ def initialize(*args)
4
+ args[1].map! {|x| x.to_s } if args.size > 1
5
+ if args.size > 2 && args[2].respond_to?(:file) && args[2].respond_to?(:line)
6
+ args = [args[0], args[1], args[2].file, (args[2].line + 1)]
7
+ end
8
+ super
9
+ end
10
+
11
+ def to_s
12
+ "#{file}:#{line}: [#{tags.join(', ')}] #{message}"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,76 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMx
3
+ ITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
4
+ RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYw
5
+ MTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMH
6
+ QXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5j
7
+ b20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j
8
+ b20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmlj
9
+ YXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcN
10
+ AQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3H
11
+ KrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQm
12
+ VZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpR
13
+ SgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRT
14
+ cDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ
15
+ 6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEu
16
+ MB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDS
17
+ kdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEB
18
+ BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0f
19
+ BD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv
20
+ c2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUH
21
+ AgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAO
22
+ BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IG
23
+ OgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMU
24
+ A2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o
25
+ 0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTX
26
+ RE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuH
27
+ qDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWV
28
+ U+4=
29
+ -----END CERTIFICATE-----
30
+ -----BEGIN CERTIFICATE-----
31
+ MIIE+zCCBGSgAwIBAgICAQ0wDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Zh
32
+ bGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
33
+ Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
34
+ QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAe
35
+ BgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MDYyMFoX
36
+ DTI0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBE
37
+ YWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0
38
+ aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgC
39
+ ggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
40
+ 2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+q
41
+ N1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiO
42
+ r18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lN
43
+ f4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+YihfukEH
44
+ U1jPEX44dMX4/7VpkI+EdOqXG68CAQOjggHhMIIB3TAdBgNVHQ4EFgQU0sSw0pHU
45
+ TBFxs2HLPaH+3ahq1OMwgdIGA1UdIwSByjCBx6GBwaSBvjCBuzEkMCIGA1UEBxMb
46
+ VmFsaUNlcnQgVmFsaWRhdGlvbiBOZXR3b3JrMRcwFQYDVQQKEw5WYWxpQ2VydCwg
47
+ SW5jLjE1MDMGA1UECxMsVmFsaUNlcnQgQ2xhc3MgMiBQb2xpY3kgVmFsaWRhdGlv
48
+ biBBdXRob3JpdHkxITAfBgNVBAMTGGh0dHA6Ly93d3cudmFsaWNlcnQuY29tLzEg
49
+ MB4GCSqGSIb3DQEJARYRaW5mb0B2YWxpY2VydC5jb22CAQEwDwYDVR0TAQH/BAUw
50
+ AwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmdv
51
+ ZGFkZHkuY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jZXJ0aWZpY2F0ZXMu
52
+ Z29kYWRkeS5jb20vcmVwb3NpdG9yeS9yb290LmNybDBLBgNVHSAERDBCMEAGBFUd
53
+ IAAwODA2BggrBgEFBQcCARYqaHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNv
54
+ bS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOBgQC1
55
+ QPmnHfbq/qQaQlpE9xXUhUaJwL6e4+PrxeNYiY+Sn1eocSxI0YGyeR+sBjUZsE4O
56
+ WBsUs5iB0QQeyAfJg594RAoYC5jcdnplDQ1tgMQLARzLrUc+cb53S8wGd9D0Vmsf
57
+ SxOaFIqII6hR8INMqzW/Rn453HWkrugp++85j09VZw==
58
+ -----END CERTIFICATE-----
59
+ -----BEGIN CERTIFICATE-----
60
+ MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
61
+ IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
62
+ BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
63
+ aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
64
+ 9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
65
+ NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
66
+ azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
67
+ YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
68
+ Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
69
+ cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
70
+ dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
71
+ WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
72
+ v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
73
+ UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
74
+ IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
75
+ W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
76
+ -----END CERTIFICATE-----
@@ -0,0 +1,107 @@
1
+ require 'net/https'
2
+ require 'nokogiri'
3
+ require 'tempfile'
4
+ require 'fileutils'
5
+
6
+ module JRuby::Lint
7
+ class Libraries
8
+ class Cache
9
+ def initialize(cache_dir = nil)
10
+ @cache_dir = cache_dir || ENV['JRUBY_LINT_CACHE'] ||
11
+ (defined?(Gem.user_dir) && File.join(Gem.user_dir, 'lint')) || Dir::tmpdir
12
+ FileUtils.mkdir_p(@cache_dir) unless File.directory?(@cache_dir)
13
+ end
14
+
15
+ def fetch(name)
16
+ filename = filename_for(name)
17
+ if File.file?(filename) && !stale?(filename)
18
+ File.read(filename)
19
+ else
20
+ read_from_wiki(name, filename)
21
+ end
22
+ end
23
+
24
+ def store(name, content)
25
+ File.open(filename_for(name), "w") {|f| f << content }
26
+ end
27
+
28
+ def filename_for(name)
29
+ name = File.basename(name)
30
+ File.join(@cache_dir, File.extname(name).empty? ? "#{name}.html" : name)
31
+ end
32
+
33
+ def stale?(filename)
34
+ File.mtime(filename) < Time.now - 24 * 60 * 60
35
+ end
36
+
37
+ def read_from_wiki(name, filename)
38
+ content = nil
39
+ uri = Net::HTTP.start('wiki.jruby.org', 80) do |http|
40
+ URI.parse http.head(name =~ %r{^/} ? name : "/#{name}")['Location']
41
+ end
42
+ if uri.host == "github.com"
43
+ Net::HTTP.new(uri.host, uri.port).tap do |http|
44
+ if uri.scheme == "https"
45
+ http.use_ssl = true
46
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
47
+ # gd_bundle.crt from https://certs.godaddy.com/anonymous/repository.seam
48
+ http.ca_file = File.expand_path('../github.crt', __FILE__)
49
+ end
50
+ http.start do
51
+ response = http.get(uri.path)
52
+ content = response.body
53
+ File.open(filename, "w") do |f|
54
+ f << content
55
+ end
56
+ end
57
+ end
58
+ end
59
+ raise "Unknown location '#{uri}' for page '#{name}'" unless content
60
+ content
61
+ rescue => e
62
+ raise "Error while reading from wiki: #{e.message}\nPlease try again later."
63
+ end
64
+ end
65
+
66
+ class CExtensions
67
+ attr_reader :gems
68
+
69
+ def initialize(cache)
70
+ @cache = cache
71
+ end
72
+
73
+ def load
74
+ @gems = {}
75
+ content = @cache.fetch('C-Extension-Alternatives')
76
+ doc = Nokogiri::HTML(content)
77
+ doc.css('#wiki-body ul li').each do |li|
78
+ key, message = li.text.split(/[ -]+/, 2)
79
+ @gems[key.downcase] = message
80
+ end
81
+ rescue => e
82
+ @error = "Unable to load C Extension alternatives list: #{e.message}"
83
+ end
84
+ end
85
+
86
+ SOURCES = [CExtensions]
87
+
88
+ attr_reader :gems
89
+
90
+ def initialize(cache)
91
+ @sources = SOURCES.map {|s| s.new(cache) }
92
+ end
93
+
94
+ def load
95
+ @gems = {}.tap do |gems|
96
+ @sources.each do |s|
97
+ s.load
98
+ gems.update(s.gems)
99
+ end
100
+ end
101
+ end
102
+
103
+ def gems
104
+ @gems ||= load
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,56 @@
1
+ require 'set'
2
+ require 'ostruct'
3
+
4
+ module JRuby::Lint
5
+ class Project
6
+ DEFAULT_TAGS = %w(error warning info)
7
+
8
+ attr_reader :collectors, :reporters, :findings, :files, :tags, :libraries
9
+
10
+ def initialize(options = OpenStruct.new)
11
+ @tags = DEFAULT_TAGS
12
+ @collectors = []
13
+ @files = Set.new
14
+
15
+ if options.eval
16
+ options.eval.each {|e| @collectors << JRuby::Lint::Collectors::Ruby.new(self, '-e', e) }
17
+ @files += @collectors
18
+ end
19
+
20
+ @sources = options.files || (options.eval ? [] : Dir['./**/*'])
21
+ load_collectors
22
+ load_reporters
23
+ load_libraries
24
+ end
25
+
26
+ def run
27
+ @findings = []
28
+ collectors.each do |c|
29
+ c.run
30
+ reporters.each {|r| r.report(c.findings)}
31
+ @findings += c.findings
32
+ end
33
+ end
34
+
35
+ private
36
+ def load_collectors
37
+ @sources.each do |f|
38
+ next unless File.file?(f)
39
+ Collector.all.each do |c|
40
+ if c.detect?(f)
41
+ @collectors << c.new(self, f)
42
+ @files << f
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def load_reporters
49
+ @reporters = [(STDOUT.tty? ? Reporters::ANSIColor : Reporters::Text).new(self, STDOUT)]
50
+ end
51
+
52
+ def load_libraries
53
+ @libraries = Libraries.new(Libraries::Cache.new)
54
+ end
55
+ end
56
+ end