rubocop-kata

A RuboCop plugin that replaces a pile of linter gems and config with one dependency. Install it and your .rubocop.yml shrinks to project-specific overrides. Everything else comes from the gem: a curated stack of RuboCop extensions, opinionated style defaults, and fifteen house cops.

Kata/GoodMethodName asks for one word per method name. A second word is allowed only when it is not hiding a missing object: a role prefix (after_fork), a role suffix (file_of), or a compound noun you have added to Terms on purpose. The wrong way to satisfy the cop is matching_idsmatchingids: if you need two words, that is a modelling smell — the concept is missing, or the method belongs on a different object. Kata/RealWords catches that dodge with a shipped dictionary instead of a word list you maintain: matchingids is not a word, so it fails with zero configuration — and so does cfg.

  • One dependency. Bundles rubocop-rspec, rubocop-performance, rubocop-elegant, rubocop-packaging, and rubocop-thread_safety behind a single gem.
  • Opinionated defaults. Methods under 5 lines, classes under 100, 4 parameters max, 120-column lines, double quotes, NewCops: enable. See config/default.yml.
  • Fifteen house cops. Naming, dependency discipline, and data honesty in the Elegant Objects spirit — from Kata/AgentNoun to Kata/ClockDiscipline.
class PaymentProcessor  # Kata/AgentNoun: `PaymentProcessor` names a doer; name the class
end                     #   for the thing it is, not the work it does. Try `Payment`.

class Payment           # OK
end

It suggests a name only when it can derive one: a compound minus its agent word, or a regular -ator/-ector/-isor/-izer noun the shipped dictionary confirms (SelectorSelection, SynthesizerSynthesis).

Getting started

Add the gem to your Gemfile:

gem "rubocop-kata", group: :development, require: false

Install it and point .rubocop.yml at the plugin:

plugins:
  - rubocop-kata

AllCops:
  TargetRubyVersion: 3.4
bundle install
bundle exec rubocop

That's it. The plugin loads the bundled extensions and the shared defaults, so your .rubocop.yml keeps only what's specific to your project. Requires Ruby >= 3.4 and RuboCop ~> 1.75.

The bundled rubocop-elegant

