Overview
YARD (Yet Another Ruby Documenter) generates documentation from Ruby source code by parsing special comment blocks and method signatures. The tool extracts documentation from comments written in a structured format and produces HTML, PDF, or other output formats.
The core YARD system consists of several key components. The YARD::Parser
reads Ruby source files and builds an abstract syntax tree. The YARD::CodeObjects
namespace contains classes representing Ruby constructs like methods, classes, and modules. The YARD::Registry
stores all parsed objects in memory during processing. The YARD::Server
provides a web interface for browsing documentation.
YARD processes documentation tags within comment blocks. Tags start with @
symbols and provide structured metadata about code elements. The @param
tag documents method parameters, while @return
describes return values. The @example
tag includes code demonstrations.
# Calculate compound interest
# @param principal [Float] the initial amount
# @param rate [Float] annual interest rate (as decimal)
# @param years [Integer] number of years
# @return [Float] the final amount after compound interest
def compound_interest(principal, rate, years)
principal * (1 + rate) ** years
end
YARD integrates with Ruby's introspection capabilities. The parser examines method signatures, class hierarchies, and module inclusions to generate comprehensive documentation without requiring explicit documentation for every code element.
class BankAccount
# @!attribute [rw] balance
# @return [Float] current account balance
attr_accessor :balance
# Initialize a new account
# @param initial_balance [Float] starting balance
def initialize(initial_balance = 0.0)
@balance = initial_balance
end
end
The tool supports multiple output formats including HTML, PDF, and plain text. The HTML output includes search functionality, cross-references, and navigation trees. YARD generates documentation hierarchies that mirror the structure of the source code.
Basic Usage
YARD operates through the yard
command-line tool or programmatic API. The command-line interface processes directories of Ruby files and generates documentation output.
The basic workflow involves running yard doc
in a project directory. YARD searches for Ruby files recursively and processes documentation comments:
# Run in project root directory
# yard doc
# Generate documentation for specific files
# yard doc lib/**/*.rb
# Specify output directory
# yard doc --output-dir docs/api
YARD recognizes several essential documentation tags. The @param
tag documents method parameters with type information and descriptions:
class FileProcessor
# Process a file with specified options
# @param filename [String] path to the file
# @param options [Hash] processing options
# @option options [Boolean] :verbose (false) enable verbose output
# @option options [String] :encoding ('utf-8') file encoding
# @return [Hash] processing results
def process_file(filename, options = {})
encoding = options[:encoding] || 'utf-8'
content = File.read(filename, encoding: encoding)
puts "Processing #{filename}" if options[:verbose]
{
filename: filename,
size: content.length,
lines: content.lines.count
}
end
end
The @example
tag demonstrates method usage with code samples. YARD renders examples with syntax highlighting:
class StringUtils
# Convert string to title case
# @param text [String] input text
# @return [String] title-cased text
# @example Basic usage
# StringUtils.titleize("hello world")
# #=> "Hello World"
# @example With punctuation
# StringUtils.titleize("ruby-on-rails")
# #=> "Ruby On Rails"
def self.titleize(text)
text.split(/[\s_-]/).map(&:capitalize).join(' ')
end
end
YARD supports class-level documentation with @!attribute
directives for documenting attr_accessor, attr_reader, and attr_writer declarations:
class Configuration
# Application configuration management
# @!attribute [rw] database_url
# @return [String] database connection URL
# @!attribute [rw] api_timeout
# @return [Integer] API request timeout in seconds
attr_accessor :database_url, :api_timeout
# @!attribute [r] version
# @return [String] current application version
attr_reader :version
def initialize
@version = "1.0.0"
@api_timeout = 30
end
end
The @see
tag creates cross-references between related code elements. YARD automatically generates links to referenced methods, classes, and external resources:
class UserManager
# Create a new user account
# @param attributes [Hash] user attributes
# @return [User] the created user
# @see #update_user for modifying existing users
# @see User#validate for validation rules
def create_user(attributes)
user = User.new(attributes)
user.save if user.validate
user
end
# Update existing user account
# @param user [User] user to update
# @param attributes [Hash] new attributes
# @return [Boolean] success status
# @see #create_user for creating new users
def update_user(user, attributes)
user.update(attributes)
end
end
Advanced Usage
YARD provides sophisticated customization through plugins, templates, and programmatic APIs. The plugin system extends YARD's functionality with additional tag types, parsers, and output formats.
Custom tags define domain-specific documentation metadata. The YARD::Tags::Library
registers new tag types with parsing logic:
# Define custom tags in .yardopts or initialization file
YARD::Tags::Library.define_tag("API Version", :api_version, :text)
YARD::Tags::Library.define_tag("Since", :since, :text)
YARD::Tags::Library.define_tag("Deprecated", :deprecated, :text)
class ApiController
# Retrieve user information
# @param user_id [Integer] unique user identifier
# @return [Hash] user data
# @api_version 2.1
# @since 1.0.0
# @deprecated Use #get_user_profile instead
def get_user(user_id)
# implementation
end
end
Template customization modifies YARD's HTML output. Templates use ERB syntax and access YARD's object model to generate custom documentation layouts:
# Custom template in templates/default/layout/html/setup.rb
def stylesheets
super + %w(custom.css)
end
def javascripts
super + %w(custom.js)
end
# Override method signature rendering
def signature(object)
if object.type == :method && object.parameters.any?
params = object.parameters.map { |param|
"<span class='param'>#{param.first}</span>"
}.join(', ')
"<div class='signature'>#{object.name}(#{params})</div>"
else
super
end
end
The programmatic API processes Ruby code and extracts documentation programmatically. The YARD::Registry
provides access to all parsed objects:
require 'yard'
# Parse files programmatically
YARD.parse(['lib/**/*.rb'])
# Access parsed objects
registry = YARD::Registry.all
methods = registry.select { |obj| obj.type == :method }
# Generate custom reports
methods.each do |method|
if method.tag(:deprecated)
puts "Deprecated method: #{method.path}"
puts " Reason: #{method.tag(:deprecated).text}"
end
if method.parameters.length > 5
puts "Method with many parameters: #{method.path}"
puts " Parameter count: #{method.parameters.length}"
end
end
# Custom documentation processing
undocumented_methods = methods.select { |m|
m.docstring.blank? && m.visibility == :public
}
puts "Undocumented public methods: #{undocumented_methods.length}"
YARD supports namespace documentation with @!group
directives. Groups organize related methods within class documentation:
class DatabaseConnection
# @!group Connection Management
# Establish database connection
# @return [Boolean] connection success
def connect
# implementation
end
# Close database connection
# @return [Boolean] disconnection success
def disconnect
# implementation
end
# @!endgroup
# @!group Query Operations
# Execute SQL query
# @param sql [String] SQL statement
# @return [Array] query results
def execute(sql)
# implementation
end
# Prepare SQL statement
# @param sql [String] SQL statement
# @return [PreparedStatement] prepared statement
def prepare(sql)
# implementation
end
# @!endgroup
end
Macros define reusable documentation patterns. The @!macro
tag creates documentation templates that can be applied to multiple code elements:
# @!macro [new] cached_method
# @note This method result is cached for performance
# @return [$2] $1
# @see #clear_cache for cache management
class DataService
# @!macro cached_method
# @param user_id [Integer] user identifier
# Retrieve user profile information
# [Hash] user profile data
def get_user_profile(user_id)
# implementation with caching
end
# @!macro cached_method
# @param query [String] search query
# Search for users matching criteria
# [Array<Hash>] matching user records
def search_users(query)
# implementation with caching
end
end
Production Patterns
YARD integrates with continuous integration systems and deployment pipelines to maintain up-to-date documentation. Automated documentation generation ensures consistency between code changes and documentation updates.
CI integration typically involves adding documentation generation to build processes. The process validates documentation completeness and generates artifacts:
# GitHub Actions example (.github/workflows/docs.yml)
name: Generate Documentation
on: [push, pull_request]
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.1
bundler-cache: true
- name: Generate documentation
run: |
bundle exec yard doc --fail-on-warning
bundle exec yard stats --list-undoc
- name: Upload documentation
uses: actions/upload-artifact@v2
with:
name: documentation
path: doc/
Documentation hosting requires web server configuration for static HTML files. The generated documentation includes search functionality that requires proper MIME type configuration:
# Rack-based documentation server
require 'rack'
require 'rack/static'
class DocumentationServer
def initialize(doc_root)
@doc_root = doc_root
@static_app = Rack::Static.new(nil,
urls: ["/"],
root: doc_root,
index: 'index.html'
)
end
def call(env)
# Handle YARD's search functionality
if env['PATH_INFO'].end_with?('.js')
env['HTTP_ACCEPT'] = 'application/javascript'
end
@static_app.call(env)
end
end
# config.ru
run DocumentationServer.new('doc')
Large codebases require documentation organization strategies. YARD supports multiple project documentation through readme files and custom landing pages:
# .yardopts configuration file
--readme README.md
--markup markdown
--charset utf-8
--embed-mixins
--private
lib/**/*.rb
- CHANGELOG.md
- LICENSE
Documentation quality metrics track coverage and completeness. Custom YARD plugins generate documentation reports:
# Custom documentation coverage plugin
class DocumentationCoverage
def self.analyze(threshold = 80)
YARD.parse(Dir['lib/**/*.rb'])
all_methods = YARD::Registry.all(:method)
public_methods = all_methods.select { |m| m.visibility == :public }
documented_methods = public_methods.select { |m| !m.docstring.blank? }
coverage = (documented_methods.length.to_f / public_methods.length) * 100
report = {
total_methods: public_methods.length,
documented_methods: documented_methods.length,
coverage_percentage: coverage.round(2),
meets_threshold: coverage >= threshold
}
# Generate detailed report
undocumented = public_methods - documented_methods
report[:undocumented] = undocumented.map(&:path)
report
end
end
# Generate coverage report
report = DocumentationCoverage.analyze(85)
puts "Documentation coverage: #{report[:coverage_percentage]}%"
unless report[:meets_threshold]
puts "Warning: Documentation below threshold"
report[:undocumented].each do |method_path|
puts " Missing documentation: #{method_path}"
end
end
Multi-project documentation requires coordination between related codebases. YARD supports external project linking:
# .yardopts for main project
--markup markdown
--external-links 'shared_lib:http://docs.example.com/shared_lib/'
--external-links 'utilities:http://docs.example.com/utilities/'
class MainService
# Process data using shared library
# @param data [Array] input data
# @return [Hash] processed results
# @see SharedLib::DataProcessor#process for processing details
# @see Utilities::Validator#validate for validation logic
def process_data(data)
validated = Utilities::Validator.validate(data)
SharedLib::DataProcessor.process(validated)
end
end
Documentation deployment automation handles versioning and archive management:
# Documentation deployment script
class DocumentationDeployer
def initialize(version, deploy_path)
@version = version
@deploy_path = deploy_path
@doc_path = 'doc'
end
def deploy
# Generate fresh documentation
system('bundle exec yard doc')
# Create versioned directory
version_path = File.join(@deploy_path, @version)
FileUtils.mkdir_p(version_path)
# Copy documentation
FileUtils.cp_r(Dir["#{@doc_path}/*"], version_path)
# Update latest symlink
latest_path = File.join(@deploy_path, 'latest')
FileUtils.rm_f(latest_path) if File.exist?(latest_path)
FileUtils.ln_s(@version, latest_path)
# Generate version index
generate_version_index
end
private
def generate_version_index
versions = Dir[File.join(@deploy_path, '*')].map { |d| File.basename(d) }
.select { |v| v != 'latest' && v != 'index.html' }
.sort_by { |v| Gem::Version.new(v) }.reverse
index_html = <<~HTML
<html>
<head><title>Documentation Versions</title></head>
<body>
<h1>Available Documentation Versions</h1>
<ul>
#{versions.map { |v| "<li><a href='#{v}/'>#{v}</a></li>" }.join}
</ul>
</body>
</html>
HTML
File.write(File.join(@deploy_path, 'index.html'), index_html)
end
end
Error Handling & Debugging
YARD encounters various parsing and generation errors during documentation processing. Understanding common error patterns and debugging techniques improves documentation reliability.
Parse errors occur when YARD cannot understand Ruby syntax or documentation formatting. The parser reports syntax errors with file locations and line numbers:
# Problematic code causing parse errors
class BadExample
# Missing closing bracket in @param tag
# @param data [Hash incomplete type annotation
# @return [String] processed data
def process_data(data)
# Implementation
end
# Invalid tag syntax
# @@invalid_double_at_tag this causes parsing issues
# @param without_type_info missing type brackets
def problematic_method(param)
# Implementation
end
end
Debug parsing issues using YARD's verbose output and parse tree inspection:
# Enable verbose parsing output
# yard doc --verbose
# Debug specific files
require 'yard'
# Parse with debug information
YARD::Logger.instance.level = Logger::DEBUG
YARD.parse(['lib/problematic_file.rb'])
# Inspect parsing results
registry = YARD::Registry.all
registry.each do |object|
if object.file && object.file.include?('problematic_file.rb')
puts "Object: #{object.path}"
puts " Type: #{object.type}"
puts " Tags: #{object.tags.map(&:tag_name)}"
puts " Errors: #{object.errors}" if object.respond_to?(:errors)
end
end
Documentation validation catches incomplete or malformed documentation. Custom validation rules identify documentation problems:
class DocumentationValidator
def self.validate_project
YARD.parse(Dir['lib/**/*.rb'])
errors = []
YARD::Registry.all(:method).each do |method|
next unless method.visibility == :public
# Check for missing documentation
if method.docstring.blank?
errors << "#{method.path}: Missing documentation"
next
end
# Validate parameter documentation
method.parameters.each do |param|
param_name = param.first.gsub(/[*&]/, '')
param_tag = method.tag(:param, param_name)
unless param_tag
errors << "#{method.path}: Missing @param for #{param_name}"
end
end
# Check for return documentation
unless method.tag(:return) || method.name.to_s.end_with?('?', '!')
errors << "#{method.path}: Missing @return tag"
end
# Validate example syntax
method.tags(:example).each do |example|
begin
# Basic Ruby syntax validation
RubyVM::InstructionSequence.compile(example.text)
rescue SyntaxError => e
errors << "#{method.path}: Invalid example syntax: #{e.message}"
end
end
end
errors
end
end
# Run validation
errors = DocumentationValidator.validate_project
if errors.any?
puts "Documentation validation errors:"
errors.each { |error| puts " #{error}" }
exit 1
end
Template rendering errors occur when custom templates contain invalid ERB syntax or reference non-existent objects. Debug template issues by examining the template resolution process:
# Debug template rendering
require 'yard'
require 'yard/server'
class TemplateDebugger
def self.debug_template(template_name)
# Initialize YARD server to access template system
server = YARD::Server::RackAdapter.new(nil)
template = server.adapter.template(template_name)
puts "Template path: #{template.path}"
puts "Template file: #{template.file}"
# Check template dependencies
template.class.ancestors.each do |ancestor|
puts "Inherits from: #{ancestor}" if ancestor.respond_to?(:path)
end
# Validate ERB syntax
begin
ERB.new(template.erb).result
puts "ERB syntax: Valid"
rescue Exception => e
puts "ERB syntax error: #{e.message}"
end
end
end
Memory issues arise when processing large codebases. YARD loads all parsed objects into memory, potentially causing out-of-memory errors:
# Monitor memory usage during parsing
class MemoryProfiler
def self.profile_parsing(file_patterns)
require 'objspace'
ObjectSpace.trace_object_allocations_start
initial_memory = `ps -o rss= -p #{Process.pid}`.to_i
puts "Initial memory: #{initial_memory} KB"
YARD.parse(file_patterns)
final_memory = `ps -o rss= -p #{Process.pid}`.to_i
registry_size = YARD::Registry.all.size
puts "Final memory: #{final_memory} KB"
puts "Memory increase: #{final_memory - initial_memory} KB"
puts "Objects parsed: #{registry_size}"
puts "Memory per object: #{(final_memory - initial_memory) / registry_size} KB"
# Analyze object allocations
allocations = ObjectSpace.dump_all(output: StringIO.new).string
puts "Total allocations: #{allocations.lines.count}"
end
end
Configuration errors prevent YARD from locating files or applying correct settings. The .yardopts
file contains command-line options that can conflict or reference non-existent files:
# Validate .yardopts configuration
class ConfigurationValidator
def self.validate_yardopts(opts_file = '.yardopts')
return unless File.exist?(opts_file)
options = File.readlines(opts_file).map(&:strip)
.reject { |line| line.empty? || line.start_with?('#') }
errors = []
file_patterns = []
options.each do |option|
case option
when /^--/
# Validate known options
unless YARD::CLI::YardDoc.default_options.key?(option.gsub(/^--/, '').tr('-', '_').to_sym)
errors << "Unknown option: #{option}"
end
when /^-/
# Short options validation would go here
else
# File pattern or exclusion
if option.start_with?('-')
exclusion = option[1..-1]
unless File.exist?(exclusion) || Dir.glob(exclusion).any?
errors << "Excluded file/pattern not found: #{exclusion}"
end
else
file_patterns << option
end
end
end
# Validate file patterns
file_patterns.each do |pattern|
unless Dir.glob(pattern).any?
errors << "No files match pattern: #{pattern}"
end
end
errors
end
end
Reference
Core Classes and Modules
Class/Module | Purpose | Key Methods |
---|---|---|
YARD::Parser |
Source code parsing | #parse , #parse_string |
YARD::Registry |
Object storage and retrieval | .all , .at , .paths |
YARD::CodeObjects::Base |
Base class for code elements | #docstring , #tag , #tags |
YARD::CodeObjects::MethodObject |
Method representation | #parameters , #signature , #visibility |
YARD::CodeObjects::ClassObject |
Class representation | #superclass , #mixins , #constants |
YARD::Server |
Documentation web server | #start , #stop |
YARD::CLI::YardDoc |
Command-line interface | #run , #parse_options |
Documentation Tags
Tag | Syntax | Description |
---|---|---|
@param |
@param name [Type] description |
Documents method parameters |
@return |
@return [Type] description |
Documents return value |
@yield |
@yield [params] description |
Documents block parameters |
@yieldparam |
@yieldparam name [Type] description |
Documents individual block parameters |
@yieldreturn |
@yieldreturn [Type] description |
Documents block return value |
@example |
@example Title\n code |
Provides usage examples |
@see |
@see Class#method |
Creates cross-references |
@since |
@since version |
Documents version introduced |
@deprecated |
@deprecated reason |
Marks deprecated elements |
@author |
@author name |
Documents code author |
@version |
@version number |
Documents version information |
@note |
@note text |
Adds additional notes |
@todo |
@todo task |
Documents pending tasks |
@raise |
@raise [ExceptionType] description |
Documents exceptions |
Attribute Directives
Directive | Syntax | Description |
---|---|---|
@!attribute |
@!attribute [rw] name |
Documents attr_accessor/reader/writer |
@!method |
@!method name |
Documents dynamically defined methods |
@!group |
@!group name |
Groups related methods |
@!endgroup |
@!endgroup |
Ends method group |
@!macro |
@!macro [new] name |
Defines reusable documentation |
@!scope |
@!scope class |
Sets method scope (class/instance) |
@!visibility |
@!visibility private |
Sets method visibility |
Command-Line Options
Option | Description | Example |
---|---|---|
--output-dir |
Specify output directory | yard doc --output-dir docs |
--readme |
Specify readme file | yard doc --readme README.md |
--markup |
Set markup format | yard doc --markup markdown |
--template |
Use custom template | yard doc --template custom |
--fail-on-warning |
Exit with error on warnings | yard doc --fail-on-warning |
--exclude |
Exclude file patterns | yard doc --exclude spec/**/* |
--embed-mixins |
Include mixin methods | yard doc --embed-mixins |
--private |
Include private methods | yard doc --private |
--protected |
Include protected methods | yard doc --protected |
--no-cache |
Disable caching | yard doc --no-cache |
Registry Query Methods
Method | Parameters | Returns | Description |
---|---|---|---|
YARD::Registry.all(type) |
type (Symbol, optional) |
Array<CodeObject> |
Returns all objects or filtered by type |
YARD::Registry.at(path) |
path (String) |
CodeObject or nil |
Finds object by path |
YARD::Registry.paths(reload) |
reload (Boolean) |
Array<String> |
Returns all object paths |
YARD::Registry.resolve(object, name) |
object (CodeObject), name (String) |
CodeObject or nil |
Resolves name in object context |
YARD::Registry.clear |
None | void |
Clears registry contents |
Parser Configuration
Method | Parameters | Returns | Description |
---|---|---|---|
YARD.parse(paths, excluded) |
paths (Array), excluded (Array) |
void |
Parse files and populate registry |
YARD.parse_string(content, filename) |
content (String), filename (String) |
Array<CodeObject> |
Parse string content |
YARD::Parser::SourceParser.parse(paths) |
paths (Array or String) |
void |
Parse specified paths |
YARD::Parser::SourceParser.parse_string(content) |
content (String) |
Array<CodeObject> |
Parse string as Ruby code |
Server Configuration
Method | Parameters | Returns | Description |
---|---|---|---|
YARD::Server.new(adapter, options) |
adapter (Symbol), options (Hash) |
Server |
Create server instance |
#start(port) |
port (Integer) |
void |
Start server on port |
#stop |
None | void |
Stop running server |
#add_library(name, paths) |
name (String), paths (Array) |
void |
Add library to server |
Error Classes
Class | Inherits From | Description |
---|---|---|
YARD::Parser::ParserSyntaxError |
RuntimeError |
Syntax parsing errors |
YARD::Parser::UndocumentableError |
RuntimeError |
Undocumentable code structures |
YARD::TemplateNotFoundError |
RuntimeError |
Missing template files |
YARD::Server::LibraryNotPreparedError |
RuntimeError |
Unprepared library access |