pelusa 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +5 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +4 -0
  5. data/Rakefile +10 -0
  6. data/Readme.md +74 -0
  7. data/bin/pelusa +7 -0
  8. data/lib/pelusa.rb +22 -0
  9. data/lib/pelusa/analysis.rb +88 -0
  10. data/lib/pelusa/analyzer.rb +48 -0
  11. data/lib/pelusa/class_analyzer.rb +28 -0
  12. data/lib/pelusa/cli.rb +27 -0
  13. data/lib/pelusa/iterator.rb +39 -0
  14. data/lib/pelusa/lint.rb +29 -0
  15. data/lib/pelusa/lint/collection_wrappers.rb +50 -0
  16. data/lib/pelusa/lint/demeter_law.rb +35 -0
  17. data/lib/pelusa/lint/else_clauses.rb +40 -0
  18. data/lib/pelusa/lint/indentation_level.rb +94 -0
  19. data/lib/pelusa/lint/instance_variables.rb +39 -0
  20. data/lib/pelusa/lint/line_restriction.rb +41 -0
  21. data/lib/pelusa/lint/properties.rb +37 -0
  22. data/lib/pelusa/lint/short_identifiers.rb +54 -0
  23. data/lib/pelusa/report.rb +27 -0
  24. data/lib/pelusa/reporters/reporter.rb +22 -0
  25. data/lib/pelusa/reporters/ruby_reporter.rb +29 -0
  26. data/lib/pelusa/reporters/stdout_reporter.rb +50 -0
  27. data/lib/pelusa/runner.rb +53 -0
  28. data/lib/pelusa/version.rb +3 -0
  29. data/pelusa.gemspec +22 -0
  30. data/test/pelusa/analysis_test.rb +38 -0
  31. data/test/pelusa/analyzer_test.rb +33 -0
  32. data/test/pelusa/class_analyzer_test.rb +26 -0
  33. data/test/pelusa/iterator_test.rb +28 -0
  34. data/test/pelusa/lint/collection_wrappers_test.rb +42 -0
  35. data/test/pelusa/lint/demeter_law_test.rb +42 -0
  36. data/test/pelusa/lint/else_clauses_test.rb +50 -0
  37. data/test/pelusa/lint/indentation_level_test.rb +47 -0
  38. data/test/pelusa/lint/instance_variables_test.rb +44 -0
  39. data/test/pelusa/lint/line_restriction_test.rb +40 -0
  40. data/test/pelusa/lint/properties_test.rb +44 -0
  41. data/test/pelusa/lint/short_identifiers_test.rb +41 -0
  42. data/test/pelusa/reporters/ruby_reporter_test.rb +42 -0
  43. data/test/pelusa/runner_test.rb +27 -0
  44. data/test/pelusa_test.rb +19 -0
  45. data/test/test_helper.rb +5 -0
  46. 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
@@ -0,0 +1,3 @@
1
+ module Pelusa
2
+ VERSION = "0.0.1"
3
+ end
@@ -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