pelusa 0.2.2 → 0.2.3

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