Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

100% doco

[git-p4: depot-paths = "//src/flay/dev/": change = 8360]
  • Loading branch information...
commit 0ac9ebbfe9b2cda7bcd8c161cdc011c3571bf3bd 1 parent 236c01d
@zenspider zenspider authored
View
110 lib/flay.rb
@@ -7,7 +7,7 @@
require 'timeout'
class File
- RUBY19 = "<3".respond_to? :encoding unless defined? RUBY19
+ RUBY19 = "<3".respond_to? :encoding unless defined? RUBY19 # :nodoc:
class << self
alias :binread :read unless RUBY19
@@ -15,7 +15,10 @@ class << self
end
class Flay
- VERSION = '2.1.0'
+ VERSION = "2.1.0" # :nodoc:
+
+ ##
+ # Returns the default options.
def self.default_options
{
@@ -30,6 +33,9 @@ def self.default_options
}
end
+ ##
+ # Process options in +args+, defaulting to +ARGV+.
+
def self.parse_options args = ARGV
options = self.default_options
@@ -101,6 +107,11 @@ def self.parse_options args = ARGV
options
end
+ ##
+ # 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
@@ -113,6 +124,9 @@ def self.expand_dirs_to_files *dirs
}.flatten
end
+ ##
+ # Loads all flay plugins. Files must be named "flog_*.rb".
+
def self.load_plugins
unless defined? @@plugins then
@@plugins = []
@@ -135,8 +149,13 @@ def self.load_plugins
# ignore
end
+ # :stopdoc:
attr_accessor :mass_threshold, :total, :identical, :masses
attr_reader :hashes, :option
+ # :startdoc:
+
+ ##
+ # Create a new instance of Flay with +option+s.
def initialize option = nil
@option = option || Flay.default_options
@@ -150,6 +169,9 @@ def initialize option = nil
require 'ruby2ruby' if @option[:diff]
end
+ ##
+ # Process any number of files.
+
def process(*files) # TODO: rename from process - should act as SexpProcessor
files.each do |file|
warn "Processing #{file}" if option[:verbose]
@@ -181,6 +203,9 @@ def process(*files) # TODO: rename from process - should act as SexpProcessor
end
end
+ ##
+ # Prune, find identical nodes, and update masses.
+
def analyze
self.prune
@@ -191,6 +216,9 @@ def analyze
update_masses
end
+ ##
+ # Reset total and recalculate the masses for all nodes in +hashes+.
+
def update_masses
self.total = 0
masses.clear
@@ -201,6 +229,12 @@ def update_masses
end
end
+ ##
+ # Parse a ruby +file+ and return the sexp.
+ #
+ # --
+ # TODO: change the system and rename this to parse_rb.
+
def process_rb file
begin
RubyParser.new.process(File.binread(file), file, option[:timeout])
@@ -209,6 +243,9 @@ def process_rb file
end
end
+ ##
+ # Process a sexp +pt+.
+
def process_sexp pt
pt.deep_each do |node|
next unless node.any? { |sub| Sexp === sub }
@@ -220,8 +257,14 @@ def process_sexp pt
end
end
+ # :stopdoc:
MAX_NODE_SIZE = 10 # prevents exponential blowout
MAX_AVG_MASS = 12 # prevents exponential blowout
+ # :startdoc:
+
+ ##
+ # Process "fuzzy" matches for +node+. A fuzzy match is a subset of
+ # +node+ up to +difference+ elements less than the original.
def process_fuzzy node, difference
return unless node.has_code?
@@ -249,6 +292,10 @@ def process_fuzzy node, difference
end
end
+ ##
+ # Prunes nodes that aren't relevant to analysis or are already
+ # covered by another node.
+
def prune
# prune trees that aren't duped at all, or are too small
self.hashes.delete_if { |_,nodes| nodes.size == 1 }
@@ -259,6 +306,10 @@ def prune
prune_conservatively
end
+ ##
+ # Conservative prune. Remove any bucket that is known to contain a
+ # subnode element of a node in another bucket.
+
def prune_conservatively
all_hashes = {}
@@ -273,6 +324,10 @@ def prune_conservatively
self.hashes.delete_if { |h,_| all_hashes[h] }
end
+ ##
+ # Liberal prune. Remove any _element_ from a bucket that is known to
+ # be a subnode of another node. Removed by identity.
+
def prune_liberally
update_masses
@@ -304,6 +359,10 @@ def prune_liberally
self.hashes.delete_if { |k,v| v.size <= 1 }
end
+ ##
+ # Output an n-way diff from +data+. This is only used if --diff is
+ # given.
+
def n_way_diff *data
data.each_with_index do |s, i|
c = (?A.ord + i).chr
@@ -335,6 +394,9 @@ def n_way_diff *data
groups.flatten.join("\n")
end
+ ##
+ # Calculate summary scores on a per-file basis. For --summary.
+
def summary
score = Hash.new 0
@@ -349,6 +411,9 @@ def summary
score
end
+ ##
+ # Output the report. Duh.
+
def report prune = nil
analyze
@@ -415,17 +480,28 @@ def report prune = nil
end
class String
- attr_accessor :group
+ attr_accessor :group # :nodoc:
end
class Sexp
+ ##
+ # Whether or not this sexp is a mutated/modified sexp.
+
attr_accessor :modified
- alias :modified? :modified
+ alias :modified? :modified # Is this sexp modified?
+
+ ##
+ # Calculate the structural hash for this sexp. Cached, so don't
+ # modify the sexp afterwards and expect it to be correct.
def structural_hash
@structural_hash ||= self.structure.hash
end
+ ##
+ # Returns a list of structural hashes for all nodes (and sub-nodes)
+ # of this sexp.
+
def all_structural_subhashes
hashes = []
self.deep_each do |node|
@@ -434,7 +510,7 @@ def all_structural_subhashes
hashes
end
- def initialize_copy o
+ def initialize_copy o # :nodoc:
s = super
s.file = o.file
s.line = o.line
@@ -442,7 +518,7 @@ def initialize_copy o
s
end
- def [] a
+ def [] a # :nodoc:
s = super
if Sexp === s then
s.file = self.file
@@ -452,14 +528,22 @@ def [] a
s
end
- def + o
+ def + o # :nodoc:
self.dup.concat o
end
+ ##
+ # Useful general array method that splits the array from 0..+n+ and
+ # the rest. Returns both sections.
+
def split_at n
return self[0..n], self[n+1..-1]
end
+ ##
+ # Return the index of the last non-code element, or nil if this sexp
+ # is not a code-bearing node.
+
def code_index
{
:block => 0, # s(:block, *code)
@@ -471,7 +555,11 @@ def code_index
}[self.sexp_type]
end
- alias has_code? code_index
+ alias has_code? code_index # Does this sexp have a +*code+ section?
+
+ ##
+ # Split the sexp into front-matter and code-matter, returning both.
+ # See #code_index.
def split_code
index = self.code_index
@@ -479,7 +567,11 @@ def split_code
end
end
-class Array
+class Array # :nodoc:
+
+ ##
+ # Delete anything in +self+ if they are identical to anything in +other+.
+
def delete_eql other
self.delete_if { |o1| other.any? { |o2| o1.equal? o2 } }
end
View
5 lib/flay_erb.rb
@@ -5,6 +5,11 @@
require 'erb'
class Flay
+
+ ##
+ # Process erb and parse the result. Returns the sexp of the parsed
+ # ruby.
+
def process_erb file
erb = File.read file
View
22 lib/flay_task.rb
@@ -1,9 +1,28 @@
class FlayTask < Rake::TaskLib
+ ##
+ # The name of the task. Defaults to :flay
+
attr_accessor :name
+
+ ##
+ # What directories to operate on. Sensible defaults.
+
attr_accessor :dirs
+
+ ##
+ # Threshold to fail the task at. Default 200.
+
attr_accessor :threshold
+
+ ##
+ # Verbosity of output. Defaults to rake's trace (-t) option.
+
attr_accessor :verbose
+ ##
+ # Creates a new FlayTask instance with given +name+, +threshold+,
+ # and +dirs+.
+
def initialize name = :flay, threshold = 200, dirs = nil
@name = name
@dirs = dirs || %w(app bin lib spec test)
@@ -17,6 +36,9 @@ def initialize name = :flay, threshold = 200, dirs = nil
define
end
+ ##
+ # Defines the flay task.
+
def define
desc "Analyze for code duplication in: #{dirs.join(', ')}"
task name do
View
2  lib/gauntlet_flay.rb
@@ -10,6 +10,7 @@
require 'gauntlet'
require 'pp'
+# :stopdoc:
class FlayGauntlet < Gauntlet
$owners = {}
$score_file = 'flay-scores.yml'
@@ -98,3 +99,4 @@ def score_for dir
flayer = FlayGauntlet.new
flayer.run_the_gauntlet filter
flayer.display_report max
+# :startdoc:
Please sign in to comment.
Something went wrong with that request. Please try again.