class Codestats
Constants
- IRC_RESET
- MANUAL_LOC_PER_DAY
- SECTION_LABELS
- SECTION_ORDER
- STYLE_ACCENT
- STYLE_COUNT
- STYLE_DIVIDER
- STYLE_TITLE
- TABLE_COLUMN_ALIGN
- TABLE_COLUMN_HEADERS
- TABLE_COLUMN_KEYS
- TABLE_PAD
Public Class Methods
Source
# File plugins/codestats.rb, line 132 def self.aggregate_rows(rows) rows.reduce(empty_stats.dup) do |total, row| merge_stats!(total, row) total end end
Source
# File plugins/codestats.rb, line 71 def self.analyze_source(text) lines = text.to_s.lines stats = empty_stats.dup stats[:lines] = lines.length stats[:loc] = lines.count { |line| loc_line?(line) } stats[:classes] = lines.count { |line| line.match?(/^\s*(class|module)\s+/) } stats[:methods] = lines.count { |line| line.match?(/^\s*def\s+/) } stats end
Source
# File plugins/codestats.rb, line 95 def self.categorize_path(relative_path) path = relative_path.to_s.tr("\\", "/") case path when %r{\Aplugins/.+\.rb\z} then :plugins when %r{\Alib/.+\.rb\z} then :libraries when %r{\Atest/.+\.rb\z} then :tests when %r{\Ascript/.+\.rb\z} then :scripts when /\Arabbot\.rb\z/, /\ARakefile\z/ then :root end end
Source
# File plugins/codestats.rb, line 244 def self.closing_doodle [ IrcFormat.decorate("+-------+", STYLE_DIVIDER), IrcFormat.decorate("| /stats|", STYLE_DIVIDER), IrcFormat.decorate("+-------+", STYLE_DIVIDER) ].join("\n") end
Source
# File plugins/codestats.rb, line 148 def self.code_test_ratio(code_loc, test_loc) return "1:0" if test_loc.zero? ratio = (code_loc.to_f / test_loc).round(1) "1:#{ratio}" end
Source
# File plugins/codestats.rb, line 139 def self.code_test_split(rows) test_loc = rows.fetch(:tests, empty_stats)[:loc] code_loc = SECTION_ORDER.reject { |key| key == :tests }.sum do |key| rows.fetch(key, empty_stats)[:loc] end { code_loc: code_loc, test_loc: test_loc } end
Source
# File plugins/codestats.rb, line 53 def self.command_pattern /codestats$/i end
Source
# File plugins/codestats.rb, line 321 def self.default_enumerator(root = default_root) lambda do files = [] %w[plugins lib test script].each do |dir| base = File.join(root, dir) next unless File.directory?(base) Dir.glob(File.join(base, "**", "*.rb")).sort.each do |absolute| relative = absolute.delete_prefix("#{root}/") files << [relative, File.read(absolute)] end end %w[rabbot.rb Rakefile].each do |name| absolute = File.join(root, name) files << [name, File.read(absolute)] if File.file?(absolute) end files end end
Source
# File plugins/codestats.rb, line 317 def self.default_root File.expand_path("..", __dir__) end
Source
# File plugins/codestats.rb, line 59 def self.empty_stats { lines: 0, loc: 0, classes: 0, methods: 0 } end
Source
# File plugins/codestats.rb, line 279 def self.format_manual_coding_duration(loc) days = manual_coding_days(loc) return "0 days" if days.zero? years, remainder = days.divmod(365) months, day_count = remainder.divmod(30) parts = [] parts << plural_duration(years, "year") if years.positive? parts << plural_duration(months, "month") if months.positive? parts << plural_duration(day_count, "day") if day_count.positive? || parts.empty? join_duration_parts(parts) end
Source
# File plugins/codestats.rb, line 293 def self.format_manual_coding_estimate(loc) loc = loc.to_i duration = format_manual_coding_duration(loc) " Early IRC hand-coding est.: ~#{duration} (#{loc} LOC @ #{MANUAL_LOC_PER_DAY}/day)" end
Source
# File plugins/codestats.rb, line 299 def self.format_report(files:, project: "rabbot") rows = gather_stats(files) split = code_test_split(rows) total_loc = aggregate_rows(rows.values)[:loc] lines = [ IrcFormat.report_header(tag: "CODESTATS", title: "#{project} ยท lines of code", style: STYLE_TITLE), opening_doodle, IrcFormat.divider("stats", style: STYLE_DIVIDER), format_table(rows), format_summary(split), format_manual_coding_estimate(total_loc), IrcFormat.report_footer("rabbot source", "rails-style stats"), closing_doodle ] lines.join("\n") end
Source
# File plugins/codestats.rb, line 205 def self.format_row_values(row, widths) cells = [ format_table_cell(row[:label], width: widths[:name], align: :left), format_table_cell(row[:lines], width: widths[:lines], align: :right), format_table_cell(row[:loc], width: widths[:loc], align: :right), format_table_cell(row[:classes], width: widths[:classes], align: :right), format_table_cell(row[:methods], width: widths[:methods], align: :right), format_table_cell(methods_per_class(row), width: widths[:mc], align: :right), format_table_cell(loc_per_method(row), width: widths[:loc_m], align: :right) ] format_table_line(cells) end
Source
# File plugins/codestats.rb, line 252 def self.format_summary(split) ratio = code_test_ratio(split[:code_loc], split[:test_loc]) [ " Code LOC: #{split[:code_loc]} lines", " Test LOC: #{split[:test_loc]} lines", " Code to Test Ratio: #{ratio}" ].join("\n") end
Source
# File plugins/codestats.rb, line 218 def self.format_table(rows) widths = table_column_widths(rows) ordered = SECTION_ORDER.filter_map { |key| rows[key] } total = aggregate_rows(ordered).merge(label: "Total") border = format_table_border(widths) body = ordered.map { |row| format_row_values(row, widths) } [ border, format_table_header(widths), border, *body, border, format_row_values(total, widths), border ].join("\n") end
Source
# File plugins/codestats.rb, line 190 def self.format_table_border(widths) "+" + TABLE_COLUMN_KEYS.map { |key| "-" * widths[key] }.join("+") + "+" end
Source
# File plugins/codestats.rb, line 175 def self.format_table_cell(text, width:, align: :left) value = text.to_s value = value[0, width] if value.length > width if align == :right value.rjust(width, TABLE_PAD) else value.ljust(width, TABLE_PAD) end end
Source
# File plugins/codestats.rb, line 194 def self.format_table_header(widths) cells = TABLE_COLUMN_KEYS.map do |key| format_table_cell( TABLE_COLUMN_HEADERS.fetch(key), width: widths.fetch(key), align: TABLE_COLUMN_ALIGN.fetch(key) ) end format_table_line(cells) end
Source
# File plugins/codestats.rb, line 186 def self.format_table_line(cells) "|#{cells.join("|")}|" end
Source
# File plugins/codestats.rb, line 114 def self.gather_stats(files_hash) buckets = Hash.new { |h, key| h[key] = empty_stats.dup } files_hash.each do |path, content| key = categorize_path(path) next unless key merge_stats!(buckets[key], analyze_source(content)) end SECTION_ORDER.each_with_object({}) do |key, rows| stats = buckets[key] next if stats[:lines].zero? rows[key] = stats.merge(label: SECTION_LABELS.fetch(key)) end end
Source
# File plugins/codestats.rb, line 272 def self.join_duration_parts(parts) return parts.first if parts.length == 1 return parts.join(" and ") if parts.length == 2 "#{parts[0..-2].join(', ')} and #{parts.last}" end
Source
# File plugins/codestats.rb, line 63 def self.loc_line?(line) stripped = line.strip return false if stripped.empty? return false if stripped.start_with?("#") true end
Source
# File plugins/codestats.rb, line 88 def self.loc_per_method(stats) methods = stats[:methods].to_i return 0 if methods.zero? stats[:loc].to_i / methods end
Source
# File plugins/codestats.rb, line 261 def self.manual_coding_days(loc) loc = loc.to_i return 0 if loc.zero? (loc.to_f / MANUAL_LOC_PER_DAY).ceil end
Source
# File plugins/codestats.rb, line 106 def self.merge_stats!(target, source) target[:lines] += source[:lines] target[:loc] += source[:loc] target[:classes] += source[:classes] target[:methods] += source[:methods] target end
Source
# File plugins/codestats.rb, line 81 def self.methods_per_class(stats) classes = stats[:classes].to_i return 0 if classes.zero? stats[:methods].to_i / classes end
Source
# File plugins/codestats.rb, line 236 def self.opening_doodle [ IrcFormat.decorate("+---+", STYLE_ACCENT), IrcFormat.decorate("|LOC|", STYLE_ACCENT), IrcFormat.decorate("+---+", STYLE_ACCENT) ].join("\n") end
Source
# File plugins/codestats.rb, line 268 def self.plural_duration(count, word) "#{count} #{word}#{"s" if count != 1}" end
Source
# File plugins/codestats.rb, line 343 def self.report(enumerate: nil, project: nil, root: default_root) enumerate ||= default_enumerator(root) project ||= File.basename(root) files_hash = enumerate.call.to_h format_report(files: files_hash, project: project) end
Source
# File plugins/codestats.rb, line 161 def self.table_column_widths(rows) table_rows = table_rows_for_formatting(rows) { name: [table_rows.map { |row| row[:label].to_s.length }.max, TABLE_COLUMN_HEADERS[:name].length].max, lines: [table_rows.map { |row| row[:lines].to_s.length }.max, TABLE_COLUMN_HEADERS[:lines].length].max, loc: [table_rows.map { |row| row[:loc].to_s.length }.max, TABLE_COLUMN_HEADERS[:loc].length].max, classes: [table_rows.map { |row| row[:classes].to_s.length }.max, TABLE_COLUMN_HEADERS[:classes].length].max, methods: [table_rows.map { |row| row[:methods].to_s.length }.max, TABLE_COLUMN_HEADERS[:methods].length].max, mc: [table_rows.map { |row| methods_per_class(row).to_s.length }.max, TABLE_COLUMN_HEADERS[:mc].length].max, loc_m: [table_rows.map { |row| loc_per_method(row).to_s.length }.max, TABLE_COLUMN_HEADERS[:loc_m].length].max } end
Source
# File plugins/codestats.rb, line 155 def self.table_rows_for_formatting(rows) ordered = SECTION_ORDER.filter_map { |key| rows[key] } total = aggregate_rows(ordered).merge(label: "Total") ordered + [total] end
Public Instance Methods
Source
# File plugins/codestats.rb, line 354 def execute(m) themed_flood_safe_reply(m, report) end
Source
# File plugins/codestats.rb, line 350 def report(enumerate: nil, project: nil, root: self.class.default_root) self.class.report(enumerate: enumerate, project: project, root: root) end