Commit 6e5c5612 authored by Coraline Ada Ehmke's avatar Coraline Ada Ehmke

Finished extension to detect arg clumps

parent 7a7e0602
require 'pry'
module Snuffle
class Cohort
class ArgsClump
include PoroPlus
attr_accessor :element, :neighbors, :line_numbers
def self.from(nodes)
nodes = nodes.non_sends.hashes
clumps = Element::Hash.materialize(nodes.to_a).inject([]) do |cohorts, element|
clump = ArgsClump.new(args: element.args, line_numbers: element.node.line_numbers )
if clump.values.count > 1 && clump.near_neighbors.count > 0
nodes = nodes.method_defs
clumps = Element::MethodDefinition.materialize(nodes.to_a).inject([]) do |clumps, element|
clump = ArgsClump.new(element: element, line_numbers: element.node.line_numbers.first )
if clump.values.count > 1 && clump.near_neighbors.any?
clumps << clump
end
clumps
......@@ -21,15 +22,15 @@ module Snuffle
end
def near_neighbors
@near_neighbors ||= neighbors.select{ |n| (n.args & args).size == args.size }
@near_neighbors ||= neighbors.select{ |n| (n.values & values).size > 0 }
end
def neighbors
@neighbors ||= [element.node.siblings - [self.element.node]].flatten.map{|sibling| Element::Hash.materialize([sibling]).first}
@neighbors ||= [element.node.siblings - [self.element.node]].flatten.map{|sibling| Element::MethodDefinition.materialize([sibling]).first}
end
def args
@args ||= self.element.args
def values
@values ||= self.element.values
end
def neighbor
......
......@@ -14,4 +14,11 @@ class Snuffle::Element::MethodDefinition
node.name
end
def values
return [] unless node.children.objects.any?
args = node.children.objects[1].children.map{|child| child.name}.flatten
args
end
end
......@@ -74,7 +74,7 @@
%div.column
%h3.indented.highlighted
Data Clumps:
- if summary.cohorts.count == 0 && summary.arg_clumps.count == 0
- unless summary.cohorts.any? || summary.arg_clumps.any?
%p.indented
%em None
- else
......@@ -87,10 +87,10 @@
(line
= ":#{cohorts.map(&:line_numbers).join(', :')}"
)
- summary.arg_clumps.group_by{|c| c.values.sort }.each do |values, clumps|
- summary.arg_clumps.group_by{|c| c.values.sort }.each do |args, clumps|
- if clumps.count > 0
%li
= values.map{|c| ".#{c}" }.join(", ")
= args.map{|c| ".#{c}" }.join(", ")
%br
(line
= ":#{clumps.map(&:line_numbers).join(', :')}"
......
......@@ -28,13 +28,13 @@ class Snuffle::LatentObject
end
def self.potential_objects_with_methods(nodes, threshold=DUPLICATE_THRESHOLD)
method_candidates = Snuffle::Element::MethodDefinition.materialize(nodes.methods)
method_candidates = Snuffle::Element::MethodDefinition.materialize(nodes.method_defs)
extract_candidates(method_candidates).select{|k,v| v.count > threshold }
end
def self.extract_candidates(methods)
def self.extract_candidates(method_defs)
stemmer = UEAStemmer.new
methods.map(&:method_name).inject({}) do |words, method_name|
method_defs.map(&:method_name).inject({}) do |words, method_name|
atoms = method_name.split('_') - STOPWORDS
atoms = atoms.map{|atom| stemmer.stem(atom.to_s)}
atoms.each{ |word| words[word] ||= []; words[word] << method_name }
......
......@@ -7,11 +7,10 @@ module Snuffle
attr_accessor :id, :name, :type, :child_ids, :parent_id, :line_numbers, :args
scope :by_id, lambda{|id| where(:id => id)}
scope :by_type, lambda{|type| where(:type => type)}
scope :with_parent, lambda{|parent_id| where(parent_id: parent_id) }
scope :hashes, {type: :hash}
scope :methods, {is_method: true}
scope :method_defs, {is_method: true}
scope :non_sends, {is_send: false}
def self.nil
......
......@@ -19,6 +19,10 @@ module Snuffle
@nodes ||= extract_nodes_from(ast)
end
def arg_clumps
@arg_clumps ||= ArgsClump.from(self.nodes)
end
def cohorts
@cohorts ||= Cohort.from(self.nodes)
end
......@@ -47,6 +51,7 @@ module Snuffle
path_to_file: self.path_to_file,
cohorts: cohorts,
latent_objects: latent_objects,
arg_clumps: arg_clumps,
source: self.source
)
end
......@@ -73,14 +78,6 @@ module Snuffle
@ast ||= Parser::CurrentRuby.parse(source)
end
def extracted_args(ast_node)
begin
ast_node.children[1].children.map{|child| child.children}.flatten
rescue
[]
end
end
def extract_nodes_from(ast_node, nodes=Ephemeral::Collection.new("Snuffle::Node"), parent_id=:root)
if name = name_from(ast_node)
if ast_node.respond_to?(:type)
......@@ -89,8 +86,7 @@ module Snuffle
type: ast_node.type,
parent_id: parent_id,
name: name,
line_numbers: lines.map(&:line_number),
args: extracted_args(ast_node)
line_numbers: lines.map(&:line_number)
)
else
extracted_node = Snuffle::Node.new(
......
......@@ -3,9 +3,10 @@ module Snuffle
class Summary
include PoroPlus
attr_accessor :class_name, :path_to_file, :cohorts, :latent_objects, :source
attr_accessor :arg_clumps
def has_results?
self.cohorts.count != 0 || self.latent_objects.count != 0
self.cohorts.count != 0 || self.latent_objects.count != 0 || self.arg_clumps.count != 0
end
def path_to_results
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment