Skip to content

Linter: Implement actionview-prefer-pluralize-helper rule - #1868

Open
joaoGabriel55 wants to merge 6 commits into
marcoroth:mainfrom
joaoGabriel55:add-Linter-Rule-TextHelper-pluralize-over-String-pluralize
Open

Linter: Implement actionview-prefer-pluralize-helper rule#1868
joaoGabriel55 wants to merge 6 commits into
marcoroth:mainfrom
joaoGabriel55:add-Linter-Rule-TextHelper-pluralize-over-String-pluralize

Conversation

@joaoGabriel55

@joaoGabriel55 joaoGabriel55 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Overview

Adds a new linter rule, erb-prefer-pluralize-helper, that flags calls to String#pluralize with a count argument in ERB templates and recommends the ActionView::Helpers::TextHelper#pluralize helper instead.

The helper form (pluralize("Alias", count)) prepends the count to the output and reads naturally in a template, whereas "Alias".pluralize(count) typically forces the count to be rendered separately — leading to duplicated output like <%= aliases.size %> <%= "Alias".pluralize(aliases.size) %>.

<!-- 🚫 Bad -->
<%= aliases.size %> Known <%= "Alias".pluralize(aliases.size) %>

<!-- ✅ Good -->
Known <%= pluralize("Alias", aliases.size) %>

The offense message includes a concrete, source-derived suggestion, e.g.:

Prefer the pluralize helper over String#pluralize for counts. Use <%= pluralize("Alias", aliases.size) %> instead.

Related Issue

Closes #1723

Affected Component(s)

  • 🔎 Linter
  • 📚 Documentation

Type of Change

  • ✨ New feature (non-breaking change that adds functionality)

Main Technical Changes

  • New rule erb-prefer-pluralize-helper (javascript/packages/linter/src/rules/erb-prefer-pluralize-helper.ts), modeled on the existing Prism-based erb-no-debug-output rule:
    • Uses parserOptions: { prism_program: true } and a PrismVisitor to walk CallNodes.
    • Flags a call when the method name is pluralize, the receiver is a string literal (StringNode / InterpolatedStringNode), and it has at least one argument (the count). This scoping avoids false positives on non-string receivers (e.g. model.pluralize(count)) and on the argument-less "Alias".pluralize, matching the "for counts" intent of the issue.
    • Default severity: warning; introducedIn: "unreleased".
    • The suggested fix is reconstructed from the original source spans of the receiver and arguments via a small slice() helper.
  • Registered the rule in src/rules.ts and re-exported it from src/rules/index.ts.
  • Added docs page docs/rules/erb-prefer-pluralize-helper.md and linked it from docs/rules/README.md (required by the rule-docs completeness test).

How to Test

cd javascript/packages/linter
npx vitest run test/rules/erb-prefer-pluralize-helper.test.ts   # new rule tests (9)
npx vitest run test/rule-docs.test.ts                           # docs completeness
npx tsc -b                                                      # typecheck

The rule test covers valid cases (helper form, argument-less String#pluralize, non-string receivers, unrelated string methods) and invalid cases (string-literal pluralize with a count in both <%= %> and <% %> tags).

Checklist

  • 🧪 Tests added or updated
  • 🔨 Ran bin/integration locally (ran the linter package test suite: 2438 passed)
  • 📚 Documentation updated (if applicable)
  • 🧭 Commit messages follow the Component: Description convention (Linter: Add \erb-prefer-pluralize-helper` rule`)

@github-actions github-actions Bot added documentation Improvements or additions to documentation linter @herb-tools/linter for HTML+ERB templates typescript TypeScript source across the javascript/ packages linter-rule Individual linter rules and their documentation labels Jul 22, 2026
Comment thread javascript/packages/linter/docs/rules/erb-prefer-pluralize-helper.md Outdated
@marcoroth marcoroth changed the title Linter: Add erb-prefer-pluralize-helper rule Linter: Add actionview-prefer-pluralize-helper rule Jul 29, 2026
@marcoroth marcoroth added this to the v0.11.0 milestone Jul 30, 2026
@marcoroth marcoroth changed the title Linter: Add actionview-prefer-pluralize-helper rule Linter: Implement actionview-prefer-pluralize-helper rule Aug 2, 2026
@github-actions github-actions Bot added formatter @herb-tools/formatter for HTML+ERB templates parser HTML+ERB parser ruby Ruby source for the gem and its libraries javascript JavaScript source across the javascript/ packages wasm WebAssembly build and bindings vscode VS Code editor support language-server @herb-tools/language-server and Language Server Protocol support c C source for the core parser, lexer, and AST highlighter @herb-tools/highlighter syntax highlighting and diagnostic rendering printer @herb-tools/printer AST printing and lossless reconstruction playground The Herb playground web app node @herb-tools/node native Node.js addon node-wasm @herb-tools/node-wasm WebAssembly parser for Node.js c-extension Ruby C extension in ext/ rbs RBS type signatures in sig/ engine Herb engine and Rails template compilation rubygem The herb RubyGem and its packaging config Herb configuration file handling and config options rust Rust bindings and the Herb Rust crate java Java bindings and the org.herb package linter-autofix Linter autofix behavior dev-tools @herb-tools/dev-tools visual debugging for HTML+ERB templates labels Aug 3, 2026
@marcoroth marcoroth removed linter-rule Individual linter rules and their documentation highlighter @herb-tools/highlighter syntax highlighting and diagnostic rendering printer @herb-tools/printer AST printing and lossless reconstruction playground The Herb playground web app node @herb-tools/node native Node.js addon node-wasm @herb-tools/node-wasm WebAssembly parser for Node.js c-extension Ruby C extension in ext/ rbs RBS type signatures in sig/ engine Herb engine and Rails template compilation rubygem The herb RubyGem and its packaging config Herb configuration file handling and config options rust Rust bindings and the Herb Rust crate java Java bindings and the org.herb package dev-tools @herb-tools/dev-tools visual debugging for HTML+ERB templates core @herb-tools/core shared AST nodes, interfaces, and utilities stimulus-lint stimulus-lint rules for Stimulus controllers and view templates cpp C++ source, primarily the WebAssembly bindings rewriter @herb-tools/rewriter AST and string transformation system tailwind-class-sorter @herb-tools/tailwind-class-sorter Tailwind CSS class sorting browser @herb-tools/browser WebAssembly parser for browsers language-service @herb-tools/language-service HTML+ERB language service action-view-helpers Action View helper support and metadata labels Aug 3, 2026
@joaoGabriel55

Copy link
Copy Markdown
Contributor Author

QA - 08/04

image image

@joaoGabriel55

Copy link
Copy Markdown
Contributor Author

@marcoroth Sorry about those labels again 😞 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action-view-helpers Action View helper support and metadata c C source for the core parser, lexer, and AST c-extension Ruby C extension in ext/ config Herb configuration file handling and config options core @herb-tools/core shared AST nodes, interfaces, and utilities cpp C++ source, primarily the WebAssembly bindings dev-client @herb-tools/client HMR client for live DOM patching diff Herb Syntax Tree Diff Engine documentation Improvements or additions to documentation engine Herb engine and Rails template compilation formatter @herb-tools/formatter for HTML+ERB templates highlighter @herb-tools/highlighter syntax highlighting and diagnostic rendering java Java bindings and the org.herb package javascript JavaScript source across the javascript/ packages language-server @herb-tools/language-server and Language Server Protocol support lexer HTML+ERB lexer linter @herb-tools/linter for HTML+ERB templates linter-autofix Linter autofix behavior linter-rule Individual linter rules and their documentation node @herb-tools/node native Node.js addon parser HTML+ERB parser playground The Herb playground web app rbs RBS type signatures in sig/ ruby Ruby source for the gem and its libraries rubygem The herb RubyGem and its packaging rust Rust bindings and the Herb Rust crate typescript TypeScript source across the javascript/ packages vscode VS Code editor support wasm WebAssembly build and bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linter Rule: Prefer ActionView::Helpers::TextHelper#pluralize over String#pluralize for counts

2 participants