Development

This page describes considerations when developing RSpec-specific cops. It is intended to be a complement to the general RuboCop development documentation.

Create a new cop

Clone the repository and run bundle install if not done yet. The following rake task can only be run inside the rubocop project directory itself.

Use the bundled rake task new_cop to generate a cop template:

$ bundle exec rake 'new_cop[RSpec/CopName]'
[create] lib/rubocop/cop/rspec/cop_name.rb
[create] spec/rubocop/cop/rspec/cop_name_spec.rb
[modify] lib/rubocop/cop/rspec_cops.rb - `require_relative 'rspec/cop_name'` was injected.
[modify] A configuration for the cop is added into config/default.yml.
Do 4 steps:
  1. Modify the description of RSpec/CopName in config/default.yml
  2. Implement your new cop in the generated file!
  3. Add an entry about new cop to CHANGELOG.md
  4. Commit your new cop with a message such as
     e.g. "Add new `#{badge}` cop"

Choose a Name

Use the following rules to give the new cop a name:

  • Pick a department. See the list of existing departments

  • The name is self-explanatory

  • The name explains the offense the cop detects, e.g. ExtraSpacing

  • The name starts with a noun instead of a verb, e.g. ArrayAlignment instead of AlignArray

  • The name is easy to understand, e.g. IndentationStyle instead of just Tab

  • The name is specific, e.g. DuplicateHashKey instead of just DuplicateKey

  • The name is neutral when possible and accommodates multiple styles when feasible, e.g. EmptyLineBeforeBegin.

  • The name uses commonly-used terms, e.g. RedundantPercentI instead of RedundantPercentSymbolArray

  • The name uses correct terms, e.g. arguments in a method call, and parameters in a method signature

  • Lines with no symbols are called "empty", not "blank", e.g. LeadingEmptyLines instead of LeadingBlankLines

  • Prefer "redundant" to "unneeded", e.g. RedundantSelf instead of UnneededSelf

See the "renamed" section of config/obsoletion.yml for good and bad examples (old name is on the left, new name on the right).

Base class

The RuboCop::Cop::RSpec::Base class includes convenient node pattern DSL matchers that will automatically account for any custom RSpec DSL configuration.

For example, if the project defines let_it_be as a Helper, then all cops will find let_it_be when using the let? matcher.

Writing specs

When working on RSpec-specific cops, ensure that the default language config is loaded for all RSpec specs. For example:

require 'rubocop/rspec/shared_contexts/default_rspec_language_config_context'

RSpec.config do |config|
  # Set metadata on all cop specs
  config.define_derived_metadata(file_path: %r{/spec/rubocop/cop/}) do |meta|
    meta[:type] = :cop_spec
  end

  # Include RuboCop's config shared context for all cop specs
  config.define_derived_metadata(type: :cop_spec) do |meta|
    meta[:config] = true
  end

  config.include_context 'with default RSpec/Language config', :config
end