In a .rubocop.yml, why can adding one AllCops Exclude pattern make RuboCop inspect vendor/, and how does inherit_mode prevent it?
answer
- hashes merge, arrays replace
- default Exclude includes vendor/**/*
- inherit_mode merge: [Exclude]
- per-cop inherit_mode override
- Preview merges Exclude since 1.91
basics
~10 sArray settings such as Exclude replace the inherited array, so a one-entry AllCops Exclude drops the default vendor/**/* pattern. inherit_mode: merge: [Exclude] unions the lists instead, globally or for one cop.
solid answer
~40 sRuboCop merges inherited configuration by type: **hashes** merge key by key, **scalars** and **arrays** are replaced. `AllCops: Exclude` is an array whose default is `node_modules/**/*`, `tmp/**/*`, `vendor/**/*` and `.git/**/*`, so a project that writes `Exclude: [db/schema.rb]` has replaced that list, and a bundle installed under `vendor/bundle` is suddenly inspected. The fix is `inherit_mode: { merge: [Exclude] }` at the top of the file, which unions the child's list with the parent's and applies to the implicit defaults as well as to `inherit_from` files. A cop section can carry its own `inherit_mode` with `override` or `merge` to reverse the top-level choice for that cop. In RuboCop 1.91, opting into `Preview` merges `Exclude` by default, and an explicit `inherit_mode` still wins.
code
yaml · 15 lines# .rubocop.yml
inherit_mode:
merge:
- Exclude
AllCops:
Exclude:
- "db/schema.rb" # added to node_modules, tmp, vendor, .git
Style/Documentation:
inherit_mode:
override:
- Exclude
Exclude:
- "app/legacy/**/*" # replaces spec/**/* and test/**/*go deeper
Recall that writing your own Exclude list replaces the default one, and that the default already skips vendor/, tmp/, node_modules/ and .git/.
Explain the type rule: hashes merge, scalars and arrays replace, and inherit_mode merge or override changes it for named array parameters, globally or per cop.
Diagnose the symptom from a slow or noisy CI run: rubocop -L shows vendor/ files, --show-cops shows the effective list, and inherit_mode fixes it without copying defaults.
Decide whether a shared base config should declare inherit_mode merge for Exclude, trading safer additive exclusions for less local control over what gets skipped.
## How inherited settings are merged Every RuboCop configuration is layered: the gem's own `config/default.yml` at the bottom, then any files named by `inherit_gem` or `inherit_from`, then the keys in the project's `.rubocop.yml`. When a child layer and a parent layer set the same key, RuboCop decides by **value type**: | Value type | Examples | Default behaviour | |---|---|---| | Hash | a cop's section, `PreferredMethods` | merged key by key | | Scalar | `Enabled`, `Max`, `EnforcedStyle` | child replaces parent | | Array | `Exclude`, `Include`, `AllowedMethods` | child replaces parent | Arrays are replaced on purpose: if they always merged, a child file could never remove an entry it inherited. For hashes the tool is different - setting a hash key to `~` (YAML `nil`) in the child cancels that key from the parent, as in `PreferredMethods: { collect: ~ }`. ## The trap `AllCops: Exclude` ships with four patterns: `node_modules/**/*`, `tmp/**/*`, `vendor/**/*` and `.git/**/*`. A developer adds one line to keep the generated schema out of the run: ```yaml AllCops: Exclude: - "db/schema.rb" ``` That list **replaces** the default one. The next CI job that installs gems into `vendor/bundle` now asks RuboCop to inspect thousands of gem files, and the run slows down and fails on code the team does not own. The same thing happens at cop level: `Style/Documentation` excludes `spec/**/*` and `test/**/*` by default, so writing `Style/Documentation: Exclude: [app/legacy/**/*]` quietly starts checking classes defined in spec and test files for documentation comments. `Metrics/BlockLength` ships `AllowedMethods: [refine]`, and a list that names only `describe` drops that entry. Two fixes exist: 1. **Repeat the defaults** you still need in your own list. 2. **Declare `inherit_mode`** so the arrays are merged. ## `inherit_mode` `inherit_mode` is a top-level key with two sub-keys, each a list of **parameter names**: - `merge:` - for these parameters, union the child's array with the inherited one; - `override:` - for these parameters, replace (the default, stated explicitly). It applies to **explicit** inheritance through `inherit_from` and `inherit_gem`, and to the **implicit** inheritance from `config/default.yml`. It can also be written inside a single cop's section, where it beats the top-level setting for that cop: - top-level `inherit_mode: { merge: [Exclude] }` makes every `Exclude` additive; - `Style/Documentation: { inherit_mode: { override: [Exclude] } }` makes that one cop's list replace again. The merge is a set union, so repeating a default pattern is harmless. `Include` works the same way: to add an unusual extension to `AllCops: Include` without copying the long default list, declare `merge: [Include]`. ## Which parameters to merge `inherit_mode` names parameters, not cops, so one declaration covers every cop that has that parameter: - **`Exclude`** is the usual candidate: adding an exclusion should rarely mean losing the inherited ones; - **`Include`** is worth merging when you add a file type, because the default `AllCops: Include` list is long and easy to truncate by accident; - **`AllowedMethods`** and **`AllowedPatterns`** can go either way - merging keeps defaults such as `refine` for `Metrics/BlockLength`, replacing lets you tighten a cop on purpose. When a shared base config declares `inherit_mode`, it is inherited like any other key, so every project built on it gets the same merge rules unless it states its own. ## What changed in RuboCop 1.91 RuboCop 1.91 added **Preview**, an opt-in channel (`AllCops: Preview: true` or `--preview`) for defaults expected in the next major release. Under Preview, `Exclude` is merged with the default configuration and with inherited files even without `inherit_mode`. An explicit `inherit_mode` still wins in either direction, so a project that lists `Exclude` under `override:` keeps replacing. Without Preview, 1.91 still replaces, as described above. ## Checking the result - `rubocop -L` lists every file the run will inspect; `vendor/` appearing there is the symptom. - `rubocop --show-cops Style/Documentation` prints the merged `Exclude` list for that cop. - With `--debug`, RuboCop prints a line such as `Style/For:Exclude overrides the same parameter in .rubocop_2.yml` when a child replaces an inherited value, and prints none for parameters listed under `inherit_mode: merge`. ## Path relativity Patterns in `.rubocop.yml`, or any file whose name starts with `.rubocop`, are relative to that file's directory; in other files they are relative to the directory where `rubocop` runs. Merging does not change that, so a pattern inherited from a sub-project's file keeps pointing where its author meant.
- How do you remove one entry from an inherited hash option such as PreferredMethods?Hash options merge key by key, so you cannot drop an entry by leaving it out. Set that key to `~`, YAML's `nil`, in the child: `Style/CollectionMethods: PreferredMethods: { collect: ~ }` keeps the other inherited preferences and cancels the one for `collect`.
- A cop section sets inherit_mode override for Exclude while the top level says merge. Which applies to that cop?The cop's own `inherit_mode` wins for that cop, so its `Exclude` list replaces the inherited one. Every other cop still follows the top-level `merge`. That lets a team make exclusions additive by default and keep one cop's list fully under local control.
saying these in an interview costs you the question
- RuboCop merges every inherited array, so a child's Exclude always adds to the parent's.
- The vendor/ directory is skipped by RuboCop whatever Exclude says.
- inherit_mode only affects files named in inherit_from, not config/default.yml.
- A cop-level inherit_mode cannot override the top-level inherit_mode.
- Leaving a key out of a child's hash option removes it from the inherited hash.