lib/flay.rb in flay-2.5.0 vs lib/flay.rb in flay-2.6.0

- old
+ new

@@ -1,23 +1,23 @@ #!/usr/bin/env ruby -w -require 'optparse' -require 'rubygems' -require 'sexp_processor' -require 'ruby_parser' -require 'timeout' +require "optparse" +require "rubygems" +require "sexp_processor" +require "ruby_parser" +require "timeout" class File RUBY19 = "<3".respond_to? :encoding unless defined? RUBY19 # :nodoc: class << self alias :binread :read unless RUBY19 end end class Flay - VERSION = "2.5.0" # :nodoc: + VERSION = "2.6.0" # :nodoc: class Item < Struct.new(:structural_hash, :name, :bonus, :mass, :locations) alias identical? bonus end @@ -47,65 +47,65 @@ def self.parse_options args = ARGV options = self.default_options OptionParser.new do |opts| - opts.banner = 'flay [options] files_or_dirs' + 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 + opts.on("-h", "--help", "Display this help.") do puts opts exit end - opts.on('-f', '--fuzzy [DIFF]', Integer, + opts.on("-f", "--fuzzy [DIFF]", Integer, "Detect fuzzy (copy & paste) duplication (default 1).") do |n| options[:fuzzy] = n || 1 end - opts.on('-l', '--liberal', "Use a more liberal detection method.") do + opts.on("-l", "--liberal", "Use a more liberal detection method.") do options[:liberal] = true end - opts.on('-m', '--mass MASS', Integer, + opts.on("-m", "--mass MASS", Integer, "Sets mass threshold (default = #{options[:mass]})") do |m| options[:mass] = m.to_i end - opts.on('-#', "Don't number output (helps with diffs)") do |m| + opts.on("-#", "Don't number output (helps with diffs)") do |m| options[:number] = false end - opts.on('-v', '--verbose', "Verbose. Show progress processing files.") do + opts.on("-v", "--verbose", "Verbose. Show progress processing files.") do options[:verbose] = true end - opts.on('-o', '--only NODE', String, "Only show matches on NODE type.") do |s| + opts.on("-o", "--only NODE", String, "Only show matches on NODE type.") do |s| options[:only] = s.to_sym end - opts.on('-d', '--diff', "Diff Mode. Display N-Way diff for ruby.") do + opts.on("-d", "--diff", "Diff Mode. Display N-Way diff for ruby.") do options[:diff] = true end - opts.on('-s', '--summary', "Summarize. Show flay score per file only.") do + opts.on("-s", "--summary", "Summarize. Show flay score per file only.") do options[:summary] = true end - opts.on('-t', '--timeout TIME', Integer, + opts.on("-t", "--timeout TIME", Integer, "Set the timeout. (default = #{options[:timeout]})") do |t| options[:timeout] = t.to_i end - extensions = ['rb'] + Flay.load_plugins + extensions = ["rb"] + Flay.load_plugins opts.separator "" - opts.separator "Known extensions: #{extensions.join(', ')}" + opts.separator "Known extensions: #{extensions.join(", ")}" extensions.each do |meth| msg = "options_#{meth}" send msg, opts, options if self.respond_to?(msg) end @@ -124,32 +124,68 @@ # Expands +*dirs+ to all files within that match ruby and rake extensions. # -- # REFACTOR: from flog def self.expand_dirs_to_files *dirs - extensions = ['rb'] + Flay.load_plugins + extensions = ["rb"] + Flay.load_plugins dirs.flatten.map { |p| if File.directory? p then - Dir[File.join(p, '**', "*.{#{extensions.join(',')}}")] + Dir[File.join(p, "**", "*.{#{extensions.join(",")}}")] else p end - }.flatten + }.flatten.map { |s| s[/^(\.\/)?/] = ""; s } # strip "./" from paths end + # so I can move this to flog wholesale + DEFAULT_IGNORE = ".flayignore" # :nodoc: + ## + # A file filter mechanism similar to, but not as extensive as, + # .gitignore files: + # + # + If a pattern does not contain a slash, it is treated as a shell glob. + # + If a pattern ends in a slash, it matches on directories (and contents). + # + Otherwise, it matches on relative paths. + # + # File.fnmatch is used throughout, so glob patterns work for all 3 types. + + def self.filter_files files, ignore = DEFAULT_IGNORE + ignore_paths = if ignore.respond_to? :read then + ignore.read + elsif File.exists? ignore then + File.read ignore + end + + if ignore_paths then + nonglobs, globs = ignore_paths.split("\n").partition { |p| p.include? "/" } + dirs, ifiles = nonglobs.partition { |p| p.end_with? "/" } + dirs = dirs.map { |s| s.chomp "/" } + + only_paths = File::FNM_PATHNAME + files = files.reject { |f| + dirs.any? { |i| File.fnmatch?(i, File.dirname(f), only_paths) } || + globs.any? { |i| File.fnmatch?(i, f) } || + ifiles.any? { |i| File.fnmatch?(i, f, only_paths) } + } + end + + files + end + + ## # Loads all flay plugins. Files must be named "flog_*.rb". def self.load_plugins unless defined? @@plugins then @@plugins = [] plugins = Gem.find_files("flay_*.rb").reject { |p| p =~ /flay_task/ } plugins.each do |plugin| - plugin_name = File.basename(plugin, '.rb').sub(/^flay_/, '') + plugin_name = File.basename(plugin, ".rb").sub(/^flay_/, "") next if @@plugins.include? plugin_name begin load plugin @@plugins << plugin_name rescue LoadError => e @@ -185,11 +221,11 @@ def process(*files) # TODO: rename from process - should act as SexpProcessor files.each do |file| warn "Processing #{file}" if option[:verbose] - ext = File.extname(file).sub(/^\./, '') + ext = File.extname(file).sub(/^\./, "") ext = "rb" if ext.nil? || ext.empty? msg = "process_#{ext}" unless respond_to? msg then warn " Unknown file type: #{ext}, defaulting to ruby" @@ -316,11 +352,11 @@ new_node = tmpl + subcode next unless new_node.any? { |sub| Sexp === sub } next if new_node.mass < self.mass_threshold - # they're already structurally similar, don't bother adding another + # they're already structurally similar, don"t bother adding another next if self.hashes[new_node.structural_hash].any? { |sub| sub.file == new_node.file and sub.line == new_node.line } self.hashes[new_node.structural_hash] << new_node @@ -501,23 +537,23 @@ io.puts nodes = hashes[item.structural_hash] sources = nodes.map do |s| - msg = "sexp_to_#{File.extname(s.file).sub(/./, '')}" + msg = "sexp_to_#{File.extname(s.file).sub(/./, "")}" self.respond_to?(msg) ? self.send(msg, s) : sexp_to_rb(s) end io.puts n_way_diff(*sources) end end end def sexp_to_rb sexp begin - require 'ruby2ruby' + require "ruby2ruby" rescue LoadError - return 'ruby2ruby is required for diff' + return "ruby2ruby is required for diff" end @r2r ||= Ruby2Ruby.new @r2r.process sexp.deep_clone end end