Skip to content
Kotoshu Kotoshu 言修

Documentation

Framework integrations

The Rails/ActiveModel spelling validator, RSpec matchers, rake kotoshu:check, and a Jekyll build gate — four opt-in layers inside the gem, no new runtime dependencies.

The gem ships four integration layers, all opt-in and all inside gem install kotoshu with no new runtime dependencies: a validator for Rails and plain ActiveModel models, RSpec matchers with real failure messages, a Rake task over your repository text files, and a Jekyll generator that fails the build on new spelling errors. Each follows the soft-dependency pattern — the framework it plugs into is required at your side, and a missing one raises a caller-friendly error, never a stack trace at load time.

ActiveModel

Rails & ActiveModel validator

An ActiveModel::EachValidator that adds one validation error per misspelling, with the top suggestion in the message. Rails is not required — standalone ActiveModel models validate the same way. For the validates ... spelling: true short syntax, alias the class constant where Rails resolves it (an initializer or the model file itself); validates_with takes the fully qualified name with no alias.

# Gemfile — activemodel ships with Rails; standalone apps add it themselves
gem "kotoshu"
gem "activemodel"
# app/models/post.rb
SpellingValidator = Kotoshu::Validators::SpellingValidator

class Post < ApplicationRecord
validates :body, spelling: true
validates :summary, spelling: { language: :en, personal_words: %w[kotoshu] }
end

# Without the alias:
# validates_with Kotoshu::Validators::SpellingValidator, attributes: [:body]

Running the model against body = "This is a smal tesst of the validator" and summary = "kotoshu checks the smal things" produces the errors below (executed against ActiveModel 8.1.3.1). Two notes: personal_words accepted kotoshu on the summary while its own misspelling still failed — and validator genuinely is outside this dictionary, which is exactly what personal_words is for:

Body 'smal' is misspelled - did you mean 'small'?
Body 'tesst' is misspelled - did you mean 'test'?
Body 'validator' is misspelled - did you mean 'validation'?
Summary 'smal' is misspelled - did you mean 'small'?'
OptionTakesEffect
languagelanguage codeCheck with one language; unset means auto-detection from the content
personal_wordsarray of wordsWords accepted in addition to the dictionary, compared downcased

The error details carry the data alongside the message: errors.details[:body] holds one { error: :spelling, word:, suggestions: } entry per misspelling, with the top three suggestions — so forms and API serializers can offer fixes without parsing English.

RSpec

RSpec matchers

Two matchers over the real checking pipeline. A bare word goes word-at-a-time through Kotoshu.correct?; a string containing whitespace — or a file via expect_document — runs the full document check. Failure messages list each misspelling with its suggestions, so a red spec tells you what to fix.

# spec/spec_helper.rb
require "kotoshu/rspec"

RSpec.configure do |config|
config.include Kotoshu::Rspec::Matchers
end
expect_words("helo", "world").to all_be_spelled_correctly
expect("helo").not_to be_spelled_correctly
expect("helo wrold").not_to be_spelled_correctly(in: "en")
expect_document("README.adoc").to be_spelled_correctly

What a failing word list actually prints — one "word" (suggestions) pair per misspelling, top three suggestions first:

expected all words to be spelled correctly, but found "helo" (hello, help, hell),
"wrold" (world, wold, weld)

be_spelled_correctly reads as a document check when the target has whitespace — the same matcher, expected the document to be spelled correctly, but found ... — and takes in: for an explicit language instead of auto-detection.

Rake

rake kotoshu:check

One require installs a kotoshu:check task over the repository text files, using the same selection the CLI directory mode uses — known extensions, .gitignore and .ignore honored, .git / node_modules / vendor / target skipped:

# Rakefile
require "kotoshu"      # Bundler apps have the gem loaded already
require "kotoshu/tasks"
$ rake kotoshu:check
.../docs/guide.md: helo -> hello, help, hell
2 file(s) checked, 1 error(s)
rake aborted!
kotoshu: 1 spelling error(s) in 2 file(s)

The task reports one line per misspelling — the walked path, the word, its top suggestions — then the summary; with fail_on_error on (the default) it aborts the task with a nonzero exit, so it slots into a default CI task. Configure or add more tasks with Kotoshu::Tasks::CheckTask:

# Rakefile — configured task, requires the class file instead of the default task
require "kotoshu"
require "kotoshu/tasks/check_task"

Kotoshu::Tasks::CheckTask.new do |t|
t.files = Rake::FileList["app/views/**/*.md"]
t.language = "en"
t.fail_on_error = false    # report without failing the build
t.baseline_path = ".kotoshu-baseline.json"
end
SettingDefaultMeaning
filesrepository selectionA file list to check instead of the walked repository files
rootworking directoryWhere the default selection walks
languageautoFix one language for every file instead of per-file detection
fail_on_errortrueAbort the task (exit 1) when errors are found
baseline_pathoffApply a baseline per file — covered errors pass

Jekyll

Jekyll generator

A safe generator that checks every post and draft and fails the build on new spelling errors. A .kotoshu-baseline.json in the site source — the same file kotoshu baseline init writes — is respected, so existing debt does not block builds while new misspellings still do.

# Gemfile
gem "kotoshu"
gem "jekyll"
# _config.yml
plugins:
- kotoshu
# _plugins/kotoshu.rb — registers the generator
require "kotoshu"
require "kotoshu/jekyll"

A post with two misspellings stops the build (executed against Jekyll 4.4.1):

kotoshu: spelling errors in 3 location(s):
_posts/2026-09-05-first.md: helo -> hello, help, hell
_posts/2026-09-05-first.md: jekyll -> jell, jewell
_posts/2026-09-05-first.md: wrold -> world, wold, weld

All three lines come from one post with two misspellings — the dictionary does not know jekyll either. Freeze that debt and the same build completes (verified): kotoshu baseline init _posts/*.md from the site source, commit .kotoshu-baseline.json, rebuild — the generator reads the baseline from the site source at build time and passes.

The wider net

At the commit and in CI

Two more integration points ship with the ecosystem and are documented on their own pages: the pre-commit hook — - repo: https://github.com/kotoshu/kotoshu, id: kotoshu — checks staged prose files before the commit lands; and the GitHub Action wraps the same CLI for CI with SARIF in the Security tab. The CLI reference covers directory mode — kotoshu check . walking a tree the same way the Rake task does.

Every behavior on this page was executed against the gem source — the validator under ActiveModel 8.1.3.1, the matchers under RSpec, the Rake task in a scratch repository, the generator under Jekyll 4.4.1 including the baseline unblock — with terminal paths shortened; the option tables read from lib/kotoshu/validators, /rspec, /tasks and /jekyll. CLI reference · gem CHANGELOG