class Cane::AbcCheck::RubyAst
Wrapper object around sexps returned from ripper.
Constants
- METH_CHARS
Attributes
anon_method_add[RW]
Stateful flag used to determine whether we are currently parsing an anonymous class. See container_label.
Public Class Methods
new(*args)
click to toggle source
Calls superclass method
# File lib/cane/abc_check.rb, line 67 def initialize(*args) super self.anon_method_add = true end
Public Instance Methods
violations()
click to toggle source
# File lib/cane/abc_check.rb, line 72 def violations process_ast(sexps). select {|nesting, complexity| complexity > max_allowed_complexity }. map {|x| { file: file_name, label: x.first, value: x.last, description: "Methods exceeded maximum allowed ABC complexity" }} end
Protected Instance Methods
assignment_nodes()
click to toggle source
# File lib/cane/abc_check.rb, line 149 def assignment_nodes [:assign, :opassign] end
branch_nodes()
click to toggle source
# File lib/cane/abc_check.rb, line 161 def branch_nodes [:call, :fcall, :brace_block, :do_block] end
calculate_abc(method_node)
click to toggle source
# File lib/cane/abc_check.rb, line 108 def calculate_abc(method_node) a = count_nodes(method_node, assignment_nodes) b = count_nodes(method_node, branch_nodes) + 1 c = count_nodes(method_node, condition_nodes) abc = Math.sqrt(a**2 + b**2 + c**2).round abc end
condition_nodes()
click to toggle source
# File lib/cane/abc_check.rb, line 165 def condition_nodes [:==, :===, :"<>", :"<=", :">=", :"=~", :>, :<, :else, :"<=>"] end
container_label(node)
click to toggle source
# File lib/cane/abc_check.rb, line 116 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
container_nodes()
click to toggle source
# File lib/cane/abc_check.rb, line 157 def container_nodes [:class, :module] end
count_nodes(node, types)
click to toggle source
# File lib/cane/abc_check.rb, line 145 def count_nodes(node, types) node.flatten.select {|n| types.include?(n) }.length end
excluded?(method_description)
click to toggle source
# File lib/cane/abc_check.rb, line 171 def excluded?(method_description) exclusions.include?(method_description) end
label_for(node)
click to toggle source
# File lib/cane/abc_check.rb, line 137 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. node.detect {|x| [:@ident, :@op, :@kw, :@const, :@backtick].include?(x[0]) }[1] end
method_description(node, *modules, meth_name)
click to toggle source
# File lib/cane/abc_check.rb, line 175 def method_description(node, *modules, meth_name) separator = METH_CHARS.fetch(node.first) description = [modules.join('::'), meth_name].join(separator) end
method_nodes()
click to toggle source
# File lib/cane/abc_check.rb, line 153 def method_nodes [:def, :defs] end
process_ast(node, complexity = {}, nesting = [])
click to toggle source
Recursive function to process an AST. The `complexity` variable mutates, which is a bit confusing. `nesting` does not.
# File lib/cane/abc_check.rb, line 91 def process_ast(node, complexity = {}, nesting = []) if method_nodes.include?(node[0]) nesting = nesting + [label_for(node)] desc = method_description(node, *nesting) unless excluded?(desc) complexity[desc] = calculate_abc(node) end elsif parent = container_label(node) nesting = nesting + [parent] end if node.is_a? Array node[1..-1].each {|n| process_ast(n, complexity, nesting) if n } end complexity end