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