How do you add rules to Standard Ruby with plugins or extend_config in .standard.yml, and why can neither change Standard's own rules?
answer
- plugins: gems built on lint_roller
- default_lint_roller_plugin in the gemspec
- extend_config: plain RuboCop YAML files
- first-in-wins per cop
- built-ins load first
basics
~20 sList lint_roller plugin gems such as standard-rails under plugins:, or RuboCop YAML files under extend_config:. Configuration is first-in-wins: Standard's built-in plugins load first, so later plugins and files can add cops but never reconfigure ones already set.
solid answer
~40 sStandard has two extension points in `.standard.yml`. **`plugins:`** names gems built on **`lint_roller`**, the small plugin API shared with RuboCop 1.72+: the gem's gemspec declares `default_lint_roller_plugin` with a class that returns its rules, and a plugin can take options, as in `- standard-rails: { target_rails_version: 7.0 }`. **`extend_config:`** lists ordinary RuboCop YAML files, useful for a local cop or a gem that is not a plugin. Standard merges configuration **first-in-wins**: its three built-in plugins (`standard-base`, `standard-custom`, `standard-performance`) load first, then your plugins in order, then extend files. Once a cop is configured, later sources cannot change it, and plugins cannot set `AllCops` keys such as `TargetRubyVersion`, `Include`, `Exclude` or `DisabledByDefault`. So you can add cops but not relax Standard's.
code
yaml · 7 lines# .standard.yml
plugins:
- standard-rails:
target_rails_version: 7.0
extend_config:
- .custom_cops.ymlgo deeper
Recall that plugins: adds rule gems such as standard-rails and extend_config: adds RuboCop YAML files.
Explain lint_roller, the default_lint_roller_plugin metadata, plugin options, and why extend_config cops need Enabled: true under DisabledByDefault.
Explain first-in-wins and the disallowed AllCops keys, and what that leaves when a team wants to change a built-in rule.
Decide whether shared house rules should ship as a lint_roller plugin usable by both RuboCop and Standard, and what staying extend-only costs.
## Two ways to add rules Standard's own ruleset is fixed, but a project can add more cops on top. | Mechanism | What you list | Typical use | |---|---|---| | `plugins:` | gems that implement a `lint_roller` plugin | framework rules such as standard-rails | | `extend_config:` | paths to RuboCop YAML files | a project's own cops, or a gem that is not a plugin | ### `plugins:` **`lint_roller`** is a small gem that defines a plugin interface for linters: a class inheriting from `LintRoller::Plugin` with `about` (name, version), `supported?(context)` and `rules(context)`, which returns a `LintRoller::Rules` object pointing at a RuboCop-format YAML file or carrying the settings directly. RuboCop 1.72 adopted the same interface for its `plugins:` key, so one plugin gem can serve both tools. To use one with Standard: 1. add the gem to the `Gemfile`; 2. list it under `plugins:` in `.standard.yml`; 3. optionally pass options in a nested hash, which the plugin receives when it is created: `- standard-rails: { target_rails_version: 7.0 }`. Standard finds the plugin class from the gem's `default_lint_roller_plugin` metadata, or from `require_path` and `plugin_class_name` keys for a plugin that is not packaged as a gem. When neither yields a class, it stops with an error explaining both options. ### `extend_config:` `extend_config:` takes RuboCop configuration files, for example a `.custom_cops.yml` that says `require: ./lib/cops/no_sleep.rb` and `Custom/NoSleep: Enabled: true`. Standard wraps each file in a pseudo-plugin and merges it after the real plugins. Since Standard sets `DisabledByDefault: true`, a cop loaded this way runs only if its entry says `Enabled: true`. ## First-in-wins Standard combines its sources in a fixed order: 1. the built-in plugins: `standard-base` (RuboCop's built-in cops), `standard-custom` and `standard-performance`; 2. your `plugins:`, in the order listed; 3. your `extend_config:` files, in order. For each cop, the **first** source that configures it wins; a later source's entry for the same cop is dropped. Because the built-ins come first and configure every built-in RuboCop cop, nothing you add can change their settings. `AllCops` keys follow the same rule, arrays such as `Include` are unioned instead, and a list of keys is never taken from plugins at all: `Include`, `Exclude`, `TargetRubyVersion`, `DisabledByDefault`, `EnabledByDefault`, `StyleGuideBaseURL`, `StyleGuideCopsOnly`. The design goal is that each plugin you add becomes a stable standard of its own; two projects using the same plugins get the same rules, whatever order people would have preferred. ## What that means in practice - **Adding** a framework's cops (for example Rails-specific ones through standard-rails): supported. - **Adding** a project-specific cop through `extend_config`: supported. - **Turning off** or **retuning** a built-in cop, such as enforcing single quotes: not possible through either mechanism. The options are an `ignore` entry for specific files, a `standard:disable` comment, or leaving Standard for a RuboCop configuration. - **Running Standard's rules from the `rubocop` command** by inheriting its YAML: the README explicitly calls this unsupported and liable to break. ## A worked scenario A team on Standard wants three things: Rails-specific cops, a house cop that forbids `sleep` in specs, and single-quoted strings. 1. **Rails cops:** add `standard-rails` to the Gemfile and to `plugins:`. Supported. 2. **House cop:** write the cop in `lib/cops/`, reference it from a RuboCop YAML file with `require:` and `Enabled: true`, and list that file under `extend_config:`. Supported. 3. **Single quotes:** an entry for `Style/StringLiterals` in the extend file is silently dropped, because `standard-base` configured that cop first. The third wish is the signal. If a team keeps needing to change built-in rules, it wants RuboCop with its own `.rubocop.yml`, not Standard. ## Writing a plugin - Subclass `LintRoller::Plugin`, implement `about`, `supported?` and `rules`. - Set `spec.metadata["default_lint_roller_plugin"] = "MyRules::Plugin"` in the gemspec, and depend on `lint_roller`. - Return rules with `config_format: :rubocop` so both RuboCop and Standard can load them.
- Two plugins listed in .standard.yml configure the same cop differently. Which settings apply?The plugin listed first. Standard merges sources first-in-wins, dropping a later source's entry for a cop that an earlier source already configured, so the order under `plugins:` decides. Built-in plugins always come before yours.
- How does Standard find the plugin class for a gem listed under plugins:?It reads the gem's `default_lint_roller_plugin` gemspec metadata and instantiates that class, passing any options given in `.standard.yml`. For a plugin that is not a gem, you give `require_path` and `plugin_class_name` in the plugin's hash instead.
saying these in an interview costs you the question
- A plugin can loosen Standard's built-in rules if it is listed last.
- extend_config accepts only lint_roller plugin gems, not YAML files.
- Later entries in plugins: override earlier ones, as in RuboCop inheritance.
- A plugin can set AllCops TargetRubyVersion for the whole project.
- Cops added through extend_config run even without Enabled: true.