pelusa 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjA3NTU1ZWZkNGViYTBmNTFhMmJmNGQ1MDZlNjQxNjcwYTJkMTA1ZQ==
5
+ data.tar.gz: !binary |-
6
+ NzEzMmJlNmE5N2ZhYTE3MjgyODMzMDgxMmEyMjYxMmFhYmZlZjBjOA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MjFmZmQyNzRiZWI3YjU0ZjE1MTNkNjViMmE5YTFiMzEwMjhiYWU1MGNiMWVi
10
+ YTI1ZDhiMjkwZjYyNjBhNGJhMThlOGQ0ZjE4M2FmMDBhZjhmNWQwMjRiNTBk
11
+ ZDgwOGQ5NzAzZmFiNTdkOTMwMTBmNmNhMWUwMDc4ZjIyMmI0OTM=
12
+ data.tar.gz: !binary |-
13
+ ODI4ZGVkOWE5MTgzMzdiOWYyNmNmYzMxZTNkOTRhY2E4M2FjNGQyOTRlYTg4
14
+ NDhmOTVkMWI0OWJkZTlmNjk3MjViNWEzMmUxYTRhMzMxNzQ5ZmY4ZWNjYTk5
15
+ OWQxOGUzZjFjZTQyNmI1OWZkODE3YjRkODUwNDkzMzgzY2RjY2I=
@@ -24,8 +24,6 @@ require 'pelusa/lint'
24
24
  require 'pelusa/analysis'
25
25
  require 'pelusa/class_analyzer'
26
26
  require 'pelusa/report'
27
- require 'pelusa/iterator'
28
-
29
27
 
30
28
  require 'pelusa/reporters/reporter'
31
29
  require 'pelusa/reporters/stdout_reporter'
@@ -19,9 +19,10 @@ module Pelusa
19
19
  reports = extract_classes(ast).map do |klass|
20
20
  class_analyzer = ClassAnalyzer.new(klass)
21
21
  class_name = class_analyzer.class_name
22
+ type = class_analyzer.type
22
23
  analysis = class_analyzer.analyze(@lints)
23
24
 
24
- Report.new(class_name, analysis)
25
+ Report.new(class_name, type, analysis)
25
26
  end
26
27
  @reporter.reports = reports
27
28
  @reporter
@@ -38,10 +39,16 @@ module Pelusa
38
39
  # Returns an Array of Class nodes.
39
40
  def extract_classes(ast)
40
41
  classes = []
41
- class_iterator = Iterator.new do |node|
42
- classes << node if node.is_a?(Rubinius::AST::Class)
42
+ if ast.is_a?(Rubinius::AST::Class) || ast.is_a?(Rubinius::AST::Module)
43
+ classes << ast
44
+ end
45
+
46
+ ast.walk do |continue, node|
47
+ if node.is_a?(Rubinius::AST::Class) || node.is_a?(Rubinius::AST::Module)
48
+ classes << node
49
+ end
50
+ true
43
51
  end
44
- Array(ast).each(&class_iterator)
45
52
  classes
46
53
  end
47
54
  end
@@ -14,6 +14,11 @@ class Pelusa::ClassAnalyzer
14
14
  name.name
15
15
  end
16
16
 
17
+ # Public: Returns the type of container being examined (class or module).
18
+ def type
19
+ @klass.is_a?(Rubinius::AST::Class) ? "class" : "module"
20
+ end
21
+
17
22
  # Public: Analyzes a class with a series of lints.
18
23
  #
19
24
  # lints - The lints to check for.
@@ -25,4 +30,15 @@ class Pelusa::ClassAnalyzer
25
30
  lint.check(@klass)
26
31
  end
27
32
  end
33
+
34
+ # Public: Walk a node, analyzing it as it goes.
35
+ #
36
+ # block - supply a block that will be executed as the node gets walked.
37
+ def self.walk(start_node)
38
+ raise ArgumentError, "Walk requires a block!" unless block_given?
39
+ start_node.walk do |continue, node|
40
+ yield(node)
41
+ true
42
+ end
43
+ end
28
44
  end
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if @violations.empty?
@@ -23,12 +22,11 @@ module Pelusa
23
22
  end
