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.
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