RuboCop

Code QualityLinterRubyDevOpsStatic AnalysisAuto-correctionRuby Style Guide

DevOps Tool

RuboCop

Overview

RuboCop is a Ruby code quality checking tool that enforces adherence to the Ruby Style Guide and maintains code style consistency. It provides auto-correction capabilities and is widely used as the standard quality checking tool for Ruby development. With built-in LSP server, gradual introduction system, and flexible configuration system, it supports everything from legacy projects to new projects. Through a rich ecosystem including Rails extensions, it powerfully supports quality improvement and coding convention unification in Ruby development.

Details

RuboCop is a Ruby-specific static code analysis tool (linter) and code formatter developed and maintained by the Ruby community. Since its release in 2013, it has been established as the de facto standard code quality checking tool in the Ruby ecosystem.

Key Features

  • Ruby Style Guide Compliance: Automatic checking of community Ruby Style Guide
  • Auto-correction: Automatic application of safe correction items (Correctable violations)
  • Cops System: Departmental rule system including Style, Lint, Metrics, Rails, etc.
  • Built-in LSP Server: Language Server Protocol support for editor integration
  • Gradual Introduction: Safe introduction to existing projects through Pending Cops feature
  • Advanced Configuration: Extremely flexible configuration system for customization
  • Extensibility: Support for third-party extensions like Rails extensions
  • Allowlist Generation: Automatic allowlist generation for existing violations
  • Rich Output Formats: Various output formats including JSON, HTML, JUnit, etc.

Cops Department Details

  • Style Cops: Code style unification (indentation, naming conventions, etc.)
  • Lint Cops: Detection of potential bugs and issues
  • Metrics Cops: Measurement of code complexity, method length, etc.
  • Rails Cops: Rails-specific best practices (separate gem required)

Pros and Cons

Pros

  • Standard tool for code style unification and quality improvement in Ruby development
  • Significant reduction in manual correction work through auto-correction features
  • Generation of readable and maintainable code through Ruby Style Guide compliance
  • Systematic and comprehensive code quality checking through Cops system
  • Real-time editor feedback through LSP server integration
  • Safe application to legacy projects through gradual introduction system
  • Specialized checking through rich ecosystem including Rails extensions
  • Flexible response to project requirements through advanced configuration system
  • Automated quality assurance and code review efficiency through CI/CD integration
  • Practical introduction to large projects through allowlist functionality

Cons

  • Complexity of initial setup and rule selection with learning costs
  • Long execution times for large projects
  • Risk of development speed reduction due to overly strict settings
  • Operational burden from massive warnings when applying to legacy code
  • Inability to use unified tools in multilingual projects due to Ruby-only focus
  • Complexity of Cops dependency and version compatibility management
  • Incompleteness of some auto-corrections (manual verification required)
  • Need for discussion and consensus formation regarding coding conventions within teams
  • Additional dependencies from external gems (Rails Cops, etc.)
  • Maintenance burden from configuration file complexity

Reference Links

Code Examples

Installation and Basic Setup

# Basic installation
gem install rubocop

# Add to Gemfile (recommended)
echo 'gem "rubocop", "~> 1.68", require: false' >> Gemfile
bundle install

# Installation with Rails extensions
gem install rubocop rubocop-rails

# Installation in development group
# Gemfile
group :development do
  gem 'rubocop', '~> 1.68', require: false
  gem 'rubocop-rails', '~> 2.27', require: false
  gem 'rubocop-rspec', '~> 3.2', require: false
  gem 'rubocop-performance', '~> 1.23', require: false
end

# Installation via bundler
bundle add rubocop --group=development
bundle add rubocop-rails --group=development

# Version verification
rubocop --version

Basic Execution Methods

# Basic execution
rubocop

# Check specific file
rubocop app/models/user.rb

# Check specific directory
rubocop app/controllers/

# Execute auto-correction
rubocop -a app/

# Execute including unsafe corrections
rubocop -A app/

# Execute only specific Cop
rubocop --only Style/StringLiterals app/

# Exclude specific Cop
rubocop --except Style/Documentation app/

# Detailed output
rubocop --display-style-guide app/

# Progress display
rubocop --progress app/

# Parallel execution
rubocop --parallel app/

# Use cache
rubocop --cache true app/

Configuration File (.rubocop.yml)

# .rubocop.yml - Basic configuration
# Inheritance settings
inherit_from:
  - .rubocop_todo.yml

# Exclusion patterns
AllCops:
  # Target Ruby version
  TargetRubyVersion: 3.2
  
  # Automatically enable new Cops
  NewCops: enable
  
  # Exclusion patterns
  Exclude:
    - 'db/schema.rb'
    - 'db/migrate/*'
    - 'vendor/**/*'
    - 'bin/*'
    - 'node_modules/**/*'
    - 'tmp/**/*'
    - '.git/**/*'
  
  # Include patterns
  Include:
    - '**/*.rb'
    - '**/*.rake'
    - '**/Rakefile'
    - '**/Gemfile'
    - '**/config.ru'

