Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI

on:
push:
branches: [master]
pull_request:

jobs:
spec:
name: spec (${{ matrix.ruby }})
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental || false }}
strategy:
fail-fast: false
matrix:
ruby:
- "3.1"
- "3.4"
- "jruby-9.4"
- "jruby-10.0"
include:
# Bleeding-edge runtime: exercised, but allowed to fail so a
# not-yet-released or in-flight toolchain never blocks a PR.
- ruby: "4.0"
experimental: true
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run specs
run: bundle exec rake spec
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--require spec_helper
--format documentation
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require:
plugins:
- rubocop-rails
- rubocop-performance

Expand Down
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.1.1
3.4.8
31 changes: 27 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
[![Dependency Status](https://gemnasium.com/mxenabled/mad_rubocop.svg)](https://gemnasium.com/mxenabled/mad_rubocop)

# MadRubocop

Custom RuboCop yml files to modify the cops enforced by RuboCop.
[![CI](https://github.com/mxenabled/mad_rubocop/actions/workflows/ci.yml/badge.svg)](https://github.com/mxenabled/mad_rubocop/actions/workflows/ci.yml)

Custom RuboCop yml files to modify the cops enforced by RuboCop, shared across projects.

Built on RuboCop `~> 1.88`, rubocop-rails `~> 2.35`, and rubocop-performance `~> 1.26`. Requires Ruby `>= 3.1`.

## What's in the box

This gem ships configuration only — no Ruby code. Consumers inherit three files:

- `.rubocop.yml` — the entry point. Loads the `rubocop-rails` and `rubocop-performance` plugins, inherits the two files below, and sets `AllCops` defaults (`NewCops: disable`, so cops added in a RuboCop upgrade stay off until explicitly enabled here).
- `lib/disabled_cops.yml` — cops turned off, either as permanent house style or because the codebase isn't ready for them.
- `lib/modified_cops.yml` — cops kept on but configured to a non-default style (e.g. `hash_rockets`, `double_quotes`, outdented access modifiers).

## Installation

Expand All @@ -27,15 +37,28 @@ inherit_gem:
mad_rubocop: .rubocop.yml
```

NOTE: The `Exlude` setting on cops that only ignore certain files or directories cannot be set by MadRubocop.
NOTE: The `Exclude` setting on cops that only ignore certain files or directories cannot be set by MadRubocop.


## Development

After checking out the repo, run `bin/setup` to install dependencies.

Run the test suite with `bundle exec rake spec`. The specs validate the shipped configuration against the installed RuboCop — they confirm the config loads, the rubocop-rails and rubocop-performance plugins are present, and every referenced cop still exists (catching cops that a RuboCop upgrade renames or removes).

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).

### Upgrading RuboCop

Keeping the pinned RuboCop (and its plugins) current is the main maintenance task. The workflow:

1. Bump the version constraints for `rubocop`, `rubocop-rails`, and/or `rubocop-performance` in `mad_rubocop.gemspec`.
2. Run `bundle update rubocop rubocop-rails rubocop-performance`.
3. Run `bundle exec rake spec`. The suite fails if the config references a cop that the new RuboCop **renamed or removed** — the failure message names the offending cop.
4. For each failure, update the cop name in `lib/disabled_cops.yml` / `lib/modified_cops.yml` (or drop it if the cop no longer exists).
5. Because `NewCops` is disabled, cops **added** in the upgrade are off by default. Review `rubocop --show-cops` (or the release notes) and add any you want to disable/configure.
6. Bump `MadRubocop::VERSION` in `version.rb`, note the change, and release.

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/mxenabled/mad_rubocop.
Expand Down
5 changes: 5 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
require "bundler/gem_tasks"
require "rspec/core/rake_task"

RSpec::Core::RakeTask.new(:spec)

task :default => :spec
2 changes: 1 addition & 1 deletion lib/disabled_cops.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Naming/HeredocDelimiterCase:
Enabled: false
Naming/MemoizedInstanceVariableName:
Enabled: false
Naming/PredicateName:
Naming/PredicatePrefix:
Enabled: false
Naming/MethodParameterName:
Enabled: false
Expand Down
2 changes: 1 addition & 1 deletion lib/mad_rubocop/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module MadRubocop
VERSION = "4.0.0"
VERSION = "4.2.0.pre1"
end
9 changes: 6 additions & 3 deletions mad_rubocop.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

spec.add_dependency "rubocop", "~> 1.32.0"
spec.add_dependency "rubocop-performance", "~> 1.14.3"
spec.add_dependency "rubocop-rails", "~> 2.15.2"
spec.required_ruby_version = ">= 3.1"

spec.add_dependency "rubocop", "~> 1.88.0"
spec.add_dependency "rubocop-performance", "~> 1.26.1"
spec.add_dependency "rubocop-rails", "~> 2.35.5"
spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec", "~> 3.0"
end
7 changes: 7 additions & 0 deletions spec/fixtures/sample.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# A tiny fixture used to exercise the gem's RuboCop configuration end-to-end.
# It exists only so the CLI has something to inspect; offenses are irrelevant.
module Sample
def self.greeting
"hello"
end
end
76 changes: 76 additions & 0 deletions spec/mad_rubocop/config_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
require "spec_helper"
require "yaml"
require "stringio"
require "rubocop"

# These specs treat the gem's YAML as the unit under test. Because mad_rubocop
# ships nothing but configuration, the most valuable guarantees are that the
# config loads under the pinned RuboCop, that every cop it references still
# exists, and that RuboCop raises no obsolete/removed-cop warnings when it runs.
# A green suite here is what catches upgrades that rename a cop (e.g. the
# Naming/PredicateName -> Naming/PredicatePrefix rename in RuboCop 1.88).
RSpec.describe "MadRubocop configuration" do
ROOT = File.expand_path("../..", __dir__)
CONFIG_FILE = File.join(ROOT, ".rubocop.yml")
COP_CONFIG_FILES = {
"disabled_cops.yml" => File.join(ROOT, "lib", "disabled_cops.yml"),
"modified_cops.yml" => File.join(ROOT, "lib", "modified_cops.yml"),
}.freeze

# Loading the config also loads the rubocop-rails / rubocop-performance
# plugins declared under `plugins:`, which registers their cops globally.
before(:all) do
RuboCop::ConfigLoader.load_file(CONFIG_FILE)
end

def self.cop_names_in(file)
YAML.safe_load(File.read(file)).keys.select { |key| key.include?("/") }
end

it "loads the top-level config without raising" do
expect { RuboCop::ConfigLoader.load_file(CONFIG_FILE) }.not_to raise_error
end

it "loads the rubocop-rails and rubocop-performance plugin cops" do
departments = RuboCop::Cop::Registry.global.departments.map(&:to_s)

expect(departments).to include("Rails", "Performance")
end

describe "every configured cop is recognized by the installed RuboCop" do
COP_CONFIG_FILES.each do |label, file|
context label do
cop_names_in(file).each do |cop_name|
it "recognizes #{cop_name}" do
cop = RuboCop::Cop::Registry.global.find_by_cop_name(cop_name)

expect(cop).not_to(
be_nil,
"#{cop_name} is not a known cop — it may have been renamed, " \
"removed, or moved to a plugin that is no longer loaded",
)
end
end
end
end
end

it "runs against a fixture without emitting obsolete/removed-cop warnings" do
fixture = File.join(ROOT, "spec", "fixtures", "sample.rb")

captured = StringIO.new
original_stderr = $stderr
$stderr = captured
begin
RuboCop::CLI.new.run(
["--config", CONFIG_FILE, "--force-exclusion", "--no-color", fixture],
)
ensure
$stderr = original_stderr
end

expect(captured.string).not_to match(
/obsolete|has been (removed|renamed|extracted)|unrecognized cop/i,
)
end
end
14 changes: 14 additions & 0 deletions spec/mad_rubocop/version_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require "spec_helper"

RSpec.describe MadRubocop do
it "has a semantic version number" do
expect(MadRubocop::VERSION).to match(/\A\d+\.\d+\.\d+\z/)
end

it "matches the version declared in the gemspec" do
gemspec_path = File.expand_path("../../mad_rubocop.gemspec", __dir__)
gemspec = Gem::Specification.load(gemspec_path)

expect(gemspec.version.to_s).to eq(MadRubocop::VERSION)
end
end
15 changes: 15 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require "mad_rubocop"

RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end

config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end

config.disable_monkey_patching!
config.order = :random
Kernel.srand config.seed
end