Checking YARD Documentation Coverage

2023/11/24

If you use YARD to document a Ruby project, it's handy to know what is not yet documented.

Documentation can be of various types, there are class descriptions, parameter definitions, return type definitions and so on.

I'm concentrating on documenting the public interface to my code, so I'd like to list everything with public visibility that's not covered.

yard stats

YARD has a stats command

> yard stats
Files:          77
Modules:         8 (    8 undocumented)
Classes:        77 (   76 undocumented)
Constants:      32 (   32 undocumented)
Attributes:     60 (    0 undocumented)
Methods:       259 (  176 undocumented)
 33.03% documented

That gives you an idea of documentation coverage, but doesn't tell you what needs documenting.

By the way, I'm running these examples on various versions of the imap-backup gem.

Yardstick

There is a gem called yardstick which measures YARD documentation coverage.

> yardstick 'lib/**/*.rb'
...
lib/imap/backup/account/folder.rb:173: Imap::Backup::Account::Folder#utf7_encoded_name: The @api tag should be specified
lib/imap/backup/account/folder.rb:173: Imap::Backup::Account::Folder#utf7_encoded_name: The @api tag must be either public, semipublic or private
lib/imap/backup/account/folder.rb:173: Imap::Backup::Account::Folder#utf7_encoded_name: A method with private visibility must have an @api tag of private
lib/imap/backup/account/folder.rb:173: Imap::Backup::Account::Folder#utf7_encoded_name: The public/semipublic method should have an example specified
lib/imap/backup/account/folder.rb:173: Imap::Backup::Account::Folder#utf7_encoded_name: The method summary should be specified
lib/imap/backup/account/folder.rb:173: Imap::Backup::Account::Folder#utf7_encoded_name: The @return tag should be specified
...
YARD-Coverage: 48.6%  Success: 2820  Failed: 2980  Total: 5800

It indicates that everything that does not have documentation coverage, regardless of public/protected/private visibility.

If, by default, you want to document everything, yardstick will work fine for you.

In my case, I'm most interested in documenting public methods/classes/etc.

As far as I can see, the only way to make Yardstick ignore private code is by adding the @api private tag, which for me would be unnecessarily cumbersome.

YARD queries

My solution to this problem is to use YARD's --query system.

The query you supply is Ruby code that can access each code item as YARD scans your project.

For example, to list just methods, you do this:

> yard list --query 'object.is_a?(YARD::CodeObjects::MethodObject)'
...
lib/imap/backup/account/folder.rb:26: Imap::Backup::Account::Folder#client
...

object is the item in your project, wrapped in a YARD::CodeObjects instance.

YARD::CodeObjects can be methods, classes, constants etc.

This become useful when you filter on an aspect of the code and it's relative coverage.

For example

yard list \
  --query 'object.is_a?(YARD::CodeObjects::MethodObject) && object.parameters.any? && !object.tag(:param)

That's rather long, but can be shortened. YARD uses a method_missing trick to allow you to omit object (as long as the method you are calling is not implemented by its Verifier class).

So the example above can be shortened to

yard list --query 'object.is_a?(CodeObjects::MethodObject) && parameters.any? && !tag(:param)'

The Queries I Use

Methods without parameter documentation

yard list --query 'object.is_a?(CodeObjects::MethodObject) && parameters.any? && !tag(:param)'

Methods without a documented return type

yard list --query 'object.is_a?(CodeObjects::MethodObject) && !constructor? && !tag(:return)'

Classes without a description

yard list --query 'object.is_a?(CodeObjects::ClassObject) && !tag(:private) && docstring.empty?'

Public constants without a description

yard list --query 'object.is_a?(CodeObjects::ConstantObject) && !tag(:private) && docstring.empty?'

Conclusion

These queries provide a fast and flexible was of finding undocumented code.