# Style Cops configuration
Style/Documentation:
  Enabled: false

Style/StringLiterals:
  EnforcedStyle: single_quotes

Style/StringLiteralsInInterpolation:
  EnforcedStyle: single_quotes

Style/TrailingCommaInArrayLiteral:
  EnforcedStyleForMultiline: consistent_comma

Style/TrailingCommaInHashLiteral:
  EnforcedStyleForMultiline: consistent_comma

Style/FrozenStringLiteralComment:
  Enabled: true
  EnforcedStyle: always

# Lint Cops configuration
Lint/UnusedMethodArgument:
  AllowUnusedKeywordArguments: true

# Metrics Cops configuration
Metrics/ClassLength:
  Max: 150

Metrics/MethodLength:
  Max: 20
  CountAsOne:
    - 'array'
    - 'hash'
    - 'heredoc'

Metrics/AbcSize:
  Max: 20

Metrics/CyclomaticComplexity:
  Max: 10

Metrics/PerceivedComplexity:
  Max: 10

Metrics/BlockLength:
  Exclude:
    - 'spec/**/*'
    - 'config/routes.rb'
    - 'db/seeds.rb'

# Layout Cops configuration
Layout/LineLength:
  Max: 120
  AllowHeredoc: true
  AllowURI: true
  URISchemes:
    - http
    - https

Layout/MultilineMethodCallIndentation:
  EnforcedStyle: aligned

Layout/HashAlignment:
  EnforcedHashRocketStyle: key
  EnforcedColonStyle: key

Rails-specific Configuration

# .rubocop.yml - Rails configuration
require:
  - rubocop-rails
  - rubocop-rspec
  - rubocop-performance

inherit_from: .rubocop_todo.yml

AllCops:
  TargetRubyVersion: 3.2
  NewCops: enable
  Exclude:
    - 'db/schema.rb'
    - 'db/migrate/*'
    - 'vendor/**/*'
    - 'bin/*'
    - 'node_modules/**/*'

# Rails Cops configuration
Rails/Enabled: true

Rails/HasManyOrHasOneDependent:
  Enabled: true

Rails/InversOf:
  Enabled: true

Rails/Presence:
  Enabled: true

Rails/UniqBeforePluck:
  Enabled: true

Rails/BulkChangeTable:
  Enabled: true

# RSpec Cops configuration
RSpec/ExampleLength:
  Max: 10

RSpec/MultipleExpectations:
  Max: 3

RSpec/NestedGroups:
  Max: 3

RSpec/DescribeClass:
  Enabled: true

# Performance Cops configuration
Performance/Caller:
  Enabled: true

Performance/CaseWhenSplat:
  Enabled: true

Performance/StringReplacement:
  Enabled: true

CI/CD Integration Examples

GitHub Actions

# .github/workflows/rubocop.yml
name: RuboCop

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  rubocop:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup Ruby
      uses: ruby/setup-ruby@v1
      with:
        ruby-version: 3.2
        bundler-cache: true
    
    - name: Install dependencies
      run: bundle install
    
    - name: Run RuboCop
      run: bundle exec rubocop --format github
    
    - name: Run RuboCop with auto-correct (if main branch)
      if: github.ref == 'refs/heads/main'
      run: |
        bundle exec rubocop -a
        if [ -n "$(git status --porcelain)" ]; then
          git config --local user.email "[email protected]"
          git config --local user.name "GitHub Action"
          git add -A
          git commit -m "Auto-fix RuboCop violations" -a
          git push
        fi

GitLab CI

# .gitlab-ci.yml
stages:
  - quality

rubocop:
  stage: quality
  image: ruby:3.2-alpine
  before_script:
    - apk add --no-cache git
    - bundle install --jobs $(nproc) --retry 3
  script:
    - bundle exec rubocop --format json --out rubocop-results.json
    - bundle exec rubocop
  artifacts:
    reports:
      codequality: rubocop-results.json
    paths:
      - rubocop-results.json
    expire_in: 1 week
  only:
    - main
    - develop
    - merge_requests

Editor Integration Configuration

VS Code Configuration

// .vscode/settings.json
{
  "ruby.rubocop.executePath": "bundle exec rubocop",
  "ruby.rubocop.configFilePath": ".rubocop.yml",
  "ruby.rubocop.onSave": true,
  "ruby.format": "rubocop",
  "ruby.lint": {
    "rubocop": {
      "useBundler": true,
      "lint": true,
      "rails": true
    }
  },
  "[ruby]": {
    "editor.defaultFormatter": "misogi.ruby-rubocop",
    "editor.formatOnSave": true,
    "editor.rulers": [120]
  }
}