24
23
 
25
24
  def iterate_lines!(klass)
26
- iterator = Iterator.new do |node|
25
+ ClassAnalyzer.walk(klass) do |node|
27
26
  if node.is_a?(Rubinius::AST::Case)
28
27
  @violations << node.line
29
28
  end
30
29
  end
31
- Array(klass).each(&iterator)
32
30
  end
33
31
  end
34
32
  end
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if @violations.empty?
@@ -23,27 +22,35 @@ module Pelusa
23
22
  end
24
23
 
25
24
  def iterate_lines!(klass)
26
- array_assignment = nil
25
+ # the array of actual assignment nodes -- we only want to assign once
26
+ array_assignments = {}
27
+ # the name of all the assigned arrays, no others allowed
28
+ array_values = {}
27
29
 
28
- get_array_assignment = Iterator.new do |node|
30
+ ClassAnalyzer.walk(klass) do |node|
29
31
  if node.is_a?(Rubinius::AST::InstanceVariableAssignment) &&
30
32
  (node.value.is_a?(Rubinius::AST::ArrayLiteral) ||
31
- node.value.is_a?(Rubinius::AST::EmptyArray))
32
- array_assignment = node
33
+ node.value.is_a?(Rubinius::AST::EmptyArray))
34
+ array_assignments[node] = true
35
+ array_values[node.name] = true
33
36
  end
34
37
  end
35
38
 
36
- iterator = Iterator.new do |node|
37
- next if node == array_assignment || !array_assignment
38
-
39
- if node.is_a?(Rubinius::AST::InstanceVariableAssignment)
40
- @violations << node.line
41
- elsif node.is_a?(Rubinius::AST::InstanceVariableAccess) && node.name != array_assignment.name
42
- @violations << node.line
39
+ unless array_assignments.empty?
40
+ ClassAnalyzer.walk(klass) do |node|
41
+ # if this is where we assign the node for the first time, good
42
+ unless array_assignments[node]
43
+ # otherwise, if it's an instance variable assignment, verboten!
44
+ if node.is_a?(Rubinius::AST::InstanceVariableAssignment)
45
+ @violations << node.line
46
+ # or if we access any other ivars
47
+ elsif node.is_a?(Rubinius::AST::InstanceVariableAccess) &&
48
+ !array_values[node.name]
49
+ @violations << node.line
50
+ end
51
+ end
43
52
  end
44
53
  end
45
- Array(klass).each(&get_array_assignment)
46
- Array(klass).each(&iterator)
47
54
  end
48
55
  end
49
56
  end
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if @violations.empty?
@@ -23,17 +22,44 @@ module Pelusa
23
22
  end
24
23
 
25
24
  def iterate_lines!(klass)
26
- iterator = Iterator.new do |node|
25
+ ClassAnalyzer.walk(klass) do |node|
27
26
  if node.is_a?(Rubinius::AST::Send) && node.receiver.is_a?(Rubinius::AST::Send)
28
27
  @violations << node.line unless white_listed?(node.receiver.name)
29
28
  end
30
29
  end
31
- Array(klass).each(&iterator)
30
+ end
31
+
32
+ # Internal: Default modules whose methods are whitelisted.
33
+ DEFAULT_WHITELIST = [Class, Fixnum, Enumerable]
34
+
35
+ # Internal: Methods on these common, fundamental classes and modules are
36
+ # allowed to be called on other objects.
37
+ #
38
+ # Note: this doesn't currently work with namespaced objects, but would be
39
+ # easy to extend.
40
+ #
41
+ # Returns an array of classes specified in .pelusa.yml (a comma-separated
42
+ # list of class names) or the default values above.
43
+ def whitelist_sources
44
+ if whitelist = Pelusa.configuration['DemeterLaw']['whitelist']
45
+ whitelist.split(",").map {|k| Kernel.const_get(k.strip)}
46
+ else
47
+ DEFAULT_WHITELIST
48
+ end
49
+ end
50
+
51
+ # Internal: Allow conversion methods -- matching /^(to_|as_)/ to violate
52
+ # the law of Demeter.
53
+ def allow_conversions?
54
+ Pelusa.configuration['DemeterLaw'].fetch('allow_conversions', false)
32
55
  end
33
56
 
34
57
  def white_listed? method
35
- [Class, Fixnum, Enumerable].any? do |enclosing_module|
36
- enclosing_module.instance_methods.any? {|instance_method| instance_method == method }
58
+ whitelist_sources.any? do |enclosing_module|
59
+ enclosing_module.instance_methods.any? do |instance_method|
60
+ instance_method == method or
61
+ allow_conversions? && instance_method =~ /^(to_|as_)/
62
+ end
37
63
  end
38
64
  end
39
65
  end
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if @violations.empty?
@@ -23,7 +22,7 @@ module Pelusa
23
22
  end
24
23
 
25
24
  def iterate_lines!(klass)
26
- iterator = Iterator.new do |node|
25
+ ClassAnalyzer.walk(klass) do |node|
27
26
  if node.is_a?(Rubinius::AST::If)
28
27
  has_body = node.body && !node.body.is_a?(Rubinius::AST::NilLiteral)
29
28
  has_else = node.else && !node.else.is_a?(Rubinius::AST::NilLiteral)
@@ -33,7 +32,6 @@ module Pelusa
33
32
  end
34
33
  end
35
34
  end
36
- Array(klass).each(&iterator)
37
35
  end
38
36
  end
39
37
  end
@@ -3,7 +3,6 @@
3
3
  module Pelusa
4
4
  module Lint
5
5
  class EvalUsage
6
-
7
6
  def initialize
8
7
  @violations = Set.new
9
8
  end
@@ -27,10 +26,9 @@ module Pelusa
27
26
  end
28
27
 
29
28
  def iterate_lines!(klass)
30
- iterator = Iterator.new do |node|
29
+ ClassAnalyzer.walk(klass) do |node|
31
30
  @violations << node.line if eval_violation?(node)
32
31
  end
33
- Array(klass).each(&iterator)
34
32
  end
35
33
 
36
34
  def eval_violation?(node)
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if @violations.empty?
@@ -23,24 +22,33 @@ module Pelusa
23
22
  end
24
23
 
25
24
  def iterate_lines!(klass)
26
- iterator = Iterator.new do |node|
25
+ # we want to find all nodes inside define blocks that
26
+ # contain > 1 indentation levels
27
+ # this method totally fails the IndentationLevel level lint :P
28
+ ClassAnalyzer.walk(klass) do |node|
27
29
  if node.is_a?(Rubinius::AST::Define)
28
- _iterate = Iterator.new do |node|
29
- __iterate = Iterator.new do |node|
30
- if body = get_body_from_node[node]
31
- if node.line != [body].flatten.first.line
32
- @violations.merge Array(body).map(&:line)
30
+ # we're inside a method body, so see if we indent anywhere
31
+ ClassAnalyzer.walk(node) do |inner_node|
32
+ if inner_body = get_body_from_node[inner_node]
33
+ # if it turns out there's an indented value in there, that
34
+ # could be okay -- walk that node to see if there's a 2nd level
35
+ if inner_node.line != [inner_body].flatten.first.line
36
+ # walk the third level, see if there's another indent
37
+ ClassAnalyzer.walk(inner_node) do |innermost_node|
38
+ if innermost_body = get_body_from_node[innermost_node]
39
+ if innermost_node.line != [innermost_body].flatten.first.line
40
+ # there's yet another level of indent -- violation!
41
+ # note: this also catches bad outdents, possibly should
42
+ # flag those separately
43
+ @violations.merge Array(innermost_body).map(&:line)
44
+ end
45
+ end
33
46
  end
34
47
  end
35
48
  end
36
-
37
- Array(get_body_from_node[node]).each(&__iterate)
38
49
  end
39
- node.body.array.each(&_iterate)
40
50
  end
41
51
  end
42
-
43
- Array(klass).each(&iterator)
44
52
  end
45
53
 
46
54
  def get_body_from_node
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if @ivars.length < limit
@@ -27,12 +26,11 @@ module Pelusa
27
26
  end
28
27
 
29
28
  def iterate_lines!(klass)
30
- iterator = Iterator.new do |node|
29
+ ClassAnalyzer.walk(klass) do |node|
31
30
  if node.is_a?(Rubinius::AST::InstanceVariableAccess) || node.is_a?(Rubinius::AST::InstanceVariableAssignment)
32
31
  @ivars << node.name
33
32
  end
34
33
  end
35
- Array(klass).each(&iterator)
36
34
  end
37
35
  end
38
36
  end
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if lines < limit
@@ -31,10 +30,9 @@ module Pelusa
31
30
  end
32
31
 
33
32
  def iterate_lines!(klass)
34
- iterator = Iterator.new do |node|
33
+ ClassAnalyzer.walk(klass) do |node|
35
34
  @lines << node.line if node.respond_to?(:line)
36
35
  end
37
- Array(klass).each(&iterator)
38
36
  end
39
37
  end
40
38
  end
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if @violations.empty?
@@ -27,16 +26,14 @@ module Pelusa
27
26
  end
28
27
 
29
28
  def iterate_lines!(klass)
30
- iterator = Iterator.new do |node|
29
+ ClassAnalyzer.walk(klass) do |node|
31
30
  if node.respond_to?(:name)
32
31
  name = node.name.respond_to?(:name) ? node.name.name.to_s : node.name.to_s
33
32
  if name =~ /[a-z]/ && name.length > limit
34
- next if name =~ /^[A-Z]/ # Ignore constants
35
- @violations << [name, node.line]
33
+ @violations << [name, node.line] unless name =~ /^[A-Z]/ # Ignore constants
36
34
  end
37
35
  end
38
36
  end
39
- Array(klass).each(&iterator)
40
37
  end
41
38
 
42
39
  def formatted_violations
@@ -6,8 +6,8 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
10
+
11
11
  return SuccessfulAnalysis.new(name) if @violations.empty?
12
12
 
13
13
  FailedAnalysis.new(name, formatted_violations) do |violations|
@@ -26,12 +26,11 @@ module Pelusa
26
26
  end
27
27
 
28
28
  def iterate_lines!(klass)
29
- iterator = Iterator.new do |node|
29
+ ClassAnalyzer.walk(klass) do |node|
30
30
  if node.is_a?(Rubinius::AST::Define) && node.arguments.total_args > limit
31
31
  @violations << node.name
32
32
  end
33
33
  end
34
- Array(klass).each(&iterator)
35
34
  end
36
35
 
37
36
  def formatted_violations
@@ -6,7 +6,6 @@ module Pelusa
6
6
  end
7
7
 
8
8
  def check(klass)
9
- initialize
10
9
  iterate_lines!(klass)
11
10
 
12
11
  return SuccessfulAnalysis.new(name) if @violations.empty?
@@ -23,14 +22,13 @@ module Pelusa
23
22
  end
24
23
 
25
24
  def iterate_lines!(klass)
26
- iterator = Iterator.new do |node|
25
+ ClassAnalyzer.walk(klass) do |node|
27
26
  if node.is_a?(Rubinius::AST::Send)
28
27
  if [:attr_accessor, :attr_writer, :attr_reader].include? node.name
29
28
  @violations << node.line
30
29
  end
31
30
  end
32
31
  end
33
- Array(klass).each(&iterator)
34
32
  end
35
33
  end
36
34
  end
@@ -8,7 +8,6 @@ module Pelusa
8
8
  end
9
9
 
10
10
  def check(klass)
11
- initialize
12
11
  iterate_lines!(klass)
13
12
 
14
13
  return SuccessfulAnalysis.new(name) if @violations.empty?
@@ -25,16 +24,14 @@ module Pelusa
25
24
  end
