Commit 7f4547c5 authored by Coraline Ehmke's avatar Coraline Ehmke

Iteration on detectors

parent e62a2791
--color
\ No newline at end of file
--color
--format documentation
\ No newline at end of file
......@@ -4,7 +4,8 @@ require 'poro_plus'
require "snuffle/version"
require "snuffle/node"
require "snuffle/detectors/data_clump"
require "snuffle/parser"
require "snuffle/elements/hashes"
module Snuffle
end
require 'parser/current'
require 'rubytree'
require 'pry'
module Snuffle
......@@ -12,30 +11,40 @@ module Snuffle
self.content = content || File.read("./spec/fixtures/program_1.rb")
end
def parsed_content
@parsed_content ||= Parser::CurrentRuby.parse(self.content)
def ast
@ast ||= Parser::CurrentRuby.parse(self.content)
end
# def build_tree
# tree = Tree::TreeNode.new("ROOT", "Root Content")
# extract_nodes(parsed_content).each do |pair|
# tree << Tree::TreeNode.new(pair)
# end
# tree
# end
def extract_nodes(node, nodes=[])
if node.respond_to?(:type)
nodes << node.type
end
if node.respond_to?(:children)
nodes << node.children.map{|child| extract_nodes(child, nodes)}.flatten
def nodes
@nodes ||= extract_nodes_from(ast)
end
def extract_nodes_from(ast_node, nodes=Ephemeral::Collection.new("Node"), parent_id=:root)
if ast_node.respond_to?(:type)
extracted_node = Node.new(
type: ast_node.type,
parent_id: parent_id,
name: name_from(ast_node)
)
else
# extracted_node = Node.nil
extracted_node = Node.new(
type: :nil,
parent_id: parent_id,
name: name_from(ast_node)
)
end
nodes << extracted_node
ast_node.children.each{|child| extract_nodes_from(child, nodes, extracted_node.id)} if ast_node.respond_to?(:children)
nodes
end
def hashes
extract_nodes(parsed_content).select{|node| node.respond_to?(:type) && node.type == :hash}
def name_from(node)
return "unknown" if node.nil?
return node unless node.respond_to?(:children)
return node.children.last unless node.respond_to?(:loc) && node.loc.respond_to?(:name)
name = node.loc.name
self.content[name.begin_pos, name.end_pos - 1]
end
end
......
module Snuffle
module Element
class Hash
attr_accessor :node
def self.materialize(nodes=[])
node.each.map{|hash_node| new(hash_node) }
end
def initialize
self.node = node
end
def pairs
@pairs ||= values.zip(keys).inject({}){|hash, pair| hash[pair[0]] = pair[1]; hash}
end
def keys
node.children.map{ |child| child.first.name }
end
def values
node.children.map{ |child| child.last.name }
end
end
end
end
\ No newline at end of file
# d.nodes.where(type: :sym).select{|n| n.grandparent.type == :hash}.group_by{|n| n.grandparent.id}.values.map{|set| set.map(&:name)}
class Node
include Ephemeral::Base
include PoroPlus
attr_accessor :id, :name, :type, :children, :parent
attr_accessor :id, :name, :type, :child_ids, :parent_id
scope :by_id, lambda{|id| where(:id => id)}
scope :by_type, lambda{|type| where(:type => type) }
scope :hashes, {type: :hash}
def self.nil
new(type: :nil)
end
def initialize(*args, &block)
@id = id
super
end
def id
@id ||= SecureRandom.uuid
end
end
def parent
Node.where(id: self.parent_id).first
end
def descendents(nodes=[])
nodes << self.children.map{|desc| descendents(desc, nodes)}
nodes.flatten
end
def siblings
Node.where(parent_id: self.parent_id)
end
def children
Node.where(parent_id: self.id)
end
end
module Snuffle
class Parser
attr_accessor :content
def initialize(content=nil)
self.content = content || File.read("./spec/fixtures/program_1.rb")
end
def ast
@ast ||= ::Parser::CurrentRuby.parse(self.content)
end
def nodes
@nodes ||= extract_nodes_from(ast)
end
def extract_nodes_from(ast_node, nodes=Ephemeral::Collection.new("Node"), parent_id=:root)
if ast_node.respond_to?(:type)
extracted_node = Node.new(
type: ast_node.type,
parent_id: parent_id,
name: name_from(ast_node)
)
else
extracted_node = Node.new(
type: :nil,
parent_id: parent_id,
name: name_from(ast_node)
)
end
nodes << extracted_node
ast_node.children.each{|child| extract_nodes_from(child, nodes, extracted_node.id)} if ast_node.respond_to?(:children)
nodes
end
def name_from(node)
return "unknown" if node.nil?
return node unless node.respond_to?(:children)
return node.children.last unless node.respond_to?(:loc) && node.loc.respond_to?(:name)
name = node.loc.name
self.content[name.begin_pos, name.end_pos - 1]
end
def hashes
Element::Hash.materialize(nodes.where(type: :hash).to_a)
end
end
end
\ No newline at end of file
......@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
spec.version = Snuffle::VERSION
spec.authors = ["Coraline Ada Ehmke", "Kerri Miller"]
spec.email = ["coraline@idolhands.com"]
spec.summary = %q{Snuffle detects latent objects in your Ruby code.}
spec.description = %q{Snuffle detects latent objects in your Ruby code.}
spec.summary = %q{Snuffle detects data clumps in your Ruby code.}
spec.description = %q{Snuffle detects data clumbs and other hints of extractable objects in your Ruby code.}
spec.homepage = ""
spec.license = "MIT"
......@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "parser"
spec.add_dependency "thor"
spec.add_dependency "ephemeral"
spec.add_dependency "ephemeral", "~> 2.3.2"
spec.add_dependency "poro_plus"
spec.add_development_dependency "bundler", "~> 1.6"
......
......@@ -10,11 +10,11 @@ class Song
end
def can_be_playlisted?
options[:available_to_playlist]
metadata[:playlist]
end
def can_be_downloaded?
options.fetch[:downloadable]
metadata[:download]
end
def drm_enabled?
......@@ -23,8 +23,8 @@ class Song
def metadata
{
:playlist => can_be_playlisted?,
:download => can_be_downloaded
:playlist => options[:available_to_playlist],
:download => options[:downloadable]
}
end
......
class Customer
attr_accessor :customer_id, :customer_name, :company_name
attr_accessor :street_address_1, :street_address_2
attr_accessor :city, :state, :postal_code
def address_is_residence?
self.company_name.nil?
end
def get_coords_for_address
fake_api_call(city: city, state: state, postal_code: postal_code)
end
def neighborhood
fake_neighborhood_api_call(city: self.city, state: self.state, postal_code: self.postal_code)
end
def fake_neighborhood_api_call(args={})
"Probably River North"
end
def fake_api_call(args={})
[112.32, 124.11]
end
def address
string = ""
string << self.customer_name
string << self.company_name if address_is_residence?
string << self.street_address_1
string << self.street_address_2 if street_address_2.present?
string << "#{self.city}, #{self.state} #{self.postal_code}"
string.join", "
end
end
# d.nodes.where(type: :sym).select{|n| n.grandparent.type == :hash}
\ No newline at end of file
......@@ -3,21 +3,35 @@ require 'pry'
describe Snuffle::Detectors::DataClump do
let(:code) { File.read("spec/fixtures/program_1.rb") }
let(:code) { File.read("spec/fixtures/program_2.rb") }
let(:detector) { Snuffle::Detectors::DataClump.new(code) }
describe "#hashes" do
it "finds all hashes" do
hashes = detector.hashes
p hashes
expect(hashes.count).to eq(2)
describe "weighting" do
# attributes are used together as values in a hash
# E.g. [city, state]
context "a hash cohort" do
it "is detected" do
expect(detector.hash_cohorts).to eq([:city, :state, :postal_code])
end
it "is assigned a weight of X"
end
end
describe "#hash_keys" do
it "finds all hash keys" do
# attributes accessed together in one or more methods
# E.g. [city, state], [city, state, postal_code]
context "method cohorts" do
it "is detected"
it "is assigned a weight of X"
end
# attributes used together in string interpolation
# E.g. [city, city, postal_code]
context "string cohorts" do
it "is detected"
it "is assigned a weight of X"
end
end
......
require 'snuffle'
def reload!
load "lib/snuffle/node.rb"
load "lib/snuffle/detectors/data_clump.rb"
end
def file
File.open("spec/fixtures/program_2.rb", "r").read
end
def d
@d ||= Snuffle::Parser.new(file)
end
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