This page is the catalogue of rules, and a public roadmap. Rules marked Available or Partial work in the extension today. Rules marked Planned do not exist yet; they are listed with a phase number, and lower phases come first. Rule names are the diagnostic codes, and the config file described below is read for the rules that exist.
The formatter and the checks work without configuration, and a .ruby-templates.toml changes them. Formatting describes what the formatter does, and the diagnostics reference lists every code the checks report.
How to read an entry
Section titled “How to read an entry”- Type. An autofix is a formatter rule that rewrites the template. A warning is a check that reports a problem and rewrites nothing.
- Rendered output. Safe means the rule leaves the rendered page as it is: it only changes whitespace the browser ignores, or changes markup into an equal form. Not safe says what can change.
- Examples. An autofix shows the template before and after. A warning shows a flagged template and a fine one. The examples of available rules are run through
ruby-templates formatandruby-templates checkby the tests; the ones of planned rules are written by hand. - Defaults. A formatting rule says whether it is on by default.
Layout/TrailingEmptyLinesis, for Haml and Slim, and so areLayout/TrailingWhitespace(all three languages) andLayout/IndentationStyle(ERB, which writes spaces unless the editor,.editorconfigor itsstylesay tabs); the others,Layout/EmptyLinesamong them, are off until you turn them on, so a file is never changed beyond what the entry says unless you ask. The planned Rails checks are on.
Configuration
Section titled “Configuration”Rules, and the switches that VS Code has as settings, are configured in a .ruby-templates.toml. The editor, ruby-templates check and ruby-templates format read the same file. A rule is a table named after it, with the department and the rule joined by a dot (["Html/ImgAlt"] in quotes is an error, and the message says how to write it):
[Html.ImgAlt]
enabled = false
A department table with only enabled switches all its rules; a rule’s own enabled beats it:
[Html]
enabled = false
[Html.DuplicateId]
enabled = true
To change a rule for some files only, give it include or exclude globs. They follow the EditorConfig syntax (*, **, ?, [abc], {a,b}): a pattern without / matches in any folder, one with a leading or inner / is anchored to the folder of the file that holds it.
[Html.ImgAlt]
exclude = ["**/*.haml", "app/views/legacy/**"]
A rule with options takes them in its own table. max and max_line_length are whole numbers, 0 or more; style is one of a few words; tags is a list of element names. A value that does not fit is a problem in the file:
[Layout.EmptyLines]
enabled = true
max = 2
[Layout.EmptyLinesAroundBlockBody]
enabled = true
style = "empty_lines"
[Layout.ElementContentOnOwnLine]
enabled = true
tags = ["div", "section"]
All keys
Section titled “All keys”root = true # stop looking in the folders above
exclude = ["vendor/**", "tmp/**"] # no checks and no formatting for these files
[erb]
format.enabled = false # no formatting for ERB
format.align_comments = true # same as --align-comments
diagnostics.enabled = true # false: no diagnostics for ERB
disabled_diagnostics = ["Html/ImgAlt", "unclosed-element"] # rule names, or the codes of 0.10 and earlier
[haml] # format.enabled, diagnostics.enabled, disabled_diagnostics
[slim] # the same, and: extra_engines = ["mjml"]; Skim files too
[jbuilder] # disabled_diagnostics, for Jbuilder and Builder views
The language tables take the keys of the rubyTemplates.<language>.* settings, in snake case. A key under the wrong table (extra_engines under [erb]) is reported.
Which file applies
Section titled “Which file applies”- The file looks for
.ruby-templates.tomlin the template’s folder and every folder above it, nearest first, and stops at one withroot = true. - The files merge key by key: the nearest file that sets a key wins. A list (
exclude,include,disabled_diagnostics,extra_engines) is replaced, not extended. Globs are relative to the folder of the file that holds them. - The file is stronger than VS Code: precedence is the nearest file, then the VS Code settings (where no file says anything), then
.editorconfigfor the indentation width and, for ERB,indent_style(the editor’sinsertSpacescounts as VS Code here), then the defaults.styleofLayout.IndentationStyleis a file setting, so it beatsinsertSpacesandindent_style. - A rule is on, if it is on by default, unless the nearest file that sets either its
enabledor its department’senabledsaysfalse, and a formatting rule that is off by default is on when that file saystrue; within that one file, the rule’s ownenabledbeats the department’s. A root file with[Html.ImgAlt] enabled = trueand alegacy/file with[Html] enabled = falsetherefore leaveHtml/ImgAltoff underlegacy/. A rule that is off (its own or its department’senabled = false, or left out byinclude/exclude) does nothing, which for the two rules that are on by default means the old behaviour:Layout/IndentationStyleoff writes spaces whatever the editor or.editorconfigsay, andLayout/TrailingWhitespaceoff keeps the whitespace at the line ends, so[Layout] enabled = falsestops that trimming too. Then the rule’sincludeandexcludeapply, and the rule must exist for the file’s language. A file in the globalexcludegets no diagnostics and is not formatted.
Problems in the file
Section titled “Problems in the file”A key or a value the server can’t read is dropped, and the rest of the file is used. A file that exists but can’t be read (not UTF-8, no permission, a folder with that name) is one problem at its first line. Problems show on the .ruby-templates.toml itself in the Problems panel, and in the Ruby Templates output, once a template it applies to is open or formatted; they refresh when the file is saved. A .ruby-templates.toml above the workspace folders is watched too, if the editor allows it. ruby-templates check prints a problem as file:line:column: warning: message and exits with 1 (the legacy ruby-templates-ls --check prints it but doesn’t fail on it), and format --verbose prints it too. A file that isn’t valid TOML is ignored whole.
| Rule | Languages | Type | Status |
|---|---|---|---|
| Layout/EmptyLines | ERB, Haml, and Slim | Autofix | Available |
| Layout/EmptyLinesAroundBlockBody | ERB, Haml, and Slim | Autofix | Available |
| Layout/EmptyLinesAroundElementBody | ERB, Haml, and Slim | Autofix | Available |
| Layout/EmptyLinesAroundBranches | ERB, Haml, and Slim | Autofix | Available |
| Layout/SpaceInHtmlTag | ERB | Autofix | Available |
| Layout/SpaceInsideErbTag | ERB | Autofix | Available |
| Layout/SpaceAfterScriptMarker | Haml and Slim | Autofix | Available |
| Layout/IndentationStyle | ERB | Autofix | Available |
| Layout/TrailingWhitespace | ERB, Haml, and Slim | Autofix | Available |
| Layout/TrailingEmptyLines | ERB, Haml, and Slim | Autofix | Available |
| Layout/ElementContentOnOwnLine | ERB, Haml, and Slim | Autofix | Available |
| Layout/AttributesPerLine | ERB | Autofix | Available |
| Layout/LineLength | ERB, Haml, and Slim | Warning | Available |
| Style/ErbCommentSyntax | ERB | Autofix | Available |
| Style/AttributeQuotes | ERB | Autofix | Available |
| Style/BooleanAttributes | ERB | Autofix | Available |
| Style/VoidTagClosing | ERB | Autofix | Available |
| Style/TagNameCase | ERB and Slim | Autofix | Available |
| Style/ImplicitDiv | Haml and Slim | Autofix | Available |
| Style/ClassesBeforeIds | Haml | Autofix | Available |
| Style/HashAttributes | Haml | Autofix | Available |
| Style/ErbTrimMode | ERB | Warning | Available |
| Rails/StrictLocals | ERB, Haml, Slim, and Jbuilder | Warning | Available |
| Rails/MissingPartial | ERB, Haml, Slim, and Jbuilder | Warning | Available |
| Rails/SilentRender | ERB, Haml, and Slim | Warning | Available |
| Rails/SilentHelper | ERB, Haml, and Slim | Warning | Available |
| Rails/InstanceVariableInPartial | ERB, Haml, and Slim | Warning | Available |
| Rails/UnusedStrictLocal | ERB, Haml, and Slim | Warning | Available |
| Rails/RenderInLoop | ERB, Haml, and Slim | Warning | Available |
| Rails/DebugOutput | ERB, Haml, and Slim | Warning | Available |
| Rails/OutputControlFlow | ERB | Warning | Available |
| Rails/UnsafeOutput | ERB, Haml, and Slim | Warning | Available |
| Rails/UnusedBlockArgument | ERB, Haml, and Slim | Warning | Available |
| Rails/I18nMissingKey | ERB, Haml, and Slim | Warning | Available |
| Rails/UnknownRoute | ERB, Haml, and Slim | Warning | Available |
| Turbo/PreferTurboAttributes | ERB, Haml, and Slim | Autofix | Available |
| Turbo/PermanentRequiresId | ERB, Haml, and Slim | Warning | Available |
| Stimulus/UnknownController | ERB, Haml, and Slim | Warning | Available |
| Stimulus/UnknownTargetOrAction | ERB, Haml, and Slim | Warning | Available |
| Html/ImgAlt | ERB, Haml, and Slim | Warning | Available |
| Html/DuplicateId | ERB, Haml, and Slim | Warning | Available |
| Html/UnexpectedCloseTag | ERB | Warning | Available |
| Html/MismatchedCloseTag | ERB | Warning | Available |
| Html/UnclosedElement | ERB | Warning | Available |
| Html/VoidChildren | Haml and Slim | Warning | Available |
| Html/DuplicateAttribute | ERB, Haml, and Slim | Warning | Available |
| Html/NestedInteractive | ERB, Haml, and Slim | Warning | Available |
| Html/AnchorHref | ERB, Haml, and Slim | Warning | Available |
| A11y/IframeTitle | ERB, Haml, and Slim | Warning | Available |
| A11y/GenericLinkText | ERB, Haml, and Slim | Warning | Available |
Layout
Layout/EmptyLines
Available
At most a set number of blank lines in a row.
Languages ERB, Haml, and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Browsers collapse the whitespace between elements, so a shorter run of blank lines renders the same. Content where whitespace counts is left alone: <pre>, <textarea>, <script>, <style>, quoted attribute values, comments and the Ruby in ERB tags in ERB, filters in Haml, text blocks and engine bodies in Slim. Two things can't be detected: elements that CSS styles white-space: pre, and blocks that capture their content and care about its whitespace (such as <%= markdown do %>). With max = 0, paragraphs there can join.
Status Available.
Today Runs of blank lines longer than max (a whole number, 1 by default; 0 removes them all) are cut to max, also at the start and the end of the file. Format selection leaves blank lines as they are. For Haml and Slim, the formatter checks that the compiled template stays the same, and keeps the blank lines of a file when it would not.
[Layout.EmptyLines]
enabled = true
max = 1
<h1>Users</h1>
<ul>
<li>Ada</li>
</ul>
<h1>Users</h1>
<ul>
<li>Ada</li>
</ul>
%h1 Users
%ul
%li Ada
%h1 Users
%ul
%li Ada
h1 Users
ul
li Ada
h1 Users
ul
li Ada
Layout/EmptyLinesAroundBlockBody
Available
Blank lines at the start and end of the body of an ERB or Ruby block.
Languages ERB, Haml, and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Only blank lines between tags change, which render as whitespace between them, and browsers collapse that. Content where whitespace counts is left alone: <pre>, <textarea>, <script>, <style>, attribute values, comments and the Ruby in ERB tags in ERB, filters in Haml, text blocks and engine bodies in Slim. Two things can't be detected: elements that CSS styles white-space: pre, and blocks that capture their content and care about its whitespace (such as <%= markdown do %>).
Status Available.
Today style is no_empty_lines (the default) or empty_lines, which keeps exactly one blank line there and adds it where there is none. In ERB the body is what lies between the opening tag and the closing tag or <% end %>. Haml and Slim have no closing line, so only the start of the body is covered: the blank lines after - if or - each do are set, and those before the next line at the same or a lower depth are siblings' separators and stay. A block with nothing in it loses its blank lines under no_empty_lines and is left alone under empty_lines. The lines before and after else and when belong to Layout/EmptyLinesAroundBranches, and Layout/EmptyLines max is applied after this rule, so max = 0 wins over empty_lines. Format selection leaves blank lines as they are. For Haml and Slim, the formatter checks that the compiled template stays the same, and keeps the blank lines of a file when it would not.
[Layout.EmptyLinesAroundBlockBody]
enabled = true
style = "no_empty_lines" # or empty_lines
With no_empty_lines. empty_lines would instead keep exactly one blank line after the opening tag and before the closing one.
<% if @admin_section %>
<% @reports.each do |report| %>
<p><%= report.title %></p>
<% end %>
<% end %>
<% if @admin_section %>
<% @reports.each do |report| %>
<p><%= report.title %></p>
<% end %>
<% end %>
Haml and Slim have no closing line, so only the blank lines after - if and - each go; the one before %footer separates siblings.
- if @admin_section
- @reports.each do |report|
%p= report.title
%footer Done
- if @admin_section
- @reports.each do |report|
%p= report.title
%footer Done
- if @admin_section
- @reports.each do |report|
p = report.title
footer Done
- if @admin_section
- @reports.each do |report|
p = report.title
footer Done
Layout/EmptyLinesAroundElementBody
Available
Blank lines at the start and end of the body of an HTML element.
Languages ERB, Haml, and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. The blank lines sit between tags, where they render as whitespace that browsers collapse. Content where whitespace counts is left alone: <pre>, <textarea>, <script>, <style>, attribute values, comments and the Ruby in ERB tags in ERB, filters in Haml, text blocks and engine bodies in Slim. Elements that CSS styles white-space: pre can't be detected.
Status Available.
Today style is no_empty_lines (the default) or empty_lines, which keeps exactly one blank line there and adds it where there is none. In ERB the body is what lies between the start tag and the end tag. Haml and Slim have no closing line, so only the start of the body is covered: the blank lines after %section or section are set, and those before the next line at the same or a lower depth are siblings' separators and stay. An element with nothing in it (<div> and </div> on two lines) loses its blank lines under no_empty_lines and is left alone under empty_lines. Layout/EmptyLines max is applied after this rule, so max = 0 wins over empty_lines. Format selection leaves blank lines as they are. For Haml and Slim, the formatter checks that the compiled template stays the same, and keeps the blank lines of a file when it would not.
[Layout.EmptyLinesAroundElementBody]
enabled = true
style = "no_empty_lines" # or empty_lines
<section class="users">
<h2>Users</h2>
<p>Active this week</p>
</section>
<section class="users">
<h2>Users</h2>
<p>Active this week</p>
</section>
Haml and Slim have no closing line, so only the blank line after %section.users goes; the one before %footer separates siblings.
%section.users
%h2 Users
%p Active this week
%footer Done
%section.users
%h2 Users
%p Active this week
%footer Done
section.users
h2 Users
p Active this week
footer Done
section.users
h2 Users
p Active this week
footer Done
Layout/EmptyLinesAroundBranches
Available
Blank lines before and after an else, elsif or when.
Languages ERB, Haml, and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. The blank lines sit between tags, where they render as whitespace that browsers collapse. Content where whitespace counts is left alone, as for Layout/EmptyLines. Two things can't be detected: elements that CSS styles white-space: pre, and blocks that capture their content and care about its whitespace (such as <%= markdown do %>).
Status Available.
Today style is no_empty_lines (the default) or empty_lines, which keeps exactly one blank line before and after the line and adds it where there is none. It covers else, elsif, when, in, rescue and ensure lines in ERB, Haml and Slim. A branch with nothing in it (<% if a %> directly above <% else %>, or - case a above - when 1) loses its blank lines under no_empty_lines and is left alone under empty_lines. Layout/EmptyLines max is applied after this rule, so max = 0 wins over empty_lines. Format selection leaves blank lines as they are. For Haml and Slim, the formatter checks that the compiled template stays the same, and keeps the blank lines of a file when it would not.
[Layout.EmptyLinesAroundBranches]
enabled = true
style = "no_empty_lines" # or empty_lines
<% if @user.admin? %>
<p>Admin</p>
<% else %>
<p>Member</p>
<% end %>
<% if @user.admin? %>
<p>Admin</p>
<% else %>
<p>Member</p>
<% end %>
- if @user.admin?
%p Admin
- else
%p Member
- if @user.admin?
%p Admin
- else
%p Member
- if @user.admin?
p Admin
- else
p Member
- if @user.admin?
p Admin
- else
p Member
Layout/SpaceInHtmlTag
Available
One space between attributes, none before > or around =.
Languages ERB
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Whitespace inside a start tag doesn't change the element. The formatter checks that every tag has the same name, attributes and values, that a blank stays where an ERB tag in a tag had one, and that the text between tags is byte for byte the same (the blanks inside ERB tags aside), and leaves the tags alone when it does not.
Status Available.
Today In the start and end tags of ERB templates, a run of blanks between the name and the attributes or between attributes becomes one space, blanks around the = of an attribute and before > go, and <br /> keeps one space before />. A run with a newline, a quoted value, an unquoted value, an ERB tag (<div <%= attrs %> class="a"> loses only the extra blank; the last space next to an ERB tag is never added or removed, and blanks beside an ERB tag in a name or value stay), a tag whose attributes are not plain enough to read (a quote after an ERB tag, say), and everything that is not markup (the bodies of <script>, <style>, <textarea>, <title>, <xmp>, <iframe>, <noscript>, <noembed>, <noframes> and <plaintext>, comments, CDATA, a < b text, <%% literals) stay. A run with a line break, a bare \r included, is never touched. Format selection applies it to the lines it covers.
[Layout.SpaceInHtmlTag]
enabled = true
<div class="card" id="user_<%= user.id %>" >
<img src = "<%= user.avatar_url %>" alt="">
</div>
<div class="card" id="user_<%= user.id %>">
<img src="<%= user.avatar_url %>" alt="">
</div>
<div class="card" id="u"><%= u.name %></div >
<div class="card" id="u"><%= u.name %></div>
Layout/SpaceInsideErbTag
Available
One space inside the delimiters of an ERB tag: <%= x %>, not <%=x%> or <%= x %>.
Languages ERB
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. The spaces are outside the Ruby code, so the compiled template is the same. The formatter checks that the trimmed Ruby of every tag is the same before and after, and keeps the spacing of a file when it is not.
Status Available.
Today Each code and output tag that sits on one line gets one space after its opening and one before its closing delimiter, the - of <%- and -%> and the = of <%== included. Tags that span lines, comments (<%#) and literal <%% tags stay. Format selection applies it to the lines it covers. The code action does the same without the rule: "Add spaces inside this ERB tag" is offered on the tags under the cursor, and "Add spaces inside every ERB tag" is the source action source.erbSpacing.
"[erb]": {
"editor.codeActionsOnSave": { "source.erbSpacing": "explicit" }
}[Layout.SpaceInsideErbTag]
enabled = true
<p><%=user.name%></p>
<p><%= user.email %></p>
<%if user.admin?%>
<span>Admin</span>
<%end%>
<p><%= user.name %></p>
<p><%= user.email %></p>
<% if user.admin? %>
<span>Admin</span>
<% end %>
Layout/SpaceAfterScriptMarker
Available
A space after the = or - that starts Ruby code.
Languages Haml and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. The marker and the code are the same tokens with a space between. The formatter checks that the template compiles to the same Ruby before and after, and keeps the markers of a file when it does not.
Status Available.
Today Where the parser reads Ruby code, the marker gets a space when the code follows it directly: -, =, ~, !=, &=, !~ and &~ in Haml, -, = and == (with < and >) in Slim, both at the start of a line and after a tag (%p=x, p=x). Text, comments (-# in Haml and Slim, / in Slim), filters and engine bodies, | text blocks, attribute values, == and \= lines in Haml (text, not code) and the continuation lines of multi-line Ruby are not touched. Also skipped: the first line of a multi-line Haml script written with |, the code after an attribute hash that spans lines, and Slim's p: =x. Format selection applies it to the lines it covers.
[Layout.SpaceAfterScriptMarker]
enabled = true
-if @user
%h1=@user.name
=render "users/card", user: @user
- if @user
%h1= @user.name
= render "users/card", user: @user
-if @user
h1=@user.name
=render "users/card", user: @user
- if @user
h1= @user.name
= render "users/card", user: @user
Layout/IndentationStyle
Available
Indent with tabs or with spaces.
Languages ERB
Type Autofix, run by the formatter. On by default.
Rendered output Safe. Leading whitespace between tags renders as nothing. Content where whitespace counts is left alone.
Status Available.
Today ERB only; Haml and Slim always use spaces, since Slim rejects mixed indentation and Haml wants it consistent. Each indent level the formatter writes is one tab with style = "tabs", or the width in spaces (the default of 2) with "spaces". Only the leading whitespace of the lines the formatter indents changes: <pre>, <textarea>, <script>, <style>, <xmp> and <listing> bodies, Ruby inside tags, quoted attribute values and comments keep every byte. The lines of a tag that spans several lines move with its first line and keep their indentation relative to it, as written, so a tag attribute lined up with spaces stays lined up. Without style, the style comes from the first of these that has one: the editor.insertSpaces setting, then .editorconfig (indent_style = tab or space), then spaces. In an editor insertSpaces always decides, because the language server protocol always sends it, as it does the tab size; .editorconfig counts only on the command line, or through an EditorConfig extension that sets insertSpaces. style in .ruby-templates.toml beats all of them, and enabled = false always writes spaces. The width is the editor's tab size, then .editorconfig (indent_size, or tab_width for indent_size = tab), then 2; it only matters for spaces. Format selection applies the style to the lines it covers.
[Layout.IndentationStyle]
enabled = false[Layout.IndentationStyle]
enabled = true
style = "tabs" # or "spaces"
With style = "tabs".
<ul>
<% @users.each do |user| %>
<li><%= user.name %></li>
<% end %>
</ul>
<ul>
<% @users.each do |user| %>
<li><%= user.name %></li>
<% end %>
</ul>
Layout/TrailingWhitespace
Available
No spaces or tabs at the end of a line.
Languages ERB, Haml, and Slim
Type Autofix, run by the formatter. On by default.
Rendered output Safe. Whitespace that is content stays: <pre> and <textarea> bodies, ERB comments, Haml filters, and Slim text lines (| text, text after a tag).
Status Available.
Today The formatter does it in ERB, Haml and Slim, on Format Document, on save and in ruby-templates format. It is on for all three; with enabled = false the spaces and tabs at the line ends stay as written, whitespace-only lines included.
[Layout.TrailingWhitespace]
enabled = false[Layout.TrailingWhitespace]
enabled = true
The spaces and tabs at the line ends are invisible here.
<ul>
<li class="user">
<%= user.name %>
</li>
</ul>
<ul>
<li class="user">
<%= user.name %>
</li>
</ul>
%ul
%li.user
= user.name
%ul
%li.user
= user.name
ul
li.user
a href="/users"
= user.name
ul
li.user
a href="/users"
= user.name
Layout/TrailingEmptyLines
Available
The file ends with exactly one line break.
Languages ERB, Haml, and Slim
Type Autofix, run by the formatter. On by default in Haml and Slim, off in ERB.
Rendered output Not safe. Safe in Haml and Slim, which ignore the blank lines at the end. Not safe in ERB: Rails renders a template or partial byte for byte, so a final line break or the blank lines at the end become output. A partial _icon.html.erb that is <svg></svg> renders with a line break after it once the rule adds one, and that shows as a space in <button><%= render "icon" %>Save</button>.
Status Available.
Today The blank lines at the end of the file go, and a file that doesn't end with a line break gets one, using the line ending the file mostly has. It is on for Haml and Slim and off for ERB, where enabled = true turns it on, and include limits it to some files. Format selection leaves the end of the file as it is.
[Layout.TrailingEmptyLines]
enabled = false[Layout.TrailingEmptyLines]
enabled = true
<h1>Users</h1>
<h1>Users</h1>
%h1 Users
%h1 Users
h1 Users
h1 Users
Layout/ElementContentOnOwnLine
Available
The content of a block-level element on its own indented line between the open and close tags.
Languages ERB, Haml, and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Not safe. It adds whitespace. Around block-level content that is invisible, but inside an inline element such as <a> or <span> the new line break shows as a space, so only the tags in the list are touched, and <a class="btn"><%= name %></a> stays as it is. Haml writes the nested content on a line of its own in the output; Slim output is the same. The formatter reads the result again and keeps it only when nothing but that whitespace is new: ERB keeps every character and element, Haml generates the same Ruby and reads the moved content one level below its tag, Slim compiles to the same template.
Status Available.
Today The content of an element in tags that is written next to its tags moves to its own line, one level in: ERB <div>…</div> on one line, Haml %div text and %div= x, Slim div text (which becomes | text) and div = x. The element names are not case-sensitive. It stays where the element or its content is not plain: ERB with a <% %> tag in the start tag or the content (output tags <%= %> and comments are fine), a tag or content over several lines, an empty element; Haml with <, > or / on the tag, a | line, content that starts with a character that makes a line something else (-, %, ., =), and a tag with lines nested under it; Slim with div: span x, =' and a tag with lines nested under it. pre, textarea, script and style are never touched, nor is anything inside them or inside an inline element such as <span> or <a>. Elements inside each other all move. Format selection leaves the rule out, since it adds lines.
[Layout.ElementContentOnOwnLine]
enabled = true
tags = ["div", "section", "article", "header", "footer", "ul", "ol"]
<div class="card" id="<%= dom_id(user) %>"><%= user.name %></div>
<div class="card" id="<%= dom_id(user) %>">
<%= user.name %>
</div>
%section.card#user= user.name
%section.card#user
= user.name
section.card#user = user.name
section.card#user
= user.name
Layout/AttributesPerLine
Available
A start tag longer than the limit gets one attribute per line.
Languages ERB
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Whitespace between attributes doesn't change the element.
Status Available.
Today A start tag that stands first on its line, fits on it and has two or more attributes is split when its line is longer than max_line_length characters (default 100, counted like Layout/LineLength). Each attribute goes on its own line one indent level in, and the > (or />) stays after the last one. Tags already over several lines, tags after other markup on their line, tags with an ERB tag between attributes or in a name, <% control flow in the tag, values over several lines and tags inside <pre> or <textarea> are left alone. Off by default; Format Selection leaves it out because it adds lines.
[Layout.AttributesPerLine]
enabled = true
max_line_length = 100
<input type="email" name="user[email]" id="user_email" class="form-control form-control-lg" placeholder="Email" required>
<input
type="email"
name="user[email]"
id="user_email"
class="form-control form-control-lg"
placeholder="Email"
required>
Layout/LineLength
Available
A line longer than the limit.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic. Off by default.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today Lines longer than max characters (default 100) are reported from the first character past the limit. Characters are counted, not display columns, a tab is one, and trailing whitespace is not counted. A line that cannot be made shorter is left alone: one without a space after its indentation (a long URL) and the # locals: comment of strict locals. Off by default. See Layout/LineLength in the diagnostics reference.
[Layout.LineLength]
enabled = true
max = 100
<p class="lead text-muted"><%= link_to "Read the full announcement", announcement_path(@announcement), class: "link" %></p>
<p class="lead text-muted">
<%= link_to "Read the full announcement", announcement_path(@announcement), class: "link" %>
</p>
%p.lead.text-muted= link_to "Read the full announcement", announcement_path(@announcement), class: "link"
%p.lead.text-muted
= link_to "Read the full announcement", announcement_path(@announcement), class: "link"
p.lead.text-muted = link_to "Read the full announcement", announcement_path(@announcement), class: "link"
p.lead.text-muted
= link_to "Read the full announcement", announcement_path(@announcement), class: "link"
Style
Style/ErbCommentSyntax
Available
An ERB comment written <%#, not as Ruby code that starts with #.
Languages ERB
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Both forms output nothing. Only a tag that is nothing but comment and stands alone on its line is rewritten: beside other content, Rails comments out the output that follows a <% # tag on the same line, and <%# would bring it back.
Status Available.
Today <% # note %> becomes <%# note %>, keeping a -%> as it is. A tag over several lines is rewritten when every line is a comment or blank, since <%# comments out everything up to %>. <%= # x %> is not a comment and stays, <%- # x -%> is skipped to be conservative, and so is a tag with content before or after it on its line (it usually breaks the template: Rails comments out what follows it), and the encoding comment. The ERB formatter checks that the trimmed Ruby of every tag is the same afterwards.
[Style.ErbCommentSyntax]
enabled = true
<% # TODO: show the avatar %>
<p><%= user.name %></p>
<%# TODO: show the avatar %>
<p><%= user.name %></p>
The second tag stays: <%- is not a comment tag.
<% # a comment over
# two lines %>
<%- # a trimming tag %>
<%# a comment over
# two lines %>
<%- # a trimming tag %>
Style/AttributeQuotes
Available
Attribute values in double quotes (or single, by choice), with none left unquoted.
Languages ERB
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. The attribute values are the same, in quotes or not. A value that contains the quote character, spans lines, or holds ERB output (<%=, <%==: helpers return text with quotes of their own) keeps its quotes, and a bare value with any ERB tag is not quoted, since its output may be empty or hold blanks. The formatter reads every tag again and keeps the rewrite only when each has the same name, attributes and values.
Status Available.
Today In the start tags of ERB templates, class='card' and data-controller=toggle become class="card" and data-controller="toggle" (or the other way with single_quotes). A value with the target quote in it (title='say "hi"'), a value over several lines, a quoted value with an output tag (title='<%= link_to … %>'), a bare value with any ERB tag (value=<%= q %>, href=<% if a %>…), a tag with a quote in a bare value (title=it's) or one the reader gives up on, an attribute name with ERB, and everything that is not markup (the bodies of <script>, <style>, <textarea>, <title> and the like, comments, <%= %> code) stay. A quoted value with only control tags (class='<% if a %>b<% end %>') is converted. Format selection applies it to the lines it covers.
[Style.AttributeQuotes]
enabled = true
style = "double_quotes" # or single_quotes
<div class='card' data-controller=toggle>
<%= user.name %>
</div>
<div class="card" data-controller="toggle">
<%= user.name %>
</div>
Style/BooleanAttributes
Available
A boolean attribute written without a value.
Languages ERB
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Only the presence of a boolean attribute counts, so disabled="disabled" and disabled are the same. checked="false" is not touched, since it means checked. The formatter reads every tag again and keeps the rewrite only when each has the same name and attributes. A CSS selector like [disabled=disabled] would no longer match.
Status Available.
Today disabled="disabled", disabled="" and disabled=disabled become disabled when the attribute is a boolean attribute of that element (checked and readonly on input, selected on option, open on details, autofocus and hidden on any, and so on: the HTML standard's list) and the value is empty or the name, in any case. disabled="false", hidden="until-found", a value with ERB, a boolean attribute on an element it does not belong to, custom elements, anything inside <svg> or <math>, and attributes over a line break (disabled = and the value on the next line) stay. Format selection applies it to the lines it covers.
[Style.BooleanAttributes]
enabled = true
<input type="checkbox" checked="checked" disabled="disabled">
<input type="checkbox" checked disabled>
Style/VoidTagClosing
Available
Void elements written <br>, or <br />, throughout.
Languages ERB
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. HTML ignores the slash on a void element. The formatter reads every tag again and keeps the rewrite only when each has the same name and attributes.
Status Available.
Today Void elements (area, base, br, col, embed, hr, img, input, link, meta, source, track, wbr) lose the / (and the blanks before it) with no_slash, or end in /> with slash. A slash that is part of an unquoted value (<img src=a.png/>), one right after an ERB tag, a slash ending after a bare value that ends in an ERB tag (<input value=<%= x %>>: empty output would make / the value), a tag with a line break before the >, anything inside <svg> or <math>, end tags and non-void elements (<div/> is not self-closing in HTML) stay. Format selection applies it to the lines it covers.
[Style.VoidTagClosing]
enabled = true
style = "no_slash" # or slash
<img src="<%= user.avatar_url %>" alt=""/>
<br/>
<img src="<%= user.avatar_url %>" alt="">
<br>
Style/TagNameCase
Available
Lowercase tag names, with the camelCase SVG elements as the SVG standard spells them.
Languages ERB and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. HTML tag names are case-insensitive, and the HTML parser maps SVG names to their camelCase form. The formatter reads every tag again, and Slim's output, and keeps the rewrite only when it is the same but for the case of names.
Status Available.
Today Tag names of start and end tags in ERB, and of tags in Slim, become lower case; foreignObject, linearGradient, clipPath and the other SVG elements the HTML parser spells in camel case get that spelling wherever they are written, since a partial may hold only the inside of an <svg>. Custom elements are lower-cased too. In Slim, a name that lower case would turn into doctype or an embedded engine (Javascript:), and a template that does not output HTML (.xml.slim), stay. Format selection applies it to the lines it covers.
[Style.TagNameCase]
enabled = true
<DIV class="icon">
<svg viewBox="0 0 8 8">
<lineargradient id="fade"></lineargradient>
</svg>
</DIV>
<div class="icon">
<svg viewBox="0 0 8 8">
<linearGradient id="fade"></linearGradient>
</svg>
</div>
DIV.icon
SPAN Hi
div.icon
span Hi
Style/ImplicitDiv
Available
No div where a class or id already implies it.
Languages Haml and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Haml and Slim render a bare .card or #main as a div, unless a Slim app changes the tag a bare shortcut makes (Slim::Engine.set_options default_tag: "span"): the rule assumes div, so leave it off there. The formatter reads the file again and keeps the rewrite only when Haml or Slim compile it to the same template.
Status Available.
Today %div.card and %div#main in Haml, div.card and div#main in Slim lose the div. A div with no class or id shorthand right after it (%div{class: "a"}, %div(class="a"), div class="a", a bare div), a name in other case (%DIV.card: it would be output as <div>), %%div, and a line over several lines with | stay. Format selection applies it to the lines it covers.
[Style.ImplicitDiv]
enabled = true
%div.card
%div#details
%p= user.bio
.card
#details
%p= user.bio
div.card
div#details
p = user.bio
.card
#details
p = user.bio
Style/ClassesBeforeIds
Available
Classes before the id in a Haml tag.
Languages Haml
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Haml sorts the attributes it renders, so class comes before id whichever way round they are written. The classes and the ids keep their order among themselves. The formatter reads the file again and keeps the rewrite only when Haml compiles it to the same template.
Status Available.
Today %p#intro.lead becomes %p.lead#intro and #a.b.c becomes .b.c#a, also with attributes after it. A head that is already in order, one with @ in it, and a line over several lines with | stay. Format selection applies it to the lines it covers.
[Style.ClassesBeforeIds]
enabled = true
%section#profile.card.wide
%p= user.bio
%section.card.wide#profile
%p= user.bio
Style/HashAttributes
Available
Haml attributes written as a Ruby hash, or in HTML style, throughout.
Languages Haml
Type Autofix, run by the formatter. Off by default.
Rendered output Safe. Only attributes both syntaxes can say the same way are converted: strings without escapes or interpolation, numbers, true, false, nil, and variables. Anything else leaves the tag as it is, and so does a data or aria with a value that is not a literal hash, which may become data-* attributes at run time. The formatter reads the file again and keeps the rewrite only when Haml compiles it to the same template and each tag sets the same attributes.
Status Available.
Today style = "ruby_hash" (the default) writes %a(href="/" title=@t checked) as %a{href: "/", title: @t, checked: true}, with "data-n": … for names that need quotes; style = "html" writes the hash as %a(href="/" title=@t checked). A tag with both a hash and a list, a nested hash (data: {…}), data or aria with a variable or other value that is not a hash, a splat, a key that is not a literal name, a repeated name, a value that is a method call with a receiver or arguments, an expression, a string with \ or #{}, :symbol or a list, an attribute list or hash over several lines, and a tag in a line joined with | stay. Format selection applies it to the lines it covers.
[Style.HashAttributes]
enabled = true
style = "html" # or ruby_hash
With style = "html"; "ruby_hash" converts the other way.
%a.btn{href: "/users", target: "_blank"} Users
%a.btn(href="/users" target="_blank") Users
Style/ErbTrimMode
Available
Both %> and -%> in one template.
Languages ERB
Type Warning, shown as a diagnostic. Off by default.
Rendered output Not safe. There is no autofix. In Rails, -%> also removes the line break after an output tag, so changing one to the other can change the page.
Status Available.
Today Every closer of the kind the template uses less is reported, -%> when there are as many of each. Comments (<%#) and literal <%% tags are not counted, nor are closers with more on their line (-%> only trims a line break). Off by default, as a style preference. See Style/ErbTrimMode in the diagnostics reference.
[Style.ErbTrimMode]
enabled = true
<% @users.each do |user| -%>
<li><%= user.name %></li>
<% end %>
<% @users.each do |user| %>
<li><%= user.name %></li>
<% end %>
Rails
Rails/StrictLocals
Available
A render whose locals don't match the partial's locals: comment.
Languages ERB, Haml, Slim, and Jbuilder
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today With Rails 7.1 or later in Gemfile.lock, a literal render that resolves to a partial with a locals: comment is compared with it, using Rails' messages (missing local, unknown local, no locals accepted). When a render matches several files (a variant next to the base file, or two handlers), a local is missing when every file requires it and unknown when none takes it. Quick fixes "Remove x from the render" and "Declare x in the locals of …" (it edits all the files). See Rails/StrictLocals in the diagnostics reference.
[Rails.StrictLocals]
enabled = false_card.html.erb starts with <%# locals: (user:) -%>.
<%= render "users/card", person: @user %>
<%= render "users/card", user: @user %>
Rails/MissingPartial
Available
A render of a partial or template that isn't in app/views.
Languages ERB, Haml, Slim, and Jbuilder
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today Checks literal names in render "users/card", render partial:, render template: and Jbuilder's json.partial!, with Rails' "Missing partial" or "Missing template" message. The quick fix "Create partial users/_profile.html.erb" creates the file next to the others, in the format and template language of the view. See Rails/MissingPartial in the diagnostics reference.
[Rails.MissingPartial]
enabled = false<%= render "users/profile", user: @user %>
<%= render "users/profile", user: @user %>
Rails/SilentRender
Available
A render in a tag that outputs nothing, so the partial never reaches the page.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today In a tag that outputs nothing (<% %> in ERB, - in Haml and Slim), a render with arguments, also the block form (render layout: "x" do). Not reported when the result is assigned or passed on, or when the call is the last statement of a block, since the helper that takes the block may use its value. Only in Rails projects, in templates under app/views, since Padrino and Middleman write helper output to the page from <% %>. The quick fix turns the tag into an output tag when the call is alone in its tag or line. See Rails/SilentRender in the diagnostics reference.
[Rails.SilentRender]
enabled = false<% render "users/card", user: @user %>
<%= render "users/card", user: @user %>
- render "users/card", user: @user
= render "users/card", user: @user
- render "users/card", user: @user
= render "users/card", user: @user
Rails/SilentHelper
Available
A helper that returns markup, such as link_to or image_tag, called in a tag that outputs nothing.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today In a tag that outputs nothing, a call to one of these helpers, without a receiver: link_to, link_to_if, link_to_unless, link_to_unless_current, button_to, mail_to, image_tag, content_tag, tag and tag.div-style calls for HTML elements, form_with, form_for, form_tag, fields_for, label_tag, text_field_tag, hidden_field_tag, submit_tag, simple_format, the number_to_* formatters, time_ago_in_words and distance_of_time_in_words. Helpers with side effects, such as content_for and provide, helpers of your own and calls on a receiver (form.fields_for) are left alone. Only in Rails projects, in templates under app/views, since Padrino and Middleman write helper output to the page from <% %>. The quick fix turns the tag into an output tag when the call is alone in its tag or line. See Rails/SilentHelper in the diagnostics reference.
[Rails.SilentHelper]
enabled = false<% link_to "Home", root_path %>
<%= link_to "Home", root_path %>
- link_to "Home", root_path
= link_to "Home", root_path
- link_to "Home", root_path
= link_to "Home", root_path
Rails/InstanceVariableInPartial
Available
An instance variable read inside a partial, which only works for the controllers that set it.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today The first read of each @name in a partial (a file starting with _), also inside #{}. Not reported: a variable the partial assigns itself (@count = 0, @x ||= …), defined?(@name), ActionView's own (@output_buffer, @virtual_path, @view_flow, anything starting @_) and partials under app/views/layouts. Only in Rails projects, in templates under app/views. See Rails/InstanceVariableInPartial in the diagnostics reference.
[Rails.InstanceVariableInPartial]
enabled = false<p><%= @user.name %></p>
<%# locals: (user:) %>
<p><%= user.name %></p>
%p= @user.name
-# locals: (user:)
%p= user.name
p = @user.name
/# locals: (user:)
p = user.name
Rails/UnusedStrictLocal
Available
A local in the locals: comment that the partial never uses.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today A keyword in the locals: comment (Rails 7.1+) that the template never reads, assigns or passes on: not even as local_assigns[:name], local_assigns.fetch(:name), binding.local_variable_get(:name), defined?(name) or in the default of another local. Nothing is reported when the template uses local_assigns as a whole (render "x", local_assigns, **local_assigns), binding, local_variables or eval, and **rest and &block are never reported. The quick fix removes the local and its comma from the comment. See Rails/UnusedStrictLocal in the diagnostics reference.
[Rails.UnusedStrictLocal]
enabled = false<%# locals: (user:, compact: false) %>
<p><%= user.name %></p>
<%# locals: (user:) %>
<p><%= user.name %></p>
-# locals: (user:, compact: false)
%p= user.name
-# locals: (user:)
%p= user.name
/# locals: (user:, compact: false)
p = user.name
/# locals: (user:)
p = user.name
Rails/RenderInLoop
Available
A render of the same partial inside a loop; render collection: does it in one call.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today Inside collection.each do |item| or each_with_index, a render "partial/name", name: item that is all the loop does: a string literal for the partial, the block variable as its only local, and nothing else in the body (no wrapping element, no other output or code). Not reported for map and other iterators, render partial:, a partial name that is not a literal or a render with more locals. In Haml and Slim the render must be the only line nested under the loop. See Rails/RenderInLoop in the diagnostics reference.
[Rails.RenderInLoop]
enabled = false<% @users.each do |user| %>
<%= render "users/card", user: user %>
<% end %>
<%= render partial: "users/card", collection: @users, as: :user %>
- @users.each do |user|
= render "users/card", user: user
= render partial: "users/card", collection: @users, as: :user
- @users.each do |user|
= render "users/card", user: user
= render partial: "users/card", collection: @users, as: :user
Rails/DebugOutput
Available
Debugging left in a view: p, pp, puts, debug, byebug, debugger, binding.irb and binding.pry.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today p, pp, puts and debug with arguments, byebug, debugger, binding.irb and binding.pry, when the call is a whole statement or the whole of an output tag. A local variable named p, a method on a receiver (form.p, obj.debug), a call used as an argument and a call under an if or unless that tests Rails.env are not reported. Only in Rails projects, in templates under app/views. See Rails/DebugOutput in the diagnostics reference.
[Rails.DebugOutput]
enabled = false<%= debug @user %>
<% binding.irb %>
<p><%= @user.name %></p>
<p><%= @user.name %></p>
= debug @user
- binding.irb
%p= @user.name
%p= @user.name
= debug @user
- binding.irb
p = @user.name
p = @user.name
Rails/OutputControlFlow
Available
Control flow in an output tag: <%= if … %> where <% if … %> is meant.
Languages ERB
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today An ERB output tag (<%=, <%==) that starts with if, unless, while, until, case, for or begin and doesn't close it in the same tag, or with else, elsif, when, end, rescue or ensure. The quick fix removes the =. The Ruby syntax errors the = causes are not shown (for x in y do and while c do compile, but print the loop's value), so the message is the only one; other errors in the template still are. A complete expression (<%= if a then b else c end %>, <%= x if y %>) and form_with … do blocks are fine. Reported in every ERB template, as the syntax errors are. Haml and Slim accept = if with a nested block. See Rails/OutputControlFlow in the diagnostics reference.
[Rails.OutputControlFlow]
enabled = false<%= if @user %>
<p>hi</p>
<% end %>
<% if @user %>
<p>hi</p>
<% end %>
Rails/UnsafeOutput
Available
Output that skips Rails' HTML escaping: raw, .html_safe and <%==.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today raw x, x.html_safe and safe_concat x are reported on the method name, and the unescaped output markers <%== (ERB), != and !~ (Haml) and == (Slim, also before an attribute value) on the marker. A to_json value gets the hint to use json_escape. Not reported when the value is certainly safe: a literal without interpolation ("<br>".html_safe, raw " "), a t/translate call with a literal key ending in _html or .html, or a call to sanitize, safe_join, strip_tags, json_escape, j, escape_javascript, h, html_escape, escape_once, link_to, button_to, mail_to, content_tag, image_tag, simple_format, render, capture or tag.div and the other tag elements. <%== render … %> is redundant, not unsafe, so it is quiet. Haml's !== and Slim's #{{x}} are not looked at. Only in Rails projects, in templates under app/views. See Rails/UnsafeOutput in the diagnostics reference.
[Rails.UnsafeOutput]
enabled = false<%= raw @user.bio %>
<%= @user.bio.html_safe %>
<%== @user.bio %>
<a href="<%= raw @user.website %>">Website</a>
<div data-bio="<%= raw @user.bio.to_json %>"></div>
<%= sanitize @user.bio %>
<%= sanitize @user.bio %>
<%= sanitize @user.bio %>
<a href="<%= @user.website %>">Website</a>
<div data-bio="<%= @user.bio.to_json %>"></div>
!= @user.bio
= raw @user.bio
= sanitize @user.bio
= sanitize @user.bio
== @user.bio
= raw @user.bio
= sanitize @user.bio
= sanitize @user.bio
Rails/UnusedBlockArgument
Available
A block argument the block never uses.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today A plain or optional block parameter that nothing in the block reads, assigns or interpolates, nested blocks included. Not reported: names starting with _, it and numbered parameters, destructured, *rest, keyword and &block parameters, or a block that calls binding, local_variables or eval. The message suggests removing the argument when it is the block's only one, and the quick fix prefixes it with _. See Rails/UnusedBlockArgument in the diagnostics reference.
[Rails.UnusedBlockArgument]
enabled = false<% @users.each do |user, index| %>
<li><%= user.name %></li>
<% end %>
<% @users.each do |user| %>
<li><%= user.name %></li>
<% end %>
- @users.each do |user, index|
%li= user.name
- @users.each do |user|
%li= user.name
- @users.each do |user, index|
li = user.name
- @users.each do |user|
li = user.name
Rails/I18nMissingKey
Available
A lazy translation key, such as t(".title"), with no entry in config/locales.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today A literal lazy key (t(".title"), translate, with or without parentheses) is turned into its full key as Rails does (users/_form gives users.form.title, a layout keeps its folder) and must be in the default locale of the YAML files under config/locales: config.i18n.default_locale, en without one. Absolute keys (t("users.index.title")) are not checked, since gems supply keys such as date.formats and helpers.submit.create; nor is a key with default:, scope: or locale:, a key with interpolation, or one in a view named for a gem in the Gemfile.lock (devise/sessions/new, a copy of the gem's view). The closest key of the view within two edits is offered. Nothing is reported without a config/application.rb or a locale file for the default locale, when the app has Ruby locale files, adds to I18n.load_path, sets I18n.backend (also config.i18n.load_path, railties_load_path) or uses a gem that keeps translations in a store (i18n-active_record, redis-i18n). Rails projects only, in templates under app/views. See Rails/I18nMissingKey in the diagnostics reference.
[Rails.I18nMissingKey]
enabled = falseconfig/locales/en.yml has en.users.index.title.
<h1><%= t(".titel") %></h1>
<h1><%= t(".title") %></h1>
%h1= t(".titel")
%h1= t(".title")
h1 = t(".titel")
h1 = t(".title")
Rails/UnknownRoute
Available
A *_path or *_url helper that config/routes.rb doesn't define.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today A *_path or *_url call without a receiver that no route of the app defines but that is within two edits of one (usr_path for user_path) is reported, with that route offered; a name with no route near it may come from a gem, a concern or lib, which can't be seen, so it is left alone. The routes are read with bin/rails runner once the editor has loaded them (setting rubyTemplates.routes.enabled), so it is reported in the editor and never by ruby-templates check. Not reported: a call on a receiver (main_app.users_path, admin.users_path for an engine), Rails' asset and polymorphic helpers (asset_path, image_url, polymorphic_path, …), rails_*, turbo_*, action_mailbox_*, omniauth_*, vite_* and asset_pack_* helpers, Devise's own helpers (session_path, new_registration_url, edit_password_url, …) when the Gemfile.lock has devise, a view in a folder named for a gem (spree/, rails_admin/), which uses the engine's routes, a helper the app defines in app/helpers or names with a symbol (helper_method, delegate, define_method), a call under defined?, a route declared with direct, and a local variable or block parameter. A bare name (item_path with resources :items) could be a local the caller passes, so it is reported only when called with arguments or when the template's strict locals don't list it. Rails projects only, in templates under app/views. See Rails/UnknownRoute in the diagnostics reference.
[Rails.UnknownRoute]
enabled = falseconfig/routes.rb has resources :users. The editor loads the routes, so ruby-templates check doesn't report this.
<%= link_to @user.name, usr_path(@user) %>
<%= link_to @user.name, user_path(@user) %>
The routes are the same.
= link_to @user.name, usr_path(@user)
= link_to @user.name, user_path(@user)
The routes are the same.
= link_to @user.name, usr_path(@user)
= link_to @user.name, user_path(@user)
Turbo and Stimulus
Turbo/PreferTurboAttributes
Available
The Turbo attributes in place of the Rails UJS ones: data-turbo-confirm, data-turbo-method and data-turbo-submits-with.
Languages ERB, Haml, and Slim
Type Autofix, run by the formatter. Off by default.
Rendered output Not safe. It renames HTML attributes, which changes the page: the new ones work only in an app that runs Turbo, the old ones only with Rails UJS, so an app that still relies on UJS for a link or button loses it. That is why the rule is off until enabled. The formatter reads every element's attributes before and after and refuses the rewrite unless only the renamed ones differ, then checks the renamed text as it checks any file, so nothing else changes.
Status Available.
Today On links (<a>, link_to, tag.a, content_tag :a), data-method becomes data-turbo-method, and data-confirm becomes data-turbo-confirm when the link sends a method, since Turbo asks to confirm only such a link. On submit buttons (<button> with no type or type="submit", <input type="submit">, submit_tag, button_tag, button_to, and submit or button on the builder of a form_with, form_for or fields_for block), data-disable-with becomes data-turbo-submits-with. In the options of a helper, the keys of data: { } are renamed in the spelling they have (confirm: becomes turbo_confirm:, "disable-with" => becomes "turbo-submits-with" =>), as is a key such as "data-method" =>. Left alone: a data-confirm on a link without a method, button_to's confirm, which Turbo reads from the form, link_to … method: :delete, which is not a data attribute, an attribute whose Turbo name the element has already, a button of another type or whose attributes are not all readable, disable_with: false and a confirm: with no value (which reads a variable), other elements, and keys and attribute names that are not literal. It runs before the other formatter rules, whose checks then apply to the renamed text. Format selection applies it to the lines it covers.
[Turbo.PreferTurboAttributes]
enabled = true
<%= link_to "Delete", user_path(user), data: { confirm: "Sure?", method: :delete } %>
<%= submit_tag "Save", data: { disable_with: "Saving..." } %>
<a href="/users/1" data-confirm="Sure?" data-method="delete">Delete</a>
<%= link_to "Delete", user_path(user), data: { turbo_confirm: "Sure?", turbo_method: :delete } %>
<%= submit_tag "Save", data: { turbo_submits_with: "Saving..." } %>
<a href="/users/1" data-turbo-confirm="Sure?" data-turbo-method="delete">Delete</a>
= link_to "Delete", user_path(user), data: { confirm: "Sure?", method: :delete }
%input{type: "submit", data: {disable_with: "Saving..."}}
= link_to "Delete", user_path(user), data: { turbo_confirm: "Sure?", turbo_method: :delete }
%input{type: "submit", data: {turbo_submits_with: "Saving..."}}
= link_to "Delete", user_path(user), data: { confirm: "Sure?", method: :delete }
a href="/users/1" data-method="delete" data-confirm="Sure?" Delete
= link_to "Delete", user_path(user), data: { turbo_confirm: "Sure?", turbo_method: :delete }
a href="/users/1" data-turbo-method="delete" data-turbo-confirm="Sure?" Delete
Turbo/PermanentRequiresId
Available
An element with data-turbo-permanent and no id, which Turbo needs to match it across pages.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today Reported on the attribute, in ERB start tags, Haml and Slim tags, and the literal options of tag.div, content_tag, link_to and the form and button helpers. Any value counts, as Turbo looks only for the attribute. An id of any value satisfies it, so does #id in Haml and Slim and an object reference such as %div[@user]. Not reported where attributes may come from code that can't be read (ERB inside the start tag, a Haml **attrs or data: attrs, a Slim *attrs or data= hash, a helper's **options) or when the value is not a literal (data: { turbo_permanent: flag }). Rails projects only, in templates under app/views. See Turbo/PermanentRequiresId in the diagnostics reference.
[Turbo.PermanentRequiresId]
enabled = false<div data-turbo-permanent>
<%= render 'shared/player' %>
</div>
<div id="player" data-turbo-permanent>
<%= render 'shared/player' %>
</div>
%div{data: {turbo_permanent: true}}
= render 'shared/player'
#player{data: {turbo_permanent: true}}
= render 'shared/player'
div data-turbo-permanent=true
= render 'shared/player'
#player data-turbo-permanent=true
= render 'shared/player'
Stimulus/UnknownController
Available
A data-controller name with no controller file in app/javascript/controllers.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today Each name in a static data-controller (ERB, Haml and Slim attributes, data: { controller: "…" } in Haml and in the options of tag, content_tag, link_to, button_to and the form helpers) must be a file in app/javascript/controllers (search_controller.js, admin/users_controller.js for admin--users; -controller, .ts, .jsx, .tsx and .mjs too) or a name a script in app/javascript registers with application.register("name", …); the closest controller within two edits is offered. Nothing is reported when the app has no app/javascript/controllers folder, when controllers are loaded from another folder (eagerLoadControllersFrom("components", …), a context or glob outside the controllers folder, an importmap pin) or registered under a name the code computes, or for a name with ERB or interpolation in it. Rails projects only, in templates under app/views. See Stimulus/UnknownController in the diagnostics reference.
[Stimulus.UnknownController]
enabled = falseapp/javascript/controllers/search_controller.js exists.
<div data-controller="serch">
<%= text_field_tag :q %>
</div>
<div data-controller="search">
<%= text_field_tag :q %>
</div>
%div{data: {controller: "serch"}}
= text_field_tag :q
%div{data: {controller: "search"}}
= text_field_tag :q
div data-controller="serch"
= text_field_tag :q
div data-controller="search"
= text_field_tag :q
Stimulus/UnknownTargetOrAction
Available
A data-*-target or data-action that names a target or method its controller doesn't have.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today Checks the static data-<controller>-target names (several may be separated by spaces) against the static targets = ["…"] of the controller, and each method of data-action (click->search#go, search#go, several descriptors, :prevent-style options, @window and @document, key filters such as keydown.enter->) against the methods, accessors and fields its class defines; connect, disconnect and initialize always count. The same goes for data: { search_target: "…", action: "…" } in Haml and in the options of the tag, link, button and form helpers. The closest name within two edits is offered. A controller is only read when its identifier is one file in app/javascript/controllers and the app registers its controllers where this can see them (as for Stimulus/UnknownController); the file is read as text, and a base controller it extends is followed when it is imported from a relative path or controllers/…. Nothing is reported for a controller that extends something other than Controller from @hotwired/stimulus and a base controller that can't be followed (a package), has a static targets that is no plain list of strings, defines members by computed names, decorators or Object.assign(…prototype…), has more than one class, or is registered by name with application.register instead of a file. ERB, interpolation and data-action values without a # are skipped. Rails projects only, in templates under app/views. See Stimulus/UnknownTargetOrAction in the diagnostics reference.
[Stimulus.UnknownTargetOrAction]
enabled = falsesearch_controller.js has static targets = ["query"] and a submit() method.
<div data-controller="search">
<input data-search-target="querry" data-action="input->search#sbumit">
</div>
<div data-controller="search">
<input data-search-target="query" data-action="input->search#submit">
</div>
%div{data: {controller: "search"}}
%input{data: {search_target: "querry", action: "input->search#sbumit"}}
%div{data: {controller: "search"}}
%input{data: {search_target: "query", action: "input->search#submit"}}
div data-controller="search"
input data-search-target="querry" data-action="input->search#sbumit"
div data-controller="search"
input data-search-target="query" data-action="input->search#submit"
HTML and accessibility
Html/ImgAlt
Available
An image without alt text.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today For <img> (ERB start tags, %img and img) and for image_tag and tag.img without alt:, in templates that output HTML (not .js.erb, .text.erb or .xml.haml). An empty alt="" counts as alt text, and so does generator-unable-to-provide-required-alt. Not reported where attributes may come from code that can't be read (ERB inside the start tag, a Haml **attrs, a Slim *attrs, a helper's **options or options in a variable). See Html/ImgAlt in the diagnostics reference.
[Html.ImgAlt]
enabled = false<img src="<%= @user.avatar_url %>">
<%= image_tag @user.avatar_url %>
<img src="<%= @user.avatar_url %>" alt="<%= @user.name %>">
<%= image_tag @user.avatar_url, alt: @user.name %>
%img{src: @user.avatar_url}
= image_tag @user.avatar_url
%img{src: @user.avatar_url, alt: @user.name}
= image_tag @user.avatar_url, alt: @user.name
img src=@user.avatar_url
= image_tag @user.avatar_url
img src=@user.avatar_url alt=@user.name
= image_tag @user.avatar_url, alt: @user.name
Html/DuplicateId
Available
The same static id twice in one template.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today At the second occurrence, in templates that output HTML. In Haml and Slim, #id and id: count. Ids that hold ERB or interpolation, ids in different branches of a conditional (ERB if/else, Haml and Slim - if/- else) and content inside <template> are left alone; in Haml and Slim, so are ids where one of them sits in a conditional, a loop or a block the other is not in. A Haml element with both #id and id: gets one joined id and is skipped. See Html/DuplicateId in the diagnostics reference.
[Html.DuplicateId]
enabled = false<div id="profile">
<%= @user.name %>
</div>
<div id="profile"></div>
<div id="profile">
<%= @user.name %>
</div>
<div id="settings"></div>
#profile
= @user.name
#profile
#profile
= @user.name
#settings
#profile
= @user.name
#profile
#profile
= @user.name
#settings
Html/UnexpectedCloseTag
Available
An end tag with no open element of that name.
Languages ERB
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today In ERB with an HTML host, including the end tag of a void element. A stray end tag in a partial is not reported, since a partial may close what another one opened. See Html/UnexpectedCloseTag in the diagnostics reference.
[Html.UnexpectedCloseTag]
enabled = false<div class="profile">
<p><%= @user.name %></p>
</div>
</div>
<div class="profile">
<p><%= @user.name %></p>
</div>
Html/MismatchedCloseTag
Available
An end tag that doesn't close the innermost open element.
Languages ERB
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today In ERB with an HTML host, partials included. When the end tag crosses an open element, the browser could recover in more than one way, so checking stops there. See Html/MismatchedCloseTag in the diagnostics reference.
[Html.MismatchedCloseTag]
enabled = false<div class="profile">
<span><%= @user.name %></div>
<div class="profile">
<span><%= @user.name %></span>
</div>
Html/UnclosedElement
Available
An element whose end tag never comes.
Languages ERB
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today In ERB with an HTML host, at the start tag. Elements whose end tag HTML lets you omit, void elements, partials and files that end inside an ERB block are left alone. See Html/UnclosedElement in the diagnostics reference.
[Html.UnclosedElement]
enabled = false<section class="users">
<h2>Users</h2>
<section class="users">
<h2>Users</h2>
</section>
Html/VoidChildren
Available
Content inside a void element such as img or input.
Languages Haml and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today Haml reports content on the tag's line or nested under it; Slim reports lines nested under a void element, in HTML templates only. See Html/VoidChildren in the diagnostics reference.
[Html.VoidChildren]
enabled = falseimg src="/logo.png"
| Logo
img src="/logo.png" alt="Logo"
Html/DuplicateAttribute
Available
An attribute given twice on one tag.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today At the second one, for names written out in ERB start tags, Haml tags (the (…) list and the {…} hash, data: { … } keys included) and Slim tags, and in the literal options of tag.div, content_tag, link_to and the form and button helpers, in templates that output HTML. class and id are repeated on purpose in Haml, Slim and the helpers, which merge them (.card{class: "wide"}), so only plain ERB start tags are checked for them. An ERB start tag with ERB inside is left alone, since it may pick one of two attributes. Slim itself rejects a repeated attribute other than class, so there it is an error with Slim's message, Multiple href attributes specified. See Html/DuplicateAttribute in the diagnostics reference.
[Html.DuplicateAttribute]
enabled = false<div class="card" class="wide">
<%= user.name %>
</div>
<div class="card wide">
<%= user.name %>
</div>
%a{href: "/users", href: "/people"} Users
%a{href: "/users"} Users
Slim's own error, Multiple href attributes specified, is reported as Syntax/Template.
a href="/users" href="/people" Users
a href="/users" Users
Html/NestedInteractive
Available
An interactive element inside another: a link in a link, a form in a form, including link_to and form_with.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today Reported on the inner element's tag name (or helper name). Inside an <a> or <button> (also link_to, tag.a, button_tag, content_tag :button with a block) these are interactive content and can't be nested (HTML 4.5.1): <a> (inside another <a> always, inside a <button> when it has an href), <button>, <input> (not type="hidden"), <select>, <textarea>, <label>, <iframe>, <embed>, <details>, <audio> and <video> with controls, and <img> with usemap. A <form> can't contain a form (HTML 4.10.3), and button_to renders a form around its button, so it counts as both, inside form_with, form_for, form_tag and <form>. Nesting is the template's own: ERB by the matching start and end tags and do … end blocks of the helper, Haml and Slim by indentation (an element on the same line as its parent, li: a …, isn't followed). Elements whose start and end tags sit in different branches of an if, partials rendered inside the element, a layout's yield, and anything inside <svg>, <math> or <template> are not followed. A <label> around an <input>, a <summary> in <details>, a <button form="…"> outside its form and a hidden input are fine. Templates that output HTML only. See Html/NestedInteractive in the diagnostics reference.
[Html.NestedInteractive]
enabled = false<%= link_to user_path(user) do %>
<%= link_to "Edit", edit_user_path(user) %>
<% end %>
<%= link_to user_path(user) do %>
<%= user.name %>
<% end %>
<%= link_to "Edit", edit_user_path(user) %>
button_to renders its own <form>.
<%= form_with model: @user do |f| %>
<%= f.text_field :name %>
<%= button_to "Delete", @user, method: :delete %>
<% end %>
<%= form_with model: @user do |f| %>
<%= f.text_field :name %>
<% end %>
<%= button_to "Delete", @user, method: :delete %>
%a{href: "/users"}
%a{href: "/users/new"} New
%a{href: "/users"} Users
%a{href: "/users/new"} New
a href="/users"
a href="/users/new" New
a href="/users" Users
a href="/users/new" New
Html/AnchorHref
Available
An <a> without an href, which is not a link and cannot be reached with the keyboard.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today On the tag name of <a> in ERB start tags and of %a and a in Haml and Slim, in templates that output HTML. link_to always has an href and is not checked. An <a> with a name (the old way to mark a place to jump to), a tabindex (the keyboard can reach it), an id and no text of its own (a target to jump to), or an attribute ending in :href (:href, x-bind:href, xlink:href) is left alone, and so is one inside an <svg>, and so is one whose attributes may come from code that can't be read (ERB inside the start tag, a Haml **attrs or data: attrs, a Slim *attrs or data= hash). See Html/AnchorHref in the diagnostics reference.
[Html.AnchorHref]
enabled = false<a class="btn" data-action="click->menu#open">Menu</a>
<button class="btn" data-action="click->menu#open">Menu</button>
%a.btn{data: {action: "click->menu#open"}} Menu
%button.btn{data: {action: "click->menu#open"}} Menu
a.btn data-action="click->menu#open" Menu
button.btn data-action="click->menu#open" Menu
A11y/IframeTitle
Available
An <iframe> without a title, which screen readers use to name it.
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today On the tag name of an <iframe> in ERB start tags, Haml and Slim tags, and of tag.iframe and content_tag :iframe, in templates that output HTML. An aria-label or aria-labelledby (also in an aria: { label: … } hash) names the frame as well. Not reported where attributes may come from code that can't be read (ERB inside the start tag, **attrs, *attrs). See A11y/IframeTitle in the diagnostics reference.
[A11y.IframeTitle]
enabled = false<iframe src="https://www.youtube.com/embed/<%= video.youtube_id %>"></iframe>
<iframe src="https://www.youtube.com/embed/<%= video.youtube_id %>" title="<%= video.title %>"></iframe>
%iframe{src: "https://www.youtube.com/embed/#{video.youtube_id}"}
%iframe{src: "https://www.youtube.com/embed/#{video.youtube_id}", title: video.title}
iframe src="https://www.youtube.com/embed/#{video.youtube_id}"
iframe src="https://www.youtube.com/embed/#{video.youtube_id}" title=video.title
A11y/GenericLinkText
Available
Link text that says nothing out of context: "click here", "here", "read more".
Languages ERB, Haml, and Slim
Type Warning, shown as a diagnostic.
Rendered output Safe. It only reports; nothing is rewritten.
Status Available.
Today On the text of an <a> that holds only text (ERB, Haml and Slim tags) and of a link_to, tag.a or content_tag :a given a string literal: "click here", "here", "read more", "learn more", "more", "more info", "more information", "link", "this link" and "click", in any case and ignoring punctuation. A link with an aria-label, aria-labelledby or aria-describedby (also in an aria: { … } hash), with ERB, interpolation, other elements or code in its content, or whose attributes may come from code that can't be read is left alone. See A11y/GenericLinkText in the diagnostics reference.
[A11y.GenericLinkText]
enabled = false<p>Our new pricing is live. <%= link_to "Click here", pricing_path %></p>
<p><%= link_to "See the new pricing", pricing_path %></p>
%p= link_to "Click here", pricing_path
%p= link_to "See the new pricing", pricing_path
p = link_to "Click here", pricing_path
p = link_to "See the new pricing", pricing_path