CrackedRuby logo

CrackedRuby

YARD

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