26
25
 
27
26
  def iterate_lines!(klass)
28
- iterator = Iterator.new do |node|
27
+ ClassAnalyzer.walk(klass) do |node|
29
28
  if node.respond_to?(:name)
30
29
  name = node.name.respond_to?(:name) ? node.name.name.to_s : node.name.to_s
31
30
  if name =~ /[a-z]/ && name.length < 3 && !RESERVED_NAMES.include?(name)
32
- next if name =~ /^[A-Z]/ # Ignore constants
33
- @violations << [name, node.line]
31
+ @violations << [name, node.line] unless name =~ /^[A-Z]/ # Ignore constants
34
32
  end
35
33
  end
36
34
  end
37
- Array(klass).each(&iterator)
38
35
  end
39
36
 
40
37
  def formatted_violations
@@ -6,19 +6,15 @@ module Pelusa
6
6
  # Public: Initializes a new Report.
7
7
  #
8
8
  # class_name - The Symbol name of the class being analyzed.
9
+ # type - the String type of the class being analyzed (class or module).
9
10
  # analyses - An Array of Analysis objects.
10
- def initialize(class_name, analyses)
11
- @class_name = class_name
11
+ def initialize(name, type, analyses)
12
+ @class_name = name
13
+ @type = type
12
14
  @analyses = analyses
13
15
  end
14
16
 
15
- def class_name
16
- @class_name
17
- end
18
-
19
- def analyses
20
- @analyses
21
- end
17
+ attr_reader :class_name, :type, :analyses
22
18
 
23
19
  def successful?
24
20
  @analyses.all? { |analysis| analysis.successful? }
@@ -21,7 +21,7 @@ module Pelusa
21
21
  def print_report(class_report)
22
22
  class_name = class_report.class_name
23
23
 
24
- puts " class #{class_name}"
24
+ puts " #{class_report.type} #{class_name}"
25
25
 
26
26
  analyses = class_report.analyses
27
27
  analyses.each do |analysis|
@@ -1,3 +1,3 @@
1
1
  module Pelusa
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -3,30 +3,60 @@ require 'test_helper'
3
3
  module Pelusa
4
4
  describe Analyzer do
5
5
  describe '#analyze' do
6
- before do
7
- @ast = """
8
- class Foo
9
- def bar
10
- 123
6
+ describe 'with a multi-expression AST' do
7
+ before do
8
+ @ast = """
9
+ class Foo
10
+ def bar
11
+ 123
12
+ end
11
13
  end
12
- end
13
14
 
14
- class Bar
15
- def baz
16
- 321
15
+ class Bar
16
+ def baz
17
+ 321
18
+ end
17
19
  end
18
- end
19
- """.to_ast
20
20
 
21
- lints = stub
22
- @analyzer = Analyzer.new([Lint::LineRestriction], RubyReporter, "foo.rb")
21
+ module Baz
22
+ def bar
23
+ 2.7
24
+ end
25
+ end
26
+ """.to_ast
27
+
28
+ lints = stub
29
+ @analyzer = Analyzer.new([Lint::LineRestriction], RubyReporter, "foo.rb")
30
+ end
31
+
32
+ it 'analyzes an ast and returns a report' do
33
+ result = @analyzer.analyze(@ast).report
34
+ result[:filename].must_equal "foo.rb"
35
+ result[:Foo]["Is below 50 lines"][:status].must_equal "successful"
36
+ result[:Bar]["Is below 50 lines"][:status].must_equal "successful"
37
+ result[:Baz]["Is below 50 lines"][:status].must_equal "successful"
38
+ end
23
39
  end
24
40
 
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"
41
+ describe 'with a single-expression AST' do
42
+ before do
43
+ @ast = """
44
+ class Foo
45
+ def bar
46
+ 123
47
+ end
48
+ end
49
+ """.to_ast
50
+
51
+ lints = stub
52
+ @analyzer = Analyzer.new([Lint::LineRestriction], RubyReporter, "foo.rb")
53
+ end
54
+
55
+ it 'analyzes an ast and returns a report' do
56
+ result = @analyzer.analyze(@ast).report
57
+ result[:filename].must_equal "foo.rb"
58
+ result[:Foo]["Is below 50 lines"][:status].must_equal "successful"
59
+ end
30
60
  end