// RuboCop LSP usage configuration
{
  "ruby.rubocop.useLsp": true,
  "ruby.rubocop.lspConfigurationOptions": {
    "safeAutocorrect": true
  }
}

RubyMine/IntelliJ Configuration

# RubyMine configuration
# File → Settings → Editor → Inspections → Ruby
# Enable RuboCop

# External Tools configuration
# File → Settings → Tools → External Tools
Name: RuboCop
Description: Ruby static code analyzer
Program: bundle
Arguments: exec rubocop $FilePath$
Working Directory: $ProjectFileDir$

# File Watchers configuration
# File Type: Ruby
# Scope: Project Files
# Program: bundle
# Arguments: exec rubocop -a $FilePath$

Gradual Introduction Strategy

TODO File Generation

# Generate TODO file for existing projects
rubocop --auto-gen-config

# Example of generated .rubocop_todo.yml
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2025-01-01 12:00:00 UTC using RuboCop version 1.68.0.

# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.

Style/Documentation:
  Exclude:
    - 'app/controllers/application_controller.rb'
    - 'app/models/user.rb'

Metrics/MethodLength:
  Max: 25  # Goal: 15

Gradual Improvement Script

# scripts/rubocop_gradual.rb
#!/usr/bin/env ruby

require 'yaml'
require 'json'

class RubocopGradualImprovement
  def initialize
    @todo_file = '.rubocop_todo.yml'
    @config = load_todo_config
  end

  def improve_gradually
    @config.each do |cop_name, settings|
      next unless improvable?(cop_name, settings)
      
      puts "Improving #{cop_name}..."
      improve_cop(cop_name, settings)
    end
    
    save_config
  end

  private

  def improvable?(cop_name, settings)
    # Metrics Cops with numerical settings
    cop_name.start_with?('Metrics/') && 
      settings.is_a?(Hash) && 
      settings['Max']
  end

  def improve_cop(cop_name, settings)
    current_max = settings['Max']
    target_max = target_value(cop_name)
    
    if current_max > target_max
      new_max = [current_max - 1, target_max].max
      settings['Max'] = new_max
      puts "  #{cop_name}: #{current_max}#{new_max}"
    end
  end

  def target_value(cop_name)
    targets = {
      'Metrics/MethodLength' => 15,
      'Metrics/ClassLength' => 100,
      'Metrics/AbcSize' => 15,
      'Metrics/CyclomaticComplexity' => 6
    }
    targets[cop_name] || 10
  end

  def load_todo_config
    YAML.load_file(@todo_file) if File.exist?(@todo_file)
  rescue
    {}
  end

  def save_config
    File.write(@todo_file, @config.to_yaml)
  end
end

# Execute
RubocopGradualImprovement.new.improve_gradually if __FILE__ == $0

Advanced Configuration and Customization

Custom Cop Creation

# lib/custom_cops/no_binding_pry.rb
module CustomCops
  class NoBindingPry < RuboCop::Cop::Base
    MSG = 'Remove binding.pry from production code.'

    def on_send(node)
      return unless binding_pry?(node)

      add_offense(node, message: MSG)
    end

    private

    def binding_pry?(node)
      node.receiver&.const_name == 'Kernel' &&
        node.method_name == :binding &&
        node.parent&.method_name == :pry
    end
  end
end
# Load custom Cop in .rubocop.yml
require:
  - './lib/custom_cops/no_binding_pry'

CustomCops/NoBindingPry:
  Enabled: true
  Exclude:
    - 'spec/**/*'

Multi-environment Configuration Management

# .rubocop.yml - Production configuration
production: &production
  AllCops:
    TargetRubyVersion: 3.2
  Metrics/LineLength:
    Max: 100

# .rubocop_development.yml - Development configuration
inherit_from: .rubocop.yml

AllCops:
  NewCops: enable

Style/Documentation:
  Enabled: false

Metrics/MethodLength:
  Max: 25  # More lenient during development
# Environment-specific execution
rubocop --config .rubocop.yml           # Production configuration
rubocop --config .rubocop_development.yml  # Development configuration

Performance Optimization and Troubleshooting

# Performance diagnosis
rubocop --profile

# Parallel execution (speed up)
rubocop --parallel

# Use cache
rubocop --cache true

# Debug output
rubocop --debug app/models/user.rb

# Detailed information for specific Cop
rubocop --show-cops Style/StringLiterals

# Configuration file validation
rubocop --config .rubocop.yml --lint

# Memory usage check
time rubocop --parallel app/