pelusa 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/Rakefile +10 -0
- data/Readme.md +74 -0
- data/bin/pelusa +7 -0
- data/lib/pelusa.rb +22 -0
- data/lib/pelusa/analysis.rb +88 -0
- data/lib/pelusa/analyzer.rb +48 -0
- data/lib/pelusa/class_analyzer.rb +28 -0
- data/lib/pelusa/cli.rb +27 -0
- data/lib/pelusa/iterator.rb +39 -0
- data/lib/pelusa/lint.rb +29 -0
- data/lib/pelusa/lint/collection_wrappers.rb +50 -0
- data/lib/pelusa/lint/demeter_law.rb +35 -0
- data/lib/pelusa/lint/else_clauses.rb +40 -0
- data/lib/pelusa/lint/indentation_level.rb +94 -0
- data/lib/pelusa/lint/instance_variables.rb +39 -0
- data/lib/pelusa/lint/line_restriction.rb +41 -0
- data/lib/pelusa/lint/properties.rb +37 -0
- data/lib/pelusa/lint/short_identifiers.rb +54 -0
- data/lib/pelusa/report.rb +27 -0
- data/lib/pelusa/reporters/reporter.rb +22 -0
- data/lib/pelusa/reporters/ruby_reporter.rb +29 -0
- data/lib/pelusa/reporters/stdout_reporter.rb +50 -0
- data/lib/pelusa/runner.rb +53 -0
- data/lib/pelusa/version.rb +3 -0
- data/pelusa.gemspec +22 -0
- data/test/pelusa/analysis_test.rb +38 -0
- data/test/pelusa/analyzer_test.rb +33 -0
- data/test/pelusa/class_analyzer_test.rb +26 -0
- data/test/pelusa/iterator_test.rb +28 -0
- data/test/pelusa/lint/collection_wrappers_test.rb +42 -0
- data/test/pelusa/lint/demeter_law_test.rb +42 -0
- data/test/pelusa/lint/else_clauses_test.rb +50 -0
- data/test/pelusa/lint/indentation_level_test.rb +47 -0
- data/test/pelusa/lint/instance_variables_test.rb +44 -0
- data/test/pelusa/lint/line_restriction_test.rb +40 -0
- data/test/pelusa/lint/properties_test.rb +44 -0
- data/test/pelusa/lint/short_identifiers_test.rb +41 -0
- data/test/pelusa/reporters/ruby_reporter_test.rb +42 -0
- data/test/pelusa/runner_test.rb +27 -0
- data/test/pelusa_test.rb +19 -0
- data/test/test_helper.rb +5 -0
- metadata +118 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module Pelusa
|
2
|
+
class Runner
|
3
|
+
# Public: Initializes an Analyzer.
|
4
|
+
#
|
5
|
+
# lints - The lints to check the code for.
|
6
|
+
# reporter - The Reporter to use. Will be used to report back the results in
|
7
|
+
# methods such as #run.
|
8
|
+
def initialize(lints, reporter)
|
9
|
+
@lints = lints
|
10
|
+
@reporter = reporter
|
11
|
+
end
|
12
|
+
|
13
|
+
# Public: Runs the analyzer on a set of files.
|
14
|
+
#
|
15
|
+
# Returns an Array of Reports of those file runs.
|
16
|
+
def run(files)
|
17
|
+
reporters = Array(files).map do |file|
|
18
|
+
run_file(file)
|
19
|
+
end
|
20
|
+
@reporter.print_banner
|
21
|
+
|
22
|
+
exit_code = 0
|
23
|
+
|
24
|
+
reporters.each do |reporter|
|
25
|
+
reporter.report
|
26
|
+
exit_code = 1 unless reporter.successful?
|
27
|
+
end
|
28
|
+
exit_code
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Runs the analyzer on a single file.
|
32
|
+
#
|
33
|
+
# Returns a Report of the single run.
|
34
|
+
def run_file(file)
|
35
|
+
ast = parser.parse_file(file)
|
36
|
+
analyzer = Analyzer.new(@lints, @reporter, file)
|
37
|
+
analyzer.analyze(ast)
|
38
|
+
end
|
39
|
+
|
40
|
+
#######
|
41
|
+
private
|
42
|
+
#######
|
43
|
+
|
44
|
+
# Internal: Returns the parser used to analyze the files, depending on the
|
45
|
+
# current Rubinius mode.
|
46
|
+
#
|
47
|
+
# Returns a Rubinius::Melbourne parser.
|
48
|
+
def parser
|
49
|
+
return Rubinius::Melbourne19 if ENV['RBXOPT'].include?("-X19")
|
50
|
+
Rubinius::Melbourne
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/pelusa.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "pelusa/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "pelusa"
|
7
|
+
s.version = Pelusa::VERSION
|
8
|
+
s.authors = ["Josep M. Bach"]
|
9
|
+
s.email = ["josep.m.bach@gmail.com"]
|
10
|
+
s.homepage = "http://github.com/codegram/pelusa"
|
11
|
+
s.summary = %q{Static analysis Lint-type tool to improve your OO Ruby code}
|
12
|
+
s.description = %q{Static analysis Lint-type tool to improve your OO Ruby code}
|
13
|
+
|
14
|
+
s.rubyforge_project = "pelusa"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = Dir["test/**/*.rb"]
|
18
|
+
s.executables = ["pelusa"]
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency 'mocha'
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
describe SuccessfulAnalysis do
|
5
|
+
before do
|
6
|
+
@analysis = SuccessfulAnalysis.new("Is awesome")
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'is successful' do
|
10
|
+
@analysis.successful?.must_equal true
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#message' do
|
14
|
+
it 'has an empty message' do
|
15
|
+
@analysis.message.empty?.must_equal true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe FailedAnalysis do
|
21
|
+
before do
|
22
|
+
number_of_errors = 42
|
23
|
+
@analysis = FailedAnalysis.new("Is awesome", number_of_errors) do |errors|
|
24
|
+
"There have been #{errors} errors."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'is failed' do
|
29
|
+
@analysis.failed?.must_equal true
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#message' do
|
33
|
+
it 'describes the failed analysis' do
|
34
|
+
@analysis.message.must_equal "There have been 42 errors."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
describe Analyzer do
|
5
|
+
describe '#analyze' do
|
6
|
+
before do
|
7
|
+
@ast = """
|
8
|
+
class Foo
|
9
|
+
def bar
|
10
|
+
123
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Bar
|
15
|
+
def baz
|
16
|
+
321
|
17
|
+
end
|
18
|
+
end
|
19
|
+
""".to_ast
|
20
|
+
|
21
|
+
lints = stub
|
22
|
+
@analyzer = Analyzer.new([Lint::LineRestriction], RubyReporter, "foo.rb")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'analyzes an ast and returns a report' do
|
26
|
+
result = @analyzer.analyze(@ast).report
|
27
|
+
result[:filename].must_equal "foo.rb"
|
28
|
+
result[:Foo]["Is below 50 lines"][:status].must_equal "successful"
|
29
|
+
result[:Bar]["Is below 50 lines"][:status].must_equal "successful"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
describe ClassAnalyzer do
|
5
|
+
describe '#analyze' do
|
6
|
+
before do
|
7
|
+
@lints = [
|
8
|
+
stub(new: stub(check: true)),
|
9
|
+
stub(new: stub(check: true))
|
10
|
+
]
|
11
|
+
@klass = stub(name: stub(name: "Foo"))
|
12
|
+
@analyzer = ClassAnalyzer.new(@klass)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'analyzes a Class node for a series of lints' do
|
16
|
+
@analyzer.analyze(@lints).must_equal [true, true]
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#class_name' do
|
20
|
+
it 'returns the name of the analyzed class' do
|
21
|
+
@analyzer.class_name.must_equal "Foo"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
describe Iterator do
|
5
|
+
before do
|
6
|
+
@last_node = nil
|
7
|
+
@iterator = Iterator.new do |node|
|
8
|
+
@last_node = node if node == 3
|
9
|
+
end
|
10
|
+
|
11
|
+
@node = [ [ 1, [ 2, 3 ] ] ]
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#call' do
|
15
|
+
it 'calls the iterator on a node' do
|
16
|
+
@iterator.call(@node)
|
17
|
+
@last_node.must_equal 3
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#to_proc' do
|
22
|
+
it 'calls the iterator on a node' do
|
23
|
+
@iterator.to_proc[@node]
|
24
|
+
@last_node.must_equal 3
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
module Lint
|
5
|
+
describe CollectionWrappers do
|
6
|
+
before do
|
7
|
+
@lint = CollectionWrappers.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#check' do
|
11
|
+
describe 'when the class is not a collection wrapper with more instance variables' do
|
12
|
+
it 'returns a SuccessAnalysis' do
|
13
|
+
klass = """
|
14
|
+
class Foo
|
15
|
+
def initialize
|
16
|
+
@things = []
|
17
|
+
end
|
18
|
+
end""".to_ast
|
19
|
+
|
20
|
+
analysis = @lint.check(klass)
|
21
|
+
analysis.successful?.must_equal true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'when the class mixes collection ivars with others' do
|
26
|
+
it 'returns a FailureAnalysis' do
|
27
|
+
klass = """
|
28
|
+
class Foo
|
29
|
+
def initialize
|
30
|
+
@things = []
|
31
|
+
@foo = 'bar'
|
32
|
+
end
|
33
|
+
end""".to_ast
|
34
|
+
|
35
|
+
analysis = @lint.check(klass)
|
36
|
+
analysis.failed?.must_equal true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
module Lint
|
5
|
+
describe DemeterLaw do
|
6
|
+
before do
|
7
|
+
@lint = DemeterLaw.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#check' do
|
11
|
+
describe 'when the class respects Demeter law' do
|
12
|
+
it 'returns a SuccessAnalysis' do
|
13
|
+
klass = """
|
14
|
+
class Foo
|
15
|
+
def initialize
|
16
|
+
foo = 'hey'.upcase
|
17
|
+
foo.downcase
|
18
|
+
end
|
19
|
+
end""".to_ast
|
20
|
+
|
21
|
+
analysis = @lint.check(klass)
|
22
|
+
analysis.successful?.must_equal true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'when the class does not respect Demeter law' do
|
27
|
+
it 'returns a FailureAnalysis' do
|
28
|
+
klass = """
|
29
|
+
class Foo
|
30
|
+
def initialize
|
31
|
+
foo = 'hey'.upcase.downcase
|
32
|
+
end
|
33
|
+
end""".to_ast
|
34
|
+
|
35
|
+
analysis = @lint.check(klass)
|
36
|
+
analysis.failed?.must_equal true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
module Lint
|
5
|
+
describe ElseClauses do
|
6
|
+
before do
|
7
|
+
@lint = ElseClauses.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#check' do
|
11
|
+
describe 'when the class does not use else clauses' do
|
12
|
+
it 'returns a SuccessAnalysis' do
|
13
|
+
klass = """
|
14
|
+
class Foo
|
15
|
+
def initialize
|
16
|
+
if 3
|
17
|
+
8
|
18
|
+
end
|
19
|
+
unless 9
|
20
|
+
3
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end""".to_ast
|
24
|
+
|
25
|
+
analysis = @lint.check(klass)
|
26
|
+
analysis.successful?.must_equal true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'when the class uses else clauses' do
|
31
|
+
it 'returns a FailureAnalysis' do
|
32
|
+
klass = """
|
33
|
+
class Foo
|
34
|
+
def initialize
|
35
|
+
if 3
|
36
|
+
8
|
37
|
+
else
|
38
|
+
9
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end""".to_ast
|
42
|
+
|
43
|
+
analysis = @lint.check(klass)
|
44
|
+
analysis.failed?.must_equal true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
module Lint
|
5
|
+
describe IndentationLevel do
|
6
|
+
before do
|
7
|
+
@lint = IndentationLevel.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#check' do
|
11
|
+
describe 'when the class has one method with one or less indentation levels' do
|
12
|
+
it 'returns a SuccessAnalysis' do
|
13
|
+
klass = """
|
14
|
+
class Foo
|
15
|
+
def initialize
|
16
|
+
if 9
|
17
|
+
3
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end""".to_ast
|
21
|
+
|
22
|
+
analysis = @lint.check(klass)
|
23
|
+
analysis.successful?.must_equal true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'when the class has one method with more than one indentation level' do
|
28
|
+
it 'returns a FailureAnalysis' do
|
29
|
+
klass = """
|
30
|
+
class Foo
|
31
|
+
def initialize
|
32
|
+
if 9
|
33
|
+
unless 3
|
34
|
+
5
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end""".to_ast
|
39
|
+
|
40
|
+
analysis = @lint.check(klass)
|
41
|
+
analysis.failed?.must_equal true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
module Lint
|
5
|
+
describe InstanceVariables do
|
6
|
+
before do
|
7
|
+
@lint = InstanceVariables.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#check' do
|
11
|
+
describe 'when the class uses less than 3 ivars' do
|
12
|
+
it 'returns a SuccessAnalysis' do
|
13
|
+
klass = """
|
14
|
+
class Foo
|
15
|
+
def initialize
|
16
|
+
@foo = 1
|
17
|
+
@bar = 2
|
18
|
+
end
|
19
|
+
end""".to_ast
|
20
|
+
|
21
|
+
analysis = @lint.check(klass)
|
22
|
+
analysis.successful?.must_equal true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'when the class has more than 50 lines' do
|
27
|
+
it 'returns a FailureAnalysis' do
|
28
|
+
klass = """
|
29
|
+
class Foo
|
30
|
+
def initialize
|
31
|
+
@foo = 1
|
32
|
+
@bar = 2
|
33
|
+
@baz = 3
|
34
|
+
end
|
35
|
+
end""".to_ast
|
36
|
+
|
37
|
+
analysis = @lint.check(klass)
|
38
|
+
analysis.failed?.must_equal true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Pelusa
|
4
|
+
module Lint
|
5
|
+
describe LineRestriction do
|
6
|
+
before do
|
7
|
+
@lint = LineRestriction.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#check' do
|
11
|
+
describe 'when the class has less than 50 lines' do
|
12
|
+
it 'returns a SuccessAnalysis' do
|
13
|
+
klass = """
|
14
|
+
class Foo
|
15
|
+
attr_accessor :foo
|
16
|
+
attr_accessor :bar
|
17
|
+
end""".to_ast
|
18
|
+
|
19
|
+
analysis = @lint.check(klass)
|
20
|
+
analysis.successful?.must_equal true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'when the class has more than 50 lines' do
|
25
|
+
it 'returns a FailureAnalysis' do
|
26
|
+
klass = """
|
27
|
+
class Foo
|
28
|
+
attr_accessor :foo
|
29
|
+
#{"\n" * 80}
|
30
|
+
attr_accessor :bar
|
31
|
+
end""".to_ast
|
32
|
+
|
33
|
+
analysis = @lint.check(klass)
|
34
|
+
analysis.failed?.must_equal true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|