lib/flay.rb in flay-1.1.0 vs lib/flay.rb in flay-1.2.0

- old
+ new

@@ -1,48 +1,148 @@ #!/usr/bin/env ruby -w $: << "../../ruby_parser/dev/lib" +$: << "../../ruby2ruby/dev/lib" +require 'optparse' require 'rubygems' require 'sexp_processor' require 'ruby_parser' -require 'pp' # TODO: remove -$m ||= 16 -$v ||= false -$f ||= false +abort "update rubygems to >= 1.3.1" unless Gem.respond_to? :find_files -if $v then - $: << "../../ruby2ruby/dev/lib" - require 'ruby2ruby' - require 'tempfile' -end - class Flay - VERSION = '1.1.0' + VERSION = '1.2.0' - attr_accessor :mass_threshold - attr_reader :hashes + def self.default_options + { + :fuzzy => false, + :verbose => false, + :mass => 16, + } + end - def initialize(mass = 16) + def self.parse_options + options = self.default_options + + OptionParser.new do |opts| + opts.banner = 'flay [options] files_or_dirs' + opts.version = Flay::VERSION + + opts.separator "" + opts.separator "Specific options:" + opts.separator "" + + opts.on('-h', '--help', 'Display this help.') do + puts opts + exit + end + + opts.on('-f', '--fuzzy', "Attempt to do fuzzy similarities. (SLOW)") do + options[:fuzzy] = true + end + + opts.on('-m', '--mass MASS', Integer, "Sets mass threshold") do |m| + options[:mass] = m.to_i + end + + opts.on('-v', '--verbose', "Verbose. Display N-Way diff for ruby.") do + options[:verbose] = true + end + + extensions = ['rb'] + Flay.load_plugins + + opts.separator "" + opts.separator "Known extensions: #{extensions.join(', ')}" + end.parse! + + options + end + + def self.expand_dirs_to_files *dirs + extensions = ['rb'] + Flay.load_plugins + + dirs.flatten.map { |p| + if File.directory? p then + Dir[File.join(p, '**', "*.{#{extensions.join(',')}}")] + else + p + end + }.flatten + end + + def self.load_plugins + plugins = Gem.find_files("flay_*.rb").reject { |path| path =~ /flay_task/ } + + plugins.each do |plugin| + begin + load plugin + rescue LoadError => e + warn "error loading #{plugin.inspect}: #{e.message}. skipping..." + end + end + + plugins.map { |f| File.basename(f, '.rb').sub(/^flay_/, '') } + end + + attr_accessor :mass_threshold, :total, :identical, :masses + attr_reader :hashes, :option + + def initialize option = nil + @option = option || Flay.default_options @hashes = Hash.new { |h,k| h[k] = [] } - @mass_threshold = mass + + self.identical = {} + self.masses = {} + self.total = 0 + self.mass_threshold = @option[:mass] + + require 'ruby2ruby' if @option[:verbose] end def process(*files) files.each do |file| warn "Processing #{file}" - pt = RubyParser.new.process(File.read(file), file) - next unless pt # empty files... hahaha, suck. + ext = File.extname(file).sub(/^\./, '') + ext = "rb" if ext.nil? || ext.empty? + msg = "process_#{ext}" - process_sexp pt + unless respond_to? msg then + warn " Unknown file type: #{ext}, defaulting to ruby" + msg = "process_rb" + end + + sexp = begin + send msg, file + rescue => e + warn " #{e.message.strip}" + warn " skipping #{file}" + nil + end + + next unless sexp + + process_sexp sexp end - process_fuzzy_similarities if $f + process_fuzzy_similarities if option[:fuzzy] + + self.prune + + self.hashes.each do |hash,nodes| + identical[hash] = nodes[1..-1].all? { |n| n == nodes.first } + masses[hash] = nodes.first.mass * nodes.size + masses[hash] *= (nodes.size) if identical[hash] + self.total += masses[hash] + end end + def process_rb file + RubyParser.new.process(File.read(file), file) + end + def process_sexp pt pt.deep_each do |node| next unless node.any? { |sub| Sexp === sub } next if node.mass < self.mass_threshold @@ -134,23 +234,15 @@ } groups.flatten.join("\n") end def report prune = nil - self.prune + puts "Total score (lower is better) = #{self.total}" + puts - identical = {} - masses = {} - - self.hashes.each do |hash,nodes| - identical[hash] = nodes[1..-1].all? { |n| n == nodes.first } - masses[hash] = nodes.first.mass * nodes.size - masses[hash] *= (nodes.size) if identical[hash] - end - count = 0 - masses.sort_by { |h,m| [-m, hashes[h].first.file] }.each do |hash,mass| + masses.sort_by { |h,m| [-m, hashes[h].first.file] }.each do |hash, mass| nodes = hashes[hash] next unless nodes.first.first == prune if prune puts same = identical[hash] @@ -165,18 +257,18 @@ count += 1 puts "%d) %s code found in %p (mass%s = %d)" % [count, match, node.first, bonus, mass] nodes.each_with_index do |node, i| - if $v then + if option[:verbose] then c = (?A + i).chr puts " #{c}: #{node.file}:#{node.line}" else puts " #{node.file}:#{node.line}" end end - if $v then + if option[:verbose] then puts r2r = Ruby2Ruby.new puts n_way_diff(*nodes.map { |s| r2r.process(s.deep_clone) }) end end