Class: RDoc::Stats

Inherits:
Object
  • Object
show all
Includes:
Text
Defined in:
lib/rdoc/stats.rb

Overview

RDoc statistics collector which prints a summary and report of a project’s documentation totals.

Defined Under Namespace

Classes: Normal, Quiet, Verbose

Constant Summary collapse

TYPE_ORDER =

Display order for item types in the coverage report

%w[Class Module Constant Attribute Method].freeze
GREAT_JOB_MESSAGE =

Message displayed when all items are documented

<<~MSG
  100% documentation!
  Great Job!
MSG

Constants included from Text

Text::MARKUP_FORMAT, Text::SPACE_SEPARATED_LETTER_CLASS

Instance Attribute Summary collapse

Attributes included from Text

#language

Instance Method Summary collapse

Methods included from Text

decode_legacy_label, expand_tabs, #flush_left, #markup, #normalize_comment, #parse, #snippet, #strip_hashes, #strip_newlines, #strip_stars, to_anchor, #wrap

Constructor Details

#initialize(store, num_files, verbosity = 1) ⇒ Stats

Creates a new Stats that will have num_files. verbosity defaults to 1 which will create an RDoc::Stats::Normal outputter.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rdoc/stats.rb', line 42

def initialize(store, num_files, verbosity = 1)
  @num_files = num_files
  @store     = store

  @coverage_level   = 0
  @doc_items        = nil
  @files_so_far     = 0
  @fully_documented = false
  @num_params       = 0
  @percent_doc      = nil
  @start            = Time.now
  @undoc_params     = 0

  self.verbosity = verbosity
end

Instance Attribute Details

#coverage_levelObject

Output level for the coverage report



26
27
28
# File 'lib/rdoc/stats.rb', line 26

def coverage_level
  @coverage_level
end

#files_so_farObject (readonly)

Count of files parsed during parsing



31
32
33
# File 'lib/rdoc/stats.rb', line 31

def files_so_far
  @files_so_far
end

#num_filesObject (readonly)

Total number of files found



36
37
38
# File 'lib/rdoc/stats.rb', line 36

def num_files
  @num_files
end

Instance Method Details

#add_alias(as) ⇒ Object

Records the parsing of an alias as.



72
73
74
# File 'lib/rdoc/stats.rb', line 72

def add_alias(as)
  @display.print_alias as
end

#add_attribute(attribute) ⇒ Object

Records the parsing of an attribute attribute



79
80
81
# File 'lib/rdoc/stats.rb', line 79

def add_attribute(attribute)
  @display.print_attribute attribute
end

#add_class(klass) ⇒ Object

Records the parsing of a class klass



86
87
88
# File 'lib/rdoc/stats.rb', line 86

def add_class(klass)
  @display.print_class klass
end

#add_constant(constant) ⇒ Object

Records the parsing of constant



93
94
95
# File 'lib/rdoc/stats.rb', line 93

def add_constant(constant)
  @display.print_constant constant
end

#add_file(file) ⇒ Object

Records the parsing of file



100
101
102
103
# File 'lib/rdoc/stats.rb', line 100

def add_file(file)
  @files_so_far += 1
  @display.print_file @files_so_far, file
end

#add_method(method) ⇒ Object

Records the parsing of method



108
109
110
# File 'lib/rdoc/stats.rb', line 108

def add_method(method)
  @display.print_method method
end

#add_module(mod) ⇒ Object

Records the parsing of a module mod



115
116
117
# File 'lib/rdoc/stats.rb', line 115

def add_module(mod)
  @display.print_module mod
end

#begin_addingObject

Call this to mark the beginning of parsing for display purposes



122
123
124
# File 'lib/rdoc/stats.rb', line 122

def begin_adding
  @display.begin_adding
end

#calculateObject

Calculates documentation totals and percentages for classes, modules, constants, attributes and methods.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/rdoc/stats.rb', line 130

def calculate
  return if @doc_items

  ucm = @store.unique_classes_and_modules

  classes = @store.unique_classes.reject { |cm| cm.full_name == 'Object' }

  constants = []
  ucm.each { |cm| constants.concat cm.constants }

  methods = []
  ucm.each { |cm| methods.concat cm.method_list }

  attributes = []
  ucm.each { |cm| attributes.concat cm.attributes }

  @num_attributes, @undoc_attributes = doc_stats attributes
  @num_classes,    @undoc_classes    = doc_stats classes
  @num_constants,  @undoc_constants  = doc_stats constants
  @num_methods,    @undoc_methods    = doc_stats methods
  @num_modules,    @undoc_modules    = doc_stats @store.unique_modules

  @num_items =
    @num_attributes +
    @num_classes +
    @num_constants +
    @num_methods +
    @num_modules +
    @num_params

  @undoc_items =
    @undoc_attributes +
    @undoc_classes +
    @undoc_constants +
    @undoc_methods +
    @undoc_modules +
    @undoc_params

  @doc_items = @num_items - @undoc_items
end

#collect_undocumented_attributes(class_module, items) ⇒ Object

Collects undocumented attributes from class_module into items.



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/rdoc/stats.rb', line 357

def collect_undocumented_attributes(class_module, items)
  class_module.attributes.each do |attr|
    next unless attr.display?
    next if attr.documented?

    file = attr.file&.full_name
    next unless file

    scope = attr.singleton ? "." : "#"
    items << {
      type: "Attribute",
      name: "#{class_module.full_name}#{scope}#{attr.name}",
      file: file,
      line: attr.line,
    }
  end
end

#collect_undocumented_class_module(class_module, items) ⇒ Object

Collects undocumented classes or modules from class_module into items. Reopened classes/modules are reported in every file they appear in.



323
324
325
326
327
328
329
330
331
332
# File 'lib/rdoc/stats.rb', line 323

def collect_undocumented_class_module(class_module, items)
  class_module.in_files.map(&:full_name).uniq.each do |file|
    items << {
      type: class_module.type.capitalize,
      name: class_module.full_name,
      file: file,
      line: nil,
    }
  end
end

#collect_undocumented_constants(class_module, items) ⇒ Object

Collects undocumented constants from class_module into items.



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/rdoc/stats.rb', line 337

def collect_undocumented_constants(class_module, items)
  class_module.constants.each do |constant|
    next unless constant.display?
    next if constant.documented? || constant.is_alias_for

    file = constant.file&.full_name
    next unless file

    items << {
      type: "Constant",
      name: constant.full_name,
      file: file,
      line: constant.line,
    }
  end
end

#collect_undocumented_itemsObject

Collects all undocumented items across all classes and modules. Returns [items, empty_classes] where items is an Array of Hashes with keys :type, :name, :file, :line, and empty_classes is an Array of ClassModule objects that are referenced but have no files.



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/rdoc/stats.rb', line 295

def collect_undocumented_items
  empty_classes = []
  items = []

  @store.unique_classes_and_modules.each do |class_module|
    next unless class_module.display?

    if class_module.in_files.empty?
      empty_classes << class_module
      next
    end

    unless class_module.documented? || class_module.full_name == 'Object'
      collect_undocumented_class_module(class_module, items)
    end

    collect_undocumented_constants(class_module, items)
    collect_undocumented_attributes(class_module, items)
    collect_undocumented_methods(class_module, items)
  end

  [items, empty_classes]
end

#collect_undocumented_methods(class_module, items) ⇒ Object

Collects undocumented methods from class_module into items. At coverage level > 0, also counts undocumented parameters.



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/rdoc/stats.rb', line 379

def collect_undocumented_methods(class_module, items)
  class_module.each_method do |method|
    next unless method.display?
    next if method.documented? && @coverage_level.zero?

    undoc_param_names = nil

    if @coverage_level > 0
      params, undoc = undoc_params method
      @num_params += params

      unless undoc.empty?
        @undoc_params += undoc.length
        undoc_param_names = undoc
      end
    end

    next if method.documented? && !undoc_param_names

    file = method.file&.full_name
    next unless file

    scope = method.singleton ? "." : "#"
    item = {
      type: "Method",
      name: "#{class_module.full_name}#{scope}#{method.name}",
      file: file,
      line: method.line,
    }
    item[:undoc_params] = undoc_param_names if undoc_param_names

    items << item
  end
end

#doc_stats(collection) ⇒ Object

Returns the length and number of undocumented items in collection.



187
188
189
190
# File 'lib/rdoc/stats.rb', line 187

def doc_stats(collection)
  visible = collection.select { |item| item.display? }
  [visible.length, visible.count { |item| not item.documented? }]
end

#done_addingObject

Call this to mark the end of parsing for display purposes



195
196
197
# File 'lib/rdoc/stats.rb', line 195

def done_adding
  @display.done_adding
end

#fully_documented?Boolean

The documentation status of this project. true when 100%, false when less than 100% and nil when unknown.

Set by calling #calculate

Returns:

  • (Boolean)


205
206
207
# File 'lib/rdoc/stats.rb', line 205

def fully_documented?
  @fully_documented
end

#percent_docObject

Calculates the percentage of items documented.



212
213
214
215
216
217
218
219
220
221
# File 'lib/rdoc/stats.rb', line 212

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

#reportObject

Returns a report on which items are not documented



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/rdoc/stats.rb', line 226

def report
  if @coverage_level > 0 then
    extend RDoc::Text
  end

  if @coverage_level.zero? then
    calculate

    return GREAT_JOB_MESSAGE if @num_items == @doc_items
  end

  items, empty_classes = collect_undocumented_items

  if @coverage_level > 0 then
    calculate

    return GREAT_JOB_MESSAGE if @num_items == @doc_items
  end

  report = +""
  report << "The following items are not documented:\n\n"

  # Referenced-but-empty classes
  empty_classes.each do |cm|
    report << "#{cm.full_name} is referenced but empty.\n"
    report << "It probably came from another project.  I'm sorry I'm holding it against you.\n\n"
  end

  # Group items by file, then by type
  by_file = items.group_by { |item| item[:file] }

  by_file.sort_by { |file, _| file }.each do |file, file_items|
    report << "#{file}:\n"

    by_type = file_items.group_by { |item| item[:type] }

    TYPE_ORDER.each do |type|
      next unless by_type[type]

      report << "  #{type}:\n"

      sorted = by_type[type].sort_by { |item| [item[:line] || 0, item[:name]] }
      name_width = sorted.reduce(0) { |max, item| item[:line] && item[:name].length > max ? item[:name].length : max }

      sorted.each do |item|
        if item[:line]
          report << "    %-*s %s:%d\n" % [name_width, item[:name], item[:file], item[:line]]
        else
          report << "    #{item[:name]}\n"
        end

        if item[:undoc_params]
          report << "      Undocumented params: #{item[:undoc_params].join(', ')}\n"
        end
      end
    end

    report << "\n"
  end

  report
end

#summaryObject

Returns a summary of the collected statistics.



417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/rdoc/stats.rb', line 417

def summary
  calculate

  num_width = [@num_files, @num_items].max.to_s.length
  undoc_width = [
    @undoc_attributes,
    @undoc_classes,
    @undoc_constants,
    @undoc_items,
    @undoc_methods,
    @undoc_modules,
    @undoc_params,
  ].max.to_s.length

  report = +""

  report << "Files:      %*d\n" % [num_width, @num_files]
  report << "\n"
  report << "Classes:    %*d (%*d undocumented)\n" % [
    num_width, @num_classes, undoc_width, @undoc_classes]
  report << "Modules:    %*d (%*d undocumented)\n" % [
    num_width, @num_modules, undoc_width, @undoc_modules]
  report << "Constants:  %*d (%*d undocumented)\n" % [
    num_width, @num_constants, undoc_width, @undoc_constants]
  report << "Attributes: %*d (%*d undocumented)\n" % [
    num_width, @num_attributes, undoc_width, @undoc_attributes]
  report << "Methods:    %*d (%*d undocumented)\n" % [
    num_width, @num_methods, undoc_width, @undoc_methods]
  report << "Parameters: %*d (%*d undocumented)\n" % [
    num_width, @num_params, undoc_width, @undoc_params] if
      @coverage_level > 0
  report << "\n"
  report << "Total:      %*d (%*d undocumented)\n" % [
    num_width, @num_items, undoc_width, @undoc_items]
  report << "%6.2f%% documented\n" % percent_doc
  report << "\n"
  report << "Elapsed: %0.1fs\n" % (Time.now - @start)

  report
end

#undoc_params(method) ⇒ Object

Determines which parameters in method were not documented. Returns a total parameter count and an Array of undocumented methods.



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/rdoc/stats.rb', line 462

def undoc_params(method)
  @formatter ||= RDoc::Markup::ToTtOnly.new

  params = method.param_list

  params = params.map { |param| param.gsub(/^\*\*?/, '') }

  return 0, [] if params.empty?

  document = parse method.comment

  tts = document.accept @formatter

  undoc = params - tts

  [params.length, undoc]
end

#verbosity=(verbosity) ⇒ Object

Sets the verbosity level, rebuilding the display outputter.



61
62
63
64
65
66
67
# File 'lib/rdoc/stats.rb', line 61

def verbosity=(verbosity)
  @display = case verbosity
             when 0 then Quiet.new   @num_files
             when 1 then Normal.new  @num_files
             else        Verbose.new @num_files
             end
end