The released rubocop-elegant 0.7.1 has four defects this project reported upstream: NoRedundantVariable autocorrect corrupts Ruby 3.1 shorthand (#74), ClassInModule reports a class nested in a class as global (#75), PairedBrackets autocorrect drifts the indent (#76), and the test-file exclusions never match a Rails or RSpec suite (#77).

The defaults here neutralise all four. Elegant/ClassInModule is off, because its offences cannot be cleared: the cop it ships beside forbids the module it asks for. The two broken correctors are set to report only, because their offences can be cleared, by hand. The exclusions are widened. Nothing corrupts your code out of the box, and nothing reports a defect you cannot fix.

A gemspec cannot name a git source, so to take the fixes themselves rather than the workarounds, add this to your own Gemfile:

gem "rubocop-elegant", github: "giacope/rubocop-elegant", branch: "fixes"

That branch is the released gem plus the five pull requests, with the version pinned so Bundler resolves it. With it in place you can turn the two correctors back on:

Elegant/NoRedundantVariable:
  AutoCorrect: true

Elegant/PairedBrackets:
  AutoCorrect: true

Elegant/ClassInModule:
  Enabled: true

Drop all three once upstream releases the fixes. On a Rails codebase, consider leaving Elegant/ClassInModule off for good: it wants every class inside a module, and Zeitwerk resolves class Account from app/models/account.rb as a top-level constant by design.

The cops

Cop Default What it enforces
Kata/AgentNoun on Classes named for what they are, not -er/-or doers. Suggests the better name when it can derive one. AllowedNames matches a whole name or a trailing segment, so Error covers UsageError.
Kata/NoComments on No prose comments; say it in the code. Magic comments, linter directives, and licence headers survive. Autocorrects.
Kata/IoDiscipline on No bare puts/warn/pp/p outside the test suite and the boot layer; write through an injected @io or an explicit receiver.
Kata/ProsePlacement off Sentence-length strings belong in the presentation layer. Enable with an Include/Exclude matching your layering.
Kata/NoUtilName on No junk-drawer names (Util, Helper, Manager, Service, …). Tune via BannedNames.
Kata/RealWords on Every name segment is a word the shipped dictionary knows — errorcount (smash) and cfg (abbreviation) both fail, with no word list to maintain. Tune via Terms/BannedWords/AllowedNames.
Kata/GoodMethodName on One word per method name; a second word needs a role prefix (after_fork), a role suffix (file_of), or a reviewed Terms entry. Tune via MaxWords/Prefixes/Suffixes/Terms/AllowedNames.
Kata/GoodVariableName on The same rule for locals, parameters, ivars, class variables, and globals; _name and @_name stay exempt.
Kata/BuilderNoun on Builders named for what they return: total, not calculate_total. Tune via BannedPrefixes/AllowedNames.
Kata/NoBooleanFlag on No positional boolean arguments; split the method or use a keyword.
Kata/ConstructorDiscipline on initialize assigns, raises, or freezes — never computes.
Kata/NoClassMethodLogic on Class methods construct (build, parse, of, from_*); instances do the work.
Kata/NoHashAsObject on A hash with MaxKeys+ keys (default 4) wants to be an object. Keyword-argument call sites exempt.
Kata/ClockDiscipline on No bare Time.now/Date.today/.current; inject a clock.
Kata/EnvDiscipline on ENV reads only in the boot layer (config/, db/seeds*, lib/tasks/, rake files, bin/, exe/) — or as a parameter default, which is the seam the cop asks for.

Dead configuration

doctor reports configuration that no longer does anything: a .rubocop.yml entry for a cop the inherited configuration disables, and a rubocop:disable/enable comment naming a cop that is not enabled where the comment sits. RuboCop reports neither.

bundle exec rubocop-kata doctor          # or: doctor path/to/project
.rubocop.yml:20: `Elegant/GoodMethodName` is disabled by the configuration this project inherits; the entry does nothing.
lib/registry.rb:44: the directive names `Elegant/GoodMethodName`, which is not enabled here; the comment does nothing.
2 dead entries

It exits non-zero when it finds something, so it can gate CI.

Adoption order

Kata's structural cops create names and its naming cops charge for them, so a structural refactor removes offenses and adds more. A single total cannot tell progress from regression. plan buckets the backlog into the stages that cause each other and names the one to take next.

bundle exec rubocop-kata plan            # or: plan path/to/project
structure     184  Kata/ConstructorDiscipline 92, Kata/NoHashAsObject 48, Kata/EnvDiscipline 44
naming        263  Kata/RealWords 141, Kata/GoodVariableName 88, Kata/AgentNoun 34
prose          57  Kata/NoComments 57
rest          412  Elegant/PairedBrackets 300, Layout/LineLength 112
next: structure — 184 offenses in 61 files; these mint the names `naming` then prices, so take them first
densest: app/models/account.rb (14)

Structure before naming, because doing naming first means renaming things the structural pass is about to move.

The gem also ships an agent skill that runs this loop: it works the stage plan names, checks every name it introduces against the same dictionary Kata/RealWords reads, and reports removed and created separately instead of a net total.

mkdir -p .claude/skills/rubocop-kata
bundle exec rubocop-kata skill > .claude/skills/rubocop-kata/SKILL.md

It writes to stdout, so the same command installs it anywhere an agent reads skills from — a project, ~/.claude/skills/, or a plugin.

The defaults

Double-quoted strings, Metrics/MethodLength: 5, Metrics/ClassLength: 100, Metrics/ParameterLists: 4, 120-column lines, endless methods on one line, rescue => error, NewCops: enable, heredocs counted as one line in spec examples, and no inline rubocop:disable comments (Style/DisableCopsWithinSourceCodeDirective). See config/default.yml.

License

The code is MIT — see LICENSE.txt. The shipped dictionaries are third-party data under their own terms:

  • data/words.txt.gz — a modified subset of SCOWL en-US size 60: filtered, deduplicated, and gzipped. SCOWL is the collective work of Kevin Atkinson and the contributors named in data/SCOWL-COPYRIGHT, which is distributed with this gem and reproduced verbatim. It includes, among others, Copyright 2000–2018 Kevin Atkinson; WordNet 1.6 Copyright 1997 by Princeton University, all rights reserved; and Copyright 1993 Geoff Kuenning, Granada Hills, CA, all rights reserved. Princeton University makes no representations or warranties, express or implied, as to this database, and its name may not be used in advertising or publicity pertaining to this distribution.
  • data/software.txt.gz — built from the software-terms, ruby, and shell dictionaries of cspell-dicts, each MIT-licensed. Copyright (c) 2017–2025 Street Side Software; the notice and permission text are reproduced in data/CSPELL-LICENSE.
  • data/supplement.txt — this project's own additions, MIT.