31
61
  end
32
62
  end
@@ -21,6 +21,20 @@ module Pelusa
21
21
  @analyzer.class_name.must_equal "Foo"
22
22
  end
23
23
  end
24
+
25
+ describe "#type" do
26
+ it "returns the type module for modules" do
27
+ # hacky!
28
+ @klass.stubs(:is_a?).with(Rubinius::AST::Class).returns(true)
29
+ @analyzer.type.must_equal "class"
30
+ end
31
+
32
+ it "returns the type module for modules" do
33
+ # hacky!
34
+ @klass.stubs(:is_a?).with(Rubinius::AST::Class).returns(false)
35
+ @analyzer.type.must_equal "module"
36
+ end
37
+ end
24
38
  end
25
39
  end
26
40
  end
@@ -22,6 +22,20 @@ module Pelusa
22
22
  end
23
23
  end
24
24
 
25
+ describe 'when the class has no ivars' do
26
+ it 'returns a SuccessAnalysis' do
27
+ klass = """
28
+ class Foo
29
+ def initialize
30
+ things = []
31
+ end
32
+ end""".to_ast
33
+
34
+ analysis = @lint.check(klass)
35
+ analysis.successful?.must_equal true
36
+ end
37
+ end
38
+
25
39
  describe 'when the class mixes collection ivars with others' do
26
40
  it 'returns a FailureAnalysis' do
27
41
  klass = """
@@ -36,6 +50,37 @@ module Pelusa
36
50
  analysis.failed?.must_equal true
37
51
  end
38
52
  end
53
+
54
+ describe 'when the class has multiple array assignments' do
55
+ it 'returns a FailureAnalysis' do
56
+ klass = """
57
+ class Foo
58
+ def initialize
59
+ @things = []
60
+ @foos = []
61
+ end
62
+ end""".to_ast
63
+
64
+ analysis = @lint.check(klass)
65
+ analysis.failed?.must_equal false
66
+ end
67
+ end
68
+
69
+ describe 'when the class has multiple array and other assignments' do
70
+ it 'returns a FailureAnalysis' do
71
+ klass = """
72
+ class Foo
73
+ def initialize
74
+ @things = []
75
+ @foos = []
76
+ @foo = 'bar'
77
+ end
78
+ end""".to_ast
79
+
80
+ analysis = @lint.check(klass)
81
+ analysis.failed?.must_equal true
82
+ end
83
+ end
39
84
  end
40
85
  end
41
86
  end
@@ -47,36 +47,92 @@ module Pelusa
47
47
  end""".to_ast
48
48
 
49
49
  analysis = @lint.check(klass)
50
- analysis.successful?.must_equal true
50
+ analysis.successful?.must_equal true
51
51
  end
52
52
  end
53
53
 
54
- describe 'when chaining operations on an Enumerable' do
55
- it 'returns a SuccessAnalysis' do
54
+ describe 'when chaining whitelisted operations' do
55
+ it 'returns a SuccessAnalysis for chained operations from Enumerable' do
56
+ klass = """
57
+ class Foo
58
+ def execute
59
+ [1,2,3].map(&:object_id).each {|i| i}
60
+ end
61
+ end""".to_ast
62
+
63
+ analysis = @lint.check(klass)
64
+ analysis.successful?.must_equal true
65
+ end
66
+
67
+ it 'returns a SuccessAnalysis when chaining methods from Fixnum' do
68
+ klass = """
69
+ class Foo
70
+ def execute
71
+ 1 + 2 + 3 + 4
72
+ end
73
+ end""".to_ast
74
+
75
+ analysis = @lint.check(klass)
76
+ analysis.successful?.must_equal true
77
+ end
78
+
79
+ it 'returns a SuccessAnalysis for chained operations from Object' do
80
+ klass = """
81
+ class Foo
82
+ def execute
83
+ Object.new.to_s.inspect
84
+ end
85
+ end""".to_ast
86
+
87
+ analysis = @lint.check(klass)
88
+ analysis.successful?.must_equal true
89
+ end
90
+
91
+ it 'returns a SuccessAnalysis for chained operations from optional sources' do
92
+ Pelusa.configuration.stubs(:[]).with("DemeterLaw").returns(
93
+ {"whitelist" => "Object, Kernel, Hash, Enumerable"}
94
+ )
95
+
56
96
  klass = """
57
97
  class Foo
58
98
  def execute
59
- [1,2,3].map(&:object_id).map(&:object_id)
99
+ {'a' => 2}.merge.each_pair {|k, v|}
60
100
  end
61
101
  end""".to_ast
62
102
 
63
103
  analysis = @lint.check(klass)
64
- analysis.successful?.must_equal true
104
+ analysis.successful?.must_equal true
65
105
  end
66
106
  end
67
- end
68
107
 
69
- describe 'when chaining Fixnum operations' do
70
- it 'returns a SuccessAnalysis' do
71
- klass = """
72
- class Foo
73
- def execute
74
- 1 + 2 + 3 + 4
75
- end
76
- end""".to_ast
77
-
78
- analysis = @lint.check(klass)
79
- analysis.successful?.must_equal true
108
+ describe 'conversions' do
109
+ it 'returns a SuccessAnalysis for conversion operations if allowed' do
110
+ Pelusa.configuration.stubs(:[]).with("DemeterLaw").returns(
111
+ {"allow_conversions" => true}
112
+ )
113
+
114
+ klass = """
115
+ class Foo
116
+ def execute
117
+ {'a' => 2}.merge({}).to_hash.as_json
118
+ end
119
+ end""".to_ast
120
+
121
+ analysis = @lint.check(klass)
122
+ analysis.successful?.must_equal true
123
+ end
124
+
125
+ it 'returns a FailureAnalysis for conversions if not allowed' do
126
+ klass = """
127
+ class Foo
128
+ def execute
129
+ {'a' => 2}.merge({}).to_hash
130
+ end
131
+ end""".to_ast
132
+
133
+ analysis = @lint.check(klass)
134
+ analysis.successful?.must_equal false
135
+ end
80
136
  end
81
137
  end
82
138
  end
@@ -10,8 +10,8 @@ module Pelusa
10
10
  okay = SuccessfulAnalysis.new("Is below 50 lines")
11
11
 
12
12
  @reports = [
13
- Report.new("Foo", [ too_many_lines ]),
14
- Report.new("Bar", [ okay ])
13
+ Report.new("Foo", "class", [ too_many_lines ]),
14
+ Report.new("Bar", "module", [ okay ])
15
15
  ]
16
16
 
17
17
  @reporter = RubyReporter.new('foo.rb')
@@ -1,6 +1,6 @@
1
1
  require 'minitest/autorun'
2
2
  require 'minitest/spec'
3
- require 'mocha'
3
+ require 'mocha/setup'
4
4
 
5
5
  require 'pelusa'
6
6
 
metadata CHANGED
@@ -1,32 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pelusa
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.2.2
4
+ version: 0.2.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Josep M. Bach
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-09-14 00:00:00.000000000 Z
11
+ date: 2013-09-19 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- prerelease: false
16
- type: :development
17
- name: mocha
18
14
  requirement: !ruby/object:Gem::Requirement
19
15
  requirements:
20
16
  - - ! '>='
21
17
  - !ruby/object:Gem::Version
22
- version: '0'
23
- none: false
18
+ version: !binary |-
19
+ MA==
20
+ name: mocha
24
21
  version_requirements: !ruby/object:Gem::Requirement
25
22
  requirements:
26
23
  - - ! '>='
27
24
  - !ruby/object:Gem::Version
28
- version: '0'
29
- none: false
25
+ version: !binary |-
26
+ MA==
27
+ prerelease: false
28
+ type: :development
30
29
  description: Static analysis Lint-type tool to improve your OO Ruby code
31
30
  email:
32
31
  - josep.m.bach@gmail.com
@@ -49,7 +48,6 @@ files:
49
48
  - lib/pelusa/class_analyzer.rb
50
49
  - lib/pelusa/cli.rb
51
50
  - lib/pelusa/configuration.rb
52
- - lib/pelusa/iterator.rb
53
51
  - lib/pelusa/lint.rb
54
52
  - lib/pelusa/lint/case_statements.rb
55
53
  - lib/pelusa/lint/collection_wrappers.rb
@@ -76,7 +74,6 @@ files:
76
74
  - test/pelusa/class_analyzer_test.rb
77
75
  - test/pelusa/cli_test.rb
78
76
  - test/pelusa/configuration_test.rb
79
- - test/pelusa/iterator_test.rb
80
77
  - test/pelusa/lint/case_statements_test.rb
81
78
  - test/pelusa/lint/collection_wrappers_test.rb
82
79
  - test/pelusa/lint/demeter_law_test.rb
@@ -95,6 +92,7 @@ files:
95
92
  - test/test_helper.rb
96
93
  homepage: http://github.com/codegram/pelusa
97
94
  licenses: []
95
+ metadata: {}
98
96
  post_install_message:
99
97
  rdoc_options: []
100
98
  require_paths:
@@ -103,19 +101,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
101
  requirements:
104
102
  - - ! '>='
105
103
  - !ruby/object:Gem::Version
106
- version: '0'
107
- none: false
104
+ version: !binary |-
105
+ MA==
108
106
  required_rubygems_version: !ruby/object:Gem::Requirement
109
107
  requirements:
110
108
  - - ! '>='
111
109
  - !ruby/object:Gem::Version
112
- version: '0'
113
- none: false
110
+ version: !binary |-
111
+ MA==
114
112
  requirements: []
115
113
  rubyforge_project: pelusa
116
- rubygems_version: 1.8.24
114
+ rubygems_version: 2.0.6
117
115
  signing_key:
118
- specification_version: 3
116
+ specification_version: 4
119
117
  summary: Static analysis Lint-type tool to improve your OO Ruby code
120
118
  test_files:
121
119
  - test/pelusa_test.rb
@@ -125,7 +123,6 @@ test_files:
125
123
  - test/pelusa/class_analyzer_test.rb
126
124
  - test/pelusa/cli_test.rb
127
125
  - test/pelusa/configuration_test.rb
128
- - test/pelusa/iterator_test.rb
129
126
  - test/pelusa/runner_test.rb
130
127
  - test/pelusa/lint/case_statements_test.rb
131
128
  - test/pelusa/lint/collection_wrappers_test.rb
@@ -1,39 +0,0 @@
1
- module Pelusa
2
- class Iterator
3
- NodeIterator = lambda do |node, check|
4
- check.call(node)
5
-
6
- if node.respond_to?(:each)
7
- return node.each { |node| NodeIterator.call(node, check) }
8
- end
9
-
10
- ivars = node.instance_variables
11
- children = ivars.map { |ivar| node.instance_variable_get(ivar) }
12
-
13
- return children.each { |node| NodeIterator.call(node, check) }
14
- end
15
-
16
- # Public: Initializes a new Iterator with a particular lint check.
17
- #
18
- # lint - The lint block that yields a node to assert for particular
19
- # conditions in that node.
20
- def initialize(&lint)
21
- @iterator = lambda { |node| NodeIterator.call(node, lint) }
22
- end
23
-
24
- # Public: Calls the iterator with the given arguments.
25
- #
26
- # node - The root node from which to iterate.
27
- def call(node)
28
- @iterator.call(node)
29
- end
30
-
31
- # Public: Returns the iterator. Useful when using symbol to proc
32
- # conversions, such as &iterator.
33
- #
34
- # Returns the Proc iterator.
35
- def to_proc
36
- @iterator
37
- end
38
- end
39
- end
@@ -1,28 +0,0 @@
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