pelusa 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|