Code in front, context behind.
The code is the omote (front). Behind it sits an Excalidraw drawing, the ura (back): diagrams, notes and the reasons behind the code, with shapes that link to the tests and methods they describe.
Status: early. The gems are on RubyGems and the extension is on the Visual Studio Marketplace. Until 1.0, minor versions may change how things work; CHANGELOG.md says what changed.
Features
Drawn by You
Omoteura never generates diagrams. Draw them in Obsidian's Excalidraw plugin or on excalidraw.com, the way you would sketch on a whiteboard, and drop the file next to your code.
Linked by Name
A shape's link names a spec description, a method or the start of a statement, not a line number, so links survive the edits around them.
Checked in CI
omoteura check fails the build when a spec is renamed and a diagram still
points at the old name, or when a diagram is left behind by a moved file.
Hands Off Your Code
Omoteura only reads files. It runs from the command line and next to RSpec, never inside your application, never rewrites your code and does nothing at all where there is no diagram.
Light
Omoteura reads every Ruby file in a project, so it is measured for allocations as well as time. Files without a magic comment are skipped without allocating.
Supported Versions
-
Ruby 3.4, 4.0
-
Rails 7.1, 7.2, 8.0, 8.1 projects; Omoteura reads their files and never loads Rails
-
RSpec 3.12+ or Minitest 5.21+
-
Excalidraw (
.excalidraw) and Obsidian Excalidraw (.excalidraw.md)
Installation
Put this in your Gemfile:
group :development, :test do
gem "omoteura", require: false
endrequire: false keeps Rails from loading Omoteura into your application; the
omoteura command and your test framework load what they need. Then bundle:
% bundleomoteura installs the following gems. You can also pick only the ones you need.
| gem | contents |
|---|---|
omoteura-core |
Diagram files, omoteura: links and the omoteura command |
omoteura-rspec |
A formatter that records RSpec results for diagrams |
omoteura-minitest |
A reporter that records Minitest results for diagrams |
Attaching Diagrams
By Path
Put the diagram under ura/ at the same path as the code. Nothing else is
needed.
| code | diagram |
|---|---|
app/models/user.rb |
ura/app/models/user.excalidraw.md |
spec/models/user_spec.rb |
ura/spec/models/user_spec.excalidraw |
When both .excalidraw.md and .excalidraw exist, .excalidraw.md is used.
By Magic Comment
To share one diagram between files or give it any name, write a magic comment on its own line at the top or the bottom of the file:
class User < ApplicationRecord
end
# omoteura: ura/shared/user-validations.excalidraw.mdA path copied from Obsidian without its .md works too.
By .omoteura.yml
To leave the code untouched, map files in .omoteura.yml:
attach:
app/models/user.rb: ura/shared/user-validations.excalidraw.mdA magic comment wins over .omoteura.yml, which wins over the path.
Drafting a Diagram
A blank page is the hardest part, so Omoteura can draft a diagram to draw on:
% bundle exec omoteura draft spec/models/user_spec.rb
wrote ura/spec/models/user_spec.excalidraw (4 shapes)A spec gets a column per example group and a box per example; other code gets a column per kind of declaration (validations, associations, callbacks, scopes, enums) and one for public methods. Every box already links to its code, so test results show up straight away. Then make it yours: move, delete, annotate.
A draft never replaces a diagram. When the file already has one, it goes beside
it as user_spec.draft.excalidraw for you to copy from, and omoteura check
reminds you to delete it. --format md writes an Obsidian .excalidraw.md
note instead, and --dry-run, --only methods,validations and --out DIR
are available too.
As the code grows, --merge adds what the diagram has no shape for yet, in
columns to the right of everything drawn. A shape counts as drawn when its
link points at the same code or its label is the same, and nothing drawn is
moved or removed:
% bundle exec omoteura draft spec/models/user_spec.rb --merge
added 2 shapes to ura/spec/models/user_spec.excalidrawLinking Shapes to Code
Set a shape's link to text starting with omoteura:. Both Excalidraw and
Obsidian let you edit a shape's link.
| link | points to |
|---|---|
omoteura:#email is unique |
the spec whose description ends with "email is unique", in the attached file |
omoteura:#display_name |
the describe "#display_name" group, or the method |
omoteura:spec/models/user_spec.rb#validations email is unique |
the same, in another file |
omoteura:app/models/user.rb#validates :email |
the statement that starts with that text |
omoteura:app/models/user.rb |
the whole file |
A fragment is tried as a spec description, then a method name, then the start of a statement. A link that matches nothing, or more than one place, is broken.
Checking Links
% bundle exec omoteura check
ura/app/models/user.excalidraw: omoteura:#full_name matches nothing in app/models/user.rb (shape "Display name")
Checked 12 diagrams and 48 links: 1 problem.It reports broken links, missing or unreadable diagrams and diagrams no code
file is attached to, and exits with status 1 when there are any. In GitHub
Actions, add --format github to see the problems as annotations.
Recording Test Results
With RSpec, add the formatter to .rspec, next to your usual one:
--format progress
--format Omoteura::RSpec::Formatter --out tmp/omoteura/results.json
With Minitest, including Rails' bin/rails test, require the reporter in
test/test_helper.rb:
require "omoteura/minitest"Each test's status and failure message go to tmp/omoteura/results.json
(for Minitest, OMOTEURA_RESULTS names another path). Keep tmp/omoteura/
out of version control.
Minitest tests link like RSpec examples: in a test class such as
UserTest < ActiveSupport::TestCase, test "requires an email" is
omoteura:#requires an email, and def test_display_name is
omoteura:#test_display_name. The coverage panel looks for
test/models/user_test.rb as well as spec/models/user_spec.rb.
When something already measures coverage, as SimpleCov does, the formatter and the reporter also record which lines of your code ran, so the coverage panel can tell which methods the tests called. They never start coverage themselves.
In VS Code
The Omoteura extension
(source) marks each line a shape
links to with the result of the tests behind it, explains it on hover, and
flips between a file and its diagram with ⌥⌘U (Ctrl+Alt+U). The diagram
opens in Excalidraw with linked shapes outlined by test status; clicking a
link opens the code. Saving writes the file's own format, so an Obsidian note
keeps everything around the drawing, and a change made elsewhere since it
opened is never overwritten. A coverage panel lists which declarations and
public methods of the open file its tests cover.
Commands
| command | does |
|---|---|
omoteura check |
Report broken links and diagrams not attached to code |
omoteura draft FILE... |
Draft a diagram from code, as a starting point to draw on |
omoteura inspect FILE |
Print one file's diagram, links and test results as JSON, for editors |
omoteura diagram FILE |
Print one diagram's drawing, links and test results as JSON, for editors |
omoteura links FILE |
Print the places in one code file a shape can link to, with their links, as JSON, for editors |
omoteura coverage FILE |
Print which declarations and public methods of one file its tests cover, as JSON, for editors |
omoteura save FILE |
Save a drawing an editor changed, read as JSON from stdin, in the file's format |
omoteura version |
Print the version |
Run omoteura <command> --help for options.
Configuring Omoteura
.omoteura.yml at the project root is optional:
| key | default | meaning |
|---|---|---|
diagrams_dir |
ura |
Where diagrams live, relative to the root |
attach |
none | Code paths mapped to diagram paths |
coverage_panel |
true |
false turns off the editor's list of tested and untested declarations and methods |
.omoteura.local.yml is read on top of it, entry by entry, for settings of
your own. Keep it out of version control.
Questions, Feedback
Open an issue on GitHub. Report security problems privately as described in SECURITY.md.
Contributing to Omoteura
Fork, fix, then send a pull request. CONTRIBUTING.md has the details.
To run the specs and lint:
% nix develop # or use Ruby 3.4+ without Nix
% bundle install
% bundle exec rakeTo run the specs against the oldest supported rspec-core:
% BUNDLE_GEMFILE=gemfiles/rspec_3_12.gemfile bundle install
% BUNDLE_GEMFILE=gemfiles/rspec_3_12.gemfile bundle exec rake specTo measure allocations and time of the main code paths:
% bundle exec rake benchCopyright
Copyright (c) 2026- KAIBYAKU K.K. See LICENSE for further details.