Skip to content

Ruby: Add RuboCop support for Ruby in ERB templates - #2260

Draft
joelhawksley wants to merge 10 commits into
marcoroth:mainfrom
joelhawksley:joelhawksley-add-erb-rubocop
Draft

joelhawksley wants to merge 10 commits into
marcoroth:mainfrom
joelhawksley:joelhawksley-add-erb-rubocop

Conversation

@joelhawksley

@joelhawksley joelhawksley commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

This adds rubocop-herb, a RuboCop plugin that runs a project鈥檚 configured RuboCop cops against Ruby embedded in .erb and .herb templates.

The plugin uses Herb's extract_ruby binding to produce position-preserving Ruby for the entire template and passes it through RuboCop鈥檚 native ruby_extractors API. This preserves control flow and variable usage across ERB tags while mapping offenses and autocorrections back to their original template locations. Existing configuration continues to work, including custom cops, inherited configuration, exclusions, target Ruby versions, --only, and safe or unsafe autocorrection.

Cops that depend on physical Ruby file contents or obscured template layout are excluded for template files by default. The integration lives under gems/rubocop-herb and is enabled through the standard RuboCop plugin configuration:

bundle add rubocop-herb
plugins:
  - rubocop-herb

This PR implements responsibility (2) from the review discussion. Running Herb鈥檚 own linter rules as part of RuboCop remains separate future work for the herb-linter integration.

@github-actions github-actions Bot added documentation Improvements or additions to documentation ruby Ruby source for the gem and its libraries typescript TypeScript source across the javascript/ packages rubygem The herb RubyGem and its packaging config Herb configuration file handling and config options rbs RBS type signatures in sig/ rust Rust bindings and the Herb Rust crate labels Aug 17, 2026
@marcoroth
marcoroth marked this pull request as draft August 21, 2026 16:47

@marcoroth marcoroth left a comment •

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting this started!

I reserved the rubocop-herb gem name to run the Herb Linter as part of a regular RuboCop run, but I could also see that gem to have tighter RuboCop integration in the future for what you are proposing here.

I think if we'd want to land something like this feature, it probably belongs into rubocop-herb.

@marcoroth marcoroth changed the title Add RuboCop support for Ruby in ERB templates Ruby: Add RuboCop support for Ruby in ERB templates Sep 12, 2026
@joelhawksley

Copy link
Copy Markdown
Contributor Author

I reserved the rubocop-herb gem name to run the Herb Linter as part of a regular RuboCop run, but I could also see that gem to have tighter RuboCop integration in the future for what you are proposing here.

I am suggesting the opposite (I think?): we should run RuboCop as part of Herb, not the other way around. I don't know if the semantics matter in this case though.

I think if we'd want to land something like this feature, it probably belongs into rubocop-herb.

@marcoroth my mental model of the Herb linter has been that it is meant to replace ERB Lint out of the box, and ERB Lint supports RuboCop as one of its linters: https://github.com/Shopify/erb_lint#linters. I think dropping ERB Lint (which doesn't get much attention these days) is a good value proposition for adopting Herb.

I'd like to drop our use of ERB Lint at GitHub, but we use the ERB Lint RuboCop linter, so this is a blocker. Which means I'm motivated to get rubocop-herb working, if you're looking for someone to take the work on.

@marcoroth

Copy link
Copy Markdown
Owner

Yeah, I think that rubocop-herb would have two responsibilities:

  1. to run Herb's own Linter rules as part of a RuboCop run (which is most likely going to be powered by the herb-linter gem)
  2. to run the configured RuboCop rules (minus the ones that don't make sense) on the Ruby inside the ERB templates

This PR is providing the work for 2). So, if you'd want to migrate the mechanics of this pull request into gems/rubocop-herb/* and cover the 2) aspect I'd be more than happy to get that going. Thank you! 馃檹馃徏

@joelhawksley
joelhawksley marked this pull request as ready for review September 18, 2026 17:23
@joelhawksley

Copy link
Copy Markdown
Contributor Author

Alright @marcoroth, I've gotten this working to the point of catching RuboCop errors in .html.erb files in other projects: discourse/discourse#43828. Let me know what you think 鉂わ笍

ZogStriP pushed a commit to discourse/discourse that referenced this pull request Sep 18, 2026
As part of testing marcoroth/herb#2260, I ran
the PR against this repository. `rubocop-herb` found and autofixed
RuboCop rule violations in six .html.erb files, using the existing
RuboCop rule configuration for this project.
@joelhawksley
joelhawksley force-pushed the joelhawksley-add-erb-rubocop branch from d1d11f6 to 5705225 Compare October 1, 2026 19:36
joelhawksley and others added 3 commits October 1, 2026 13:51
Replace the duplicate Ruby source builder with Herb.extract_ruby while retaining range collection for safe autocorrection. Normalize masked multibyte characters to RuboCop's character offsets.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Inline the minimal RuboCop extraction hook around Herb.extract_ruby and remove the standalone extractor, range collector, and autocorrection restriction patches.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Refresh the path gem lock entry for Herb 0.11.0 and align extraction tests with the current Herb binding behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@joelhawksley
joelhawksley force-pushed the joelhawksley-add-erb-rubocop branch from 4472bc2 to 75e2ebe Compare October 1, 2026 21:06
Comment thread gems/rubocop-herb/config/default.yml Outdated
Comment thread gems/rubocop-herb/config/default.yml Outdated
Comment thread gems/rubocop-herb/config/default.yml Outdated
Comment thread gems/rubocop-herb/config/default.yml Outdated
Comment thread gems/rubocop-herb/config/default.yml Outdated
Comment thread gems/rubocop-herb/config/default.yml Outdated
Comment thread gems/rubocop-herb/config/default.yml Outdated
Comment thread gems/rubocop-herb/config/default.yml Outdated
joelhawksley and others added 3 commits October 2, 2026 10:39
Signed-off-by: Joel Hawksley <joel@hawksley.org>
Register every .erb and .herb file for RuboCop extraction, apply template-specific exclusions consistently, and cover non-HTML ERB and Herb files in integration tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Register the Herb extractor directly through RuboCop::Runner.ruby_extractors, matching RuboCop's established template plugins.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread gems/rubocop-herb/lib/rubocop/herb/processed_source_builder.rb Outdated
Comment thread gems/rubocop-herb/rubocop-herb.gemspec Outdated
joelhawksley and others added 4 commits October 6, 2026 08:58
Co-authored-by: Marco Roth <marco.roth@intergga.ch>
Signed-off-by: Joel Hawksley <joel@hawksley.org>
Remove the single-use ProcessedSourceBuilder class and construct extracted RuboCop sources directly in the plugin while preserving parser and configuration metadata.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use Herb's version directly, replace Pathname with File.expand_path, keep the extractor as a Method, and streamline source construction naming and calls.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep invalid extracted sources in RuboCop's pipeline so Lint/Syntax can report malformed Ruby, while continuing to skip valid template fragments with no AST.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@joelhawksley

Copy link
Copy Markdown
Contributor Author

Moving back to draft while I look at byte vs. character position logic gaps between RuboCop and Herb 鉂わ笍

This branch has not been deployed

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

Labels

config Herb configuration file handling and config options documentation Improvements or additions to documentation 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants