2008-01-13 22:34:05 -05:00
|
|
|
##
|
2010-12-19 22:22:49 -05:00
|
|
|
# RDoc statistics collector which prints a summary and report of a project's
|
|
|
|
# documentation totals.
|
2008-01-13 22:34:05 -05:00
|
|
|
|
|
|
|
class RDoc::Stats
|
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
##
|
|
|
|
# Output level for the coverage report
|
|
|
|
|
|
|
|
attr_reader :coverage_level
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Count of files parsed during parsing
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
attr_reader :files_so_far
|
2008-07-21 14:35:14 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Total number of files found
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
attr_reader :num_files
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Creates a new Stats that will have +num_files+. +verbosity+ defaults to 1
|
|
|
|
# which will create an RDoc::Stats::Normal outputter.
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2012-11-26 23:28:14 -05:00
|
|
|
def initialize store, num_files, verbosity = 1
|
2010-12-19 22:22:49 -05:00
|
|
|
@num_files = num_files
|
2012-11-26 23:28:14 -05:00
|
|
|
@store = store
|
2011-02-01 19:32:30 -05:00
|
|
|
|
2012-11-26 23:28:14 -05:00
|
|
|
@coverage_level = 0
|
|
|
|
@doc_items = nil
|
|
|
|
@files_so_far = 0
|
2011-05-13 20:39:16 -04:00
|
|
|
@fully_documented = false
|
2012-11-26 23:28:14 -05:00
|
|
|
@num_params = 0
|
|
|
|
@percent_doc = nil
|
|
|
|
@start = Time.now
|
|
|
|
@undoc_params = 0
|
2008-07-21 14:35:14 -04:00
|
|
|
|
|
|
|
@display = case verbosity
|
2010-12-19 22:22:49 -05:00
|
|
|
when 0 then Quiet.new num_files
|
|
|
|
when 1 then Normal.new num_files
|
|
|
|
else Verbose.new num_files
|
2008-07-21 14:35:14 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Records the parsing of an alias +as+.
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
def add_alias as
|
2008-07-21 14:35:14 -04:00
|
|
|
@display.print_alias as
|
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Records the parsing of an attribute +attribute+
|
|
|
|
|
|
|
|
def add_attribute attribute
|
|
|
|
@display.print_attribute attribute
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Records the parsing of a class +klass+
|
|
|
|
|
|
|
|
def add_class klass
|
2008-07-21 14:35:14 -04:00
|
|
|
@display.print_class klass
|
2010-04-01 03:45:16 -04:00
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Records the parsing of +constant+
|
|
|
|
|
|
|
|
def add_constant constant
|
2010-04-01 03:45:16 -04:00
|
|
|
@display.print_constant constant
|
2008-07-21 14:35:14 -04:00
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Records the parsing of +file+
|
|
|
|
|
2008-07-21 14:35:14 -04:00
|
|
|
def add_file(file)
|
2010-12-19 22:22:49 -05:00
|
|
|
@files_so_far += 1
|
|
|
|
@display.print_file @files_so_far, file
|
2008-07-21 14:35:14 -04:00
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Records the parsing of +method+
|
|
|
|
|
2008-07-21 14:35:14 -04:00
|
|
|
def add_method(method)
|
|
|
|
@display.print_method method
|
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Records the parsing of a module +mod+
|
|
|
|
|
2008-07-21 14:35:14 -04:00
|
|
|
def add_module(mod)
|
|
|
|
@display.print_module mod
|
2010-04-01 03:45:16 -04:00
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Call this to mark the beginning of parsing for display purposes
|
2008-01-13 22:34:05 -05:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
def begin_adding
|
|
|
|
@display.begin_adding
|
2008-07-21 14:35:14 -04:00
|
|
|
end
|
|
|
|
|
2010-04-01 03:45:16 -04:00
|
|
|
##
|
2011-02-01 19:32:30 -05:00
|
|
|
# Calculates documentation totals and percentages for classes, modules,
|
|
|
|
# constants, attributes and methods.
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
def calculate
|
2011-02-01 19:32:30 -05:00
|
|
|
return if @doc_items
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2012-11-26 23:28:14 -05:00
|
|
|
ucm = @store.unique_classes_and_modules
|
|
|
|
|
|
|
|
classes = @store.unique_classes.reject { |cm| cm.full_name == 'Object' }
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
constants = []
|
|
|
|
ucm.each { |cm| constants.concat cm.constants }
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
methods = []
|
|
|
|
ucm.each { |cm| methods.concat cm.method_list }
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
attributes = []
|
|
|
|
ucm.each { |cm| attributes.concat cm.attributes }
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
@num_attributes, @undoc_attributes = doc_stats attributes
|
2012-11-26 23:28:14 -05:00
|
|
|
@num_classes, @undoc_classes = doc_stats classes
|
2010-12-19 22:22:49 -05:00
|
|
|
@num_constants, @undoc_constants = doc_stats constants
|
|
|
|
@num_methods, @undoc_methods = doc_stats methods
|
2012-11-26 23:28:14 -05:00
|
|
|
@num_modules, @undoc_modules = doc_stats @store.unique_modules
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
@num_items =
|
|
|
|
@num_attributes +
|
|
|
|
@num_classes +
|
|
|
|
@num_constants +
|
|
|
|
@num_methods +
|
2011-02-01 19:32:30 -05:00
|
|
|
@num_modules +
|
|
|
|
@num_params
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
@undoc_items =
|
|
|
|
@undoc_attributes +
|
|
|
|
@undoc_classes +
|
|
|
|
@undoc_constants +
|
|
|
|
@undoc_methods +
|
2011-02-01 19:32:30 -05:00
|
|
|
@undoc_modules +
|
|
|
|
@undoc_params
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
@doc_items = @num_items - @undoc_items
|
2011-02-01 19:32:30 -05:00
|
|
|
end
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
##
|
|
|
|
# Sets coverage report level. Accepted values are:
|
|
|
|
#
|
|
|
|
# false or nil:: No report
|
|
|
|
# 0:: Classes, modules, constants, attributes, methods
|
|
|
|
# 1:: Level 0 + method parameters
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
def coverage_level= level
|
|
|
|
level = -1 unless level
|
|
|
|
|
|
|
|
@coverage_level = level
|
2010-12-19 22:22:49 -05:00
|
|
|
end
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Returns the length and number of undocumented items in +collection+.
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
def doc_stats collection
|
2012-11-26 23:28:14 -05:00
|
|
|
visible = collection.select { |item| item.display? }
|
|
|
|
[visible.length, visible.count { |item| not item.documented? }]
|
2010-12-19 22:22:49 -05:00
|
|
|
end
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Call this to mark the end of parsing for display purposes
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
def done_adding
|
|
|
|
@display.done_adding
|
|
|
|
end
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# The documentation status of this project. +true+ when 100%, +false+ when
|
|
|
|
# less than 100% and +nil+ when unknown.
|
|
|
|
#
|
|
|
|
# Set by calling #calculate
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
def fully_documented?
|
|
|
|
@fully_documented
|
2008-07-21 14:35:14 -04:00
|
|
|
end
|
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
##
|
|
|
|
# A report that says you did a great job!
|
|
|
|
|
|
|
|
def great_job
|
|
|
|
report = []
|
|
|
|
report << '100% documentation!'
|
|
|
|
report << nil
|
|
|
|
report << 'Great Job!'
|
|
|
|
|
|
|
|
report.join "\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Calculates the percentage of items documented.
|
|
|
|
|
|
|
|
def percent_doc
|
|
|
|
return @percent_doc if @percent_doc
|
|
|
|
|
|
|
|
@fully_documented = (@num_items - @doc_items) == 0
|
|
|
|
|
|
|
|
@percent_doc = @doc_items.to_f / @num_items * 100 if @num_items.nonzero?
|
|
|
|
@percent_doc ||= 0
|
|
|
|
|
|
|
|
@percent_doc
|
|
|
|
end
|
|
|
|
|
2010-04-01 03:45:16 -04:00
|
|
|
##
|
2010-12-19 22:22:49 -05:00
|
|
|
# Returns a report on which items are not documented
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
def report
|
2011-02-01 19:32:30 -05:00
|
|
|
if @coverage_level > 0 then
|
|
|
|
extend RDoc::Text
|
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
report = []
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
if @coverage_level.zero? then
|
|
|
|
calculate
|
|
|
|
|
|
|
|
return great_job if @num_items == @doc_items
|
|
|
|
end
|
|
|
|
|
2012-11-26 23:28:14 -05:00
|
|
|
ucm = @store.unique_classes_and_modules
|
2011-02-01 19:32:30 -05:00
|
|
|
|
|
|
|
ucm.sort.each do |cm|
|
|
|
|
report << report_class_module(cm) {
|
|
|
|
[
|
|
|
|
report_constants(cm),
|
|
|
|
report_attributes(cm),
|
|
|
|
report_methods(cm),
|
|
|
|
].compact
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
if @coverage_level > 0 then
|
|
|
|
calculate
|
|
|
|
|
|
|
|
return great_job if @num_items == @doc_items
|
|
|
|
end
|
|
|
|
|
|
|
|
report.unshift nil
|
|
|
|
report.unshift 'The following items are not documented:'
|
|
|
|
|
|
|
|
report.join "\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Returns a report on undocumented attributes in ClassModule +cm+
|
|
|
|
|
|
|
|
def report_attributes cm
|
|
|
|
return if cm.attributes.empty?
|
|
|
|
|
|
|
|
report = []
|
|
|
|
|
|
|
|
cm.each_attribute do |attr|
|
|
|
|
next if attr.documented?
|
2012-11-26 23:28:14 -05:00
|
|
|
line = attr.line ? ":#{attr.line}" : nil
|
|
|
|
report << " #{attr.definition} :#{attr.name} # in file #{attr.file.full_name}#{line}"
|
2011-02-01 19:32:30 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
report
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Returns a report on undocumented items in ClassModule +cm+
|
|
|
|
|
|
|
|
def report_class_module cm
|
|
|
|
return if cm.fully_documented? and @coverage_level.zero?
|
2012-11-26 23:28:14 -05:00
|
|
|
return unless cm.display?
|
2011-02-01 19:32:30 -05:00
|
|
|
|
|
|
|
report = []
|
|
|
|
|
|
|
|
if cm.in_files.empty? then
|
|
|
|
report << "# #{cm.definition} is referenced but empty."
|
2012-11-26 23:28:14 -05:00
|
|
|
report << "#"
|
|
|
|
report << "# It probably came from another project. I'm sorry I'm holding it against you."
|
2011-02-01 19:32:30 -05:00
|
|
|
report << nil
|
|
|
|
|
|
|
|
return report
|
|
|
|
elsif cm.documented? then
|
|
|
|
documented = true
|
|
|
|
report << "#{cm.definition} # is documented"
|
|
|
|
else
|
|
|
|
report << '# in files:'
|
|
|
|
|
|
|
|
cm.in_files.each do |file|
|
|
|
|
report << "# #{file.full_name}"
|
|
|
|
end
|
2008-07-21 14:35:14 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
report << nil
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
report << "#{cm.definition}"
|
2008-07-21 14:35:14 -04:00
|
|
|
end
|
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
body = yield.flatten # HACK remove #flatten
|
|
|
|
|
|
|
|
return if body.empty? and documented
|
|
|
|
|
|
|
|
report << nil << body unless body.empty?
|
|
|
|
|
|
|
|
report << 'end'
|
2010-12-19 22:22:49 -05:00
|
|
|
report << nil
|
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
report
|
|
|
|
end
|
2010-12-19 22:22:49 -05:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
##
|
|
|
|
# Returns a report on undocumented constants in ClassModule +cm+
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
def report_constants cm
|
|
|
|
return if cm.constants.empty?
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
report = []
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
cm.each_constant do |constant|
|
|
|
|
# TODO constant aliases are listed in the summary but not reported
|
|
|
|
# figure out what to do here
|
|
|
|
next if constant.documented? || constant.is_alias_for
|
2012-11-26 23:28:14 -05:00
|
|
|
|
|
|
|
line = constant.line ? ":#{constant.line}" : line
|
|
|
|
report << " # in file #{constant.file.full_name}#{line}"
|
2011-02-01 19:32:30 -05:00
|
|
|
report << " #{constant.name} = nil"
|
|
|
|
end
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
report
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
# Returns a report on undocumented methods in ClassModule +cm+
|
2008-07-21 14:35:14 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
def report_methods cm
|
|
|
|
return if cm.method_list.empty?
|
2008-07-21 14:35:14 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
report = []
|
|
|
|
|
|
|
|
cm.each_method do |method|
|
|
|
|
next if method.documented? and @coverage_level.zero?
|
|
|
|
|
|
|
|
if @coverage_level > 0 then
|
|
|
|
params, undoc = undoc_params method
|
2008-07-21 14:35:14 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
@num_params += params
|
2008-07-21 14:35:14 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
unless undoc.empty? then
|
|
|
|
@undoc_params += undoc.length
|
|
|
|
|
|
|
|
undoc = undoc.map do |param| "+#{param}+" end
|
|
|
|
param_report = " # #{undoc.join ', '} is not documented"
|
2010-12-19 22:22:49 -05:00
|
|
|
end
|
|
|
|
end
|
2008-07-21 14:35:14 -04:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
next if method.documented? and not param_report
|
2012-11-26 23:28:14 -05:00
|
|
|
|
|
|
|
line = method.line ? ":#{method.line}" : nil
|
|
|
|
scope = method.singleton ? 'self.' : nil
|
|
|
|
|
|
|
|
report << " # in file #{method.file.full_name}#{line}"
|
2011-02-01 19:32:30 -05:00
|
|
|
report << param_report if param_report
|
2012-11-26 23:28:14 -05:00
|
|
|
report << " def #{scope}#{method.name}#{method.params}; end"
|
2010-12-19 22:22:49 -05:00
|
|
|
report << nil
|
2008-07-21 14:35:14 -04:00
|
|
|
end
|
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
report
|
2010-12-19 22:22:49 -05:00
|
|
|
end
|
2010-04-01 03:45:16 -04:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
##
|
|
|
|
# Returns a summary of the collected statistics.
|
|
|
|
|
|
|
|
def summary
|
|
|
|
calculate
|
|
|
|
|
2010-12-28 17:08:56 -05:00
|
|
|
num_width = [@num_files, @num_items].max.to_s.length
|
2011-02-01 19:32:30 -05:00
|
|
|
undoc_width = [
|
2010-12-28 17:08:56 -05:00
|
|
|
@undoc_attributes,
|
|
|
|
@undoc_classes,
|
|
|
|
@undoc_constants,
|
|
|
|
@undoc_items,
|
|
|
|
@undoc_methods,
|
|
|
|
@undoc_modules,
|
2011-02-01 19:32:30 -05:00
|
|
|
@undoc_params,
|
2010-12-28 17:08:56 -05:00
|
|
|
].max.to_s.length
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
report = []
|
2010-12-28 17:08:56 -05:00
|
|
|
report << 'Files: %*d' % [num_width, @num_files]
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
report << nil
|
2010-12-28 17:08:56 -05:00
|
|
|
|
|
|
|
report << 'Classes: %*d (%*d undocumented)' % [
|
2011-02-01 19:32:30 -05:00
|
|
|
num_width, @num_classes, undoc_width, @undoc_classes]
|
2010-12-28 17:08:56 -05:00
|
|
|
report << 'Modules: %*d (%*d undocumented)' % [
|
2011-02-01 19:32:30 -05:00
|
|
|
num_width, @num_modules, undoc_width, @undoc_modules]
|
2010-12-28 17:08:56 -05:00
|
|
|
report << 'Constants: %*d (%*d undocumented)' % [
|
2011-02-01 19:32:30 -05:00
|
|
|
num_width, @num_constants, undoc_width, @undoc_constants]
|
2010-12-28 17:08:56 -05:00
|
|
|
report << 'Attributes: %*d (%*d undocumented)' % [
|
2011-02-01 19:32:30 -05:00
|
|
|
num_width, @num_attributes, undoc_width, @undoc_attributes]
|
2010-12-28 17:08:56 -05:00
|
|
|
report << 'Methods: %*d (%*d undocumented)' % [
|
2011-02-01 19:32:30 -05:00
|
|
|
num_width, @num_methods, undoc_width, @undoc_methods]
|
|
|
|
report << 'Parameters: %*d (%*d undocumented)' % [
|
|
|
|
num_width, @num_params, undoc_width, @undoc_params] if
|
|
|
|
@coverage_level > 0
|
2010-12-28 17:08:56 -05:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
report << nil
|
2010-12-28 17:08:56 -05:00
|
|
|
|
|
|
|
report << 'Total: %*d (%*d undocumented)' % [
|
2011-02-01 19:32:30 -05:00
|
|
|
num_width, @num_items, undoc_width, @undoc_items]
|
2010-12-19 22:22:49 -05:00
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
report << '%6.2f%% documented' % percent_doc
|
2010-12-19 22:22:49 -05:00
|
|
|
report << nil
|
|
|
|
report << 'Elapsed: %0.1fs' % (Time.now - @start)
|
|
|
|
|
|
|
|
report.join "\n"
|
2008-01-13 22:34:05 -05:00
|
|
|
end
|
|
|
|
|
2011-02-01 19:32:30 -05:00
|
|
|
##
|
|
|
|
# Determines which parameters in +method+ were not documented. Returns a
|
|
|
|
# total parameter count and an Array of undocumented methods.
|
|
|
|
|
|
|
|
def undoc_params method
|
|
|
|
@formatter ||= RDoc::Markup::ToTtOnly.new
|
|
|
|
|
|
|
|
params = method.param_list
|
|
|
|
|
|
|
|
return 0, [] if params.empty?
|
|
|
|
|
|
|
|
document = parse method.comment
|
|
|
|
|
|
|
|
tts = document.accept @formatter
|
|
|
|
|
|
|
|
undoc = params - tts
|
|
|
|
|
|
|
|
[params.length, undoc]
|
|
|
|
end
|
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
autoload :Quiet, 'rdoc/stats/quiet'
|
|
|
|
autoload :Normal, 'rdoc/stats/normal'
|
|
|
|
autoload :Verbose, 'rdoc/stats/verbose'
|
2008-01-13 22:34:05 -05:00
|
|
|
|
2010-12-19 22:22:49 -05:00
|
|
|
end
|
2008-01-13 22:34:05 -05:00
|
|
|
|