Ruby Formatter

Ruby closed blocks with end long before JavaScript taught the internet to count curly braces. Paste a class, a Rails action, or a script dumped into Slack. Indent follows the keywords. A missing end is named before you copy.

Your RubyEdit freely
RebuiltRead only
Paste Ruby on the left.

Most pages sold as a ruby formatter started life as a JavaScript beautifier. Somebody swapped the Ace mode, left the brace counter running, and shipped. Ruby never wrapped a method in curly braces. The closer is the word end. Feed /end/ to a regular expression and you also hit send, pending, the string "weekend", and the comment at the bottom of every half-finished method. Indent then walks one level left for the rest of the file.

This ruby formatter lexes first. end counts only as a keyword sitting outside a string or a comment.

RuboCop this is not. Cop names such as Style/AndOr, Style/HashSyntax, and Style/FrozenStringLiteralComment never run here. and stays and. A hash rocket stays a hash rocket. A missing frozen string magic comment stays missing. Indent and spacing are the job. Offense lists belong in the repository, next to .rubocop.yml.

Brace counters were trained on a different language

A { in JavaScript opens a block. A { in Ruby opens a hash, or a block, and the two are not interchangeable. end closes class, module, def, begin, do, if, unless, case, while, until, and for. Counting braces never sees those.

The rail on the left keeps a running tally of openers against end. When the two disagree, the first problem line is named. Read the tally before you copy. Indent below an unmatched keyword is a guess.

A string holding the letters e-n-d used to wreck pages like this. So did # end of setup. Both survive here because the lexer already hid them.

Two spaces already won

The community style guide, RuboCop's default Layout/IndentationWidth, Rails, and the GitHub ruby repository indent with two spaces. Four spaces is what you bring from Java. Tabs are what you bring from Go.

Neither is illegal. Both look like a foreign patch in a Rails app. Reviewers bounce the pull request for the indent before they read the logic.

Match the file you were handed. For a new gem with no history, pick two.

I still see four-space Ruby in take-home tests. The candidate came from Python. The linter still fails the build.

If the end tally on the left turns red, leave Copy alone. The right pane still fills. Every indent below the first unmatched keyword is a guess. Fix the missing end on the left, format again, then copy.

{ |n| } is not a short do

Block delimiters in Ruby are not a taste toggle. { |n| n * 2 } binds tighter than do |n| n * 2 end.

puts [1, 2, 3].map { |n| n * 2 } prints an array. puts [1, 2, 3].map do |n| n * 2 end prints the enumerator, then surprises you. A formatter rewriting one into the other is rewriting meaning. This one never converts.

Expand one-line do...end onto several lines when you want them readable. Brace blocks stay brace blocks. The Keep switch only protects methods already written as def foo; 1; end.

The Lua formatter on this site has the same end problem in a different grammar. Open Lua when the paste is a Neovim spec or an OpenResty handler, not a Rails action.

The SQL in <<~SQL is data

A squiggly heredoc strips its own leading whitespace at runtime. Reindent the body to match the surrounding method and you change the string Postgres receives, or you fight the strip and get a mess.

Single-quoted 'SQL' tags skip interpolation. Double-quoted tags interpolate. The body is a string literal either way. The opener line is formatted. The characters between opener and closer stay as you typed them.

Need the query readable on its own? Paste the inner SQL into the SQL formatter. Leave the Ruby wrapper alone.

Do not paste schema.rb

db/schema.rb is generated by rails db:migrate. RuboCop often excludes the file on purpose. Dumping generated schema into a browser, copying the result, and committing the diff is how a team spends an afternoon arguing about trailing commas on t.string.

Format the migration you wrote. Leave the generated dump to Rails.

The same warning applies to structure.sql and to any file whose header says the dump is auto-generated.

Rewrites versus refusals

Rewritten
Leading whitespace, rebuilt from keyword depth. Spaces around =>, =, ||, &&, and comparisons. Space after commas, none before. Trailing spaces stripped. Mixed tabs remapped onto the unit you picked. One-line def foo; 1; end expanded unless Keep is on.
Left alone
Identifier names. and / or / not versus && / || / !. Hash rockets versus { foo: 1 }. Parentheses you omitted on method calls. Frozen string magic comments. require and gem order. Heredoc bodies. Comment text.
Refused
Files over 2 MB. The editors hold the buffer twice, and a tab trying to pretty-print a whole gem source tree will lock the browser before indent finishes. Split the file, or run RuboCop on disk.

RuboCop stays in the repository

A paste from Slack, a gist, a failed CI log: this desk. A commit: RuboCop, Standard Ruby, or syntax_tree, already wired so every file lands on the same rules without opening a browser.

# .rubocop.yml AllCops:NewCops: enable TargetRubyVersion: 3.3 Layout/IndentationWidth:Width: 2 Style/FrozenStringLiteralComment:Enabled: true
bundle exec rubocop -A app/ lib/ bundle exec rubocop --format github

The first command rewrites. The second prints annotations a pull request understands. Put the check in CI so indent stops being a personal preference.

ERB, Haml, and Slim are outside scope. A template with <%= %> tags is not a Ruby file. Pull the Ruby out, or stop. GraphQL sitting inside a string in a Rails controller belongs on the GraphQL formatter once you have peeled the query out. PHP colleagues running PSR-12 want the PHP formatter. A clipboard whose language you have not named yet belongs on the universal code formatter until the guess chip settles. A JSON body with no Ruby wrapper belongs on the JSON beautifier.

Python people arriving from PEP 8 should open the Python formatter instead of forcing four-space Ruby through this page. The languages share indentation-as-structure. They do not share a style guide.

Questions the end tally does not answer

RuboCop, heredocs, schema.rb, and what a browser formatter will not rewrite.

Is my Ruby sent to a server?

No. The lexer and the printer are JavaScript inside this tab. A controller with customer emails, a credentials file, or a script holding api.toolexe.com keys stays on the machine. Closing the tab drops both buffers.

Why does the output ignore RuboCop cops I rely on?

This page rebuilds indent and operator spacing. Cops never run. The word and stays and. Hash rockets stay hash rockets. Frozen string magic comments are not inserted. Put RuboCop or Standard Ruby in the repository for those rewrites, then use this desk for a Slack paste.

The tally is red but the right pane still has code. Do I commit the result?

No. A missing end still emits indent. Every level below the first unmatched keyword is a guess. Fix the source on the left, format again, and copy only after the tally is boring.

Will formatting a squiggly heredoc change the SQL my app sends?

The body between the opener and the closer is left as typed, so the string Postgres receives does not change. The opener line is indented with the surrounding method. If you need the SQL itself pretty-printed, paste the inner query into the SQL formatter.

Should I format db/schema.rb here?

No. Rails generates schema.rb. RuboCop often excludes the file. Reformatting a generated dump creates a noisy diff and solves nothing. Format the migration you wrote. Leave the dump to rails db:migrate.

Why is two spaces the default?

The community style guide, RuboCop Layout/IndentationWidth, Rails, and the GitHub ruby repository all indent with two spaces. Four spaces is a Java habit. Tabs are a Go habit. Match the file you were handed. Switch the rail if the project already picked something else.

Did the formatter turn my brace block into do/end?

No. Brace blocks and do/end have different precedence. Converting one into the other changes meaning, so the page never converts. Expand only rewrites one-line def and do/end methods. Brace blocks stay brace blocks.

How large a file does this accept?

Up to 2 MB. Past 2 MB the page refuses the paste rather than freezing the tab. A whole gem belongs in RuboCop on disk, not in two Ace editors.