Permalink
Browse files

Handle anonymous class definitions correctly in ABC output.

Fixes #20.
  • Loading branch information...
1 parent f6bab2c commit 1c3c2e67429d3052a3c4da460e60bb144872346f @xaviershay xaviershay committed Aug 18, 2012
Showing with 60 additions and 2 deletions.
  1. +32 −2 lib/cane/abc_check.rb
  2. +28 −0 spec/abc_check_spec.rb
View
@@ -38,6 +38,12 @@ def violations
# Wrapper object around sexps returned from ripper.
class RubyAst < Struct.new(:file_name, :max_allowed_complexity,
:sexps, :exclusions)
+
+ def initialize(*args)
+ super
+ self.anon_method_add = true
+ end
+
def violations
process_ast(sexps).
select { |nesting, complexity| complexity > max_allowed_complexity }.
@@ -46,6 +52,10 @@ def violations
protected
+ # Stateful flag used to determine whether we are currently parsing an
+ # anonymous class. See #container_label.
+ attr_accessor :anon_method_add
+
# Recursive function to process an AST. The `complexity` variable mutates,
# which is a bit confusing. `nesting` does not.
def process_ast(node, complexity = {}, nesting = [])
@@ -54,8 +64,7 @@ def process_ast(node, complexity = {}, nesting = [])
unless excluded?(node, *nesting)
complexity[nesting.join(" > ")] = calculate_abc(node)
end
- elsif container_nodes.include?(node[0])
- parent = node[1][-1][1]
+ elsif parent = container_label(node)
nesting = nesting + [parent]
end
@@ -73,6 +82,27 @@ def calculate_abc(method_node)
abc
end
+ def container_label(node)
+ if container_nodes.include?(node[0])
+ # def foo, def self.foo
+ node[1][-1][1]
+ elsif node[0] == :method_add_block
+ if anon_method_add
+ # Class.new do ...
+ "(anon)"
+ else
+ # MyClass = Class.new do ...
+ # parent already added when processing a parent node
+ anon_method_add = true
+ nil
+ end
+ elsif node[0] == :assign && node[2][0] == :method_add_block
+ # MyClass = Class.new do ...
+ self.anon_method_add = false
+ node[1][-1][1]
+ end
+ end
+
def label_for(node)
# A default case is deliberately omitted since I know of no way this
# could fail and want it to fail fast.
View
@@ -92,6 +92,34 @@ def other_meth
columns.should == [file_name, "Harness > Nested > other_meth", 1]
end
+ it "creates an AbcMaxViolation for method in assigned anonymous class" do
+ file_name = make_file(<<-RUBY)
+ MyClass = Struct.new(:foo) do
+ def test_method(a)
+ b = a
+ return b if b > 3
+ end
+ end
+ RUBY
+
+ violations = described_class.new(files: file_name, max: 1).violations
+ violations[0].detail.should == "MyClass > test_method"
+ end
+
+ it "creates an AbcMaxViolation for method in anonymous class" do
+ file_name = make_file(<<-RUBY)
+ Class.new do
+ def test_method(a)
+ b = a
+ return b if b > 3
+ end
+ end
+ RUBY
+
+ violations = described_class.new(files: file_name, max: 1).violations
+ violations[0].detail.should == "(anon) > test_method"
+ end
+
def self.it_should_extract_method_name(method_name, label=method_name)
it "creates an AbcMaxViolation for #{method_name}" do
file_name = make_file(<<-RUBY)

0 comments on commit 1c3c2e6

Please sign in to comment.