Skip to content

Commit

Permalink
[Fix #9122] Add suggestion if any gems are loaded that have rubocop e…
Browse files Browse the repository at this point in the history
…xtensions (#9130)

The suggestion can be disabled by setting `AllCops/SuggestExtensions: false`
  • Loading branch information
dvandersluis committed Dec 1, 2020
1 parent a9772af commit d29357c
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 2 deletions.
1 change: 1 addition & 0 deletions .rubocop.yml
Expand Up @@ -15,6 +15,7 @@ AllCops:
- '.git/**/*'
- 'bin/*'
TargetRubyVersion: 2.4
SuggestExtensions: false

Naming/PredicateName:
# Method define macros for dynamically generated method.
Expand Down
1 change: 1 addition & 0 deletions changelog/change_added_notification_if_any_gems_are.md
@@ -0,0 +1 @@
* [#9122](https://github.com/rubocop-hq/rubocop/issues/9122): Added tip message if any gems are loaded that have RuboCop extensions. ([@dvandersluis][])
10 changes: 10 additions & 0 deletions config/default.yml
Expand Up @@ -139,6 +139,16 @@ AllCops:
# from the lock file.) If the Ruby version is still unresolved, RuboCop will
# use the oldest officially supported Ruby version (currently Ruby 2.4).
TargetRubyVersion: ~
# Determines if a notification for extension libraries should be shown when
# rubocop is run. Keys are the name of the extension, and values are an array
# of gems in the Gemfile that the extension is suggested for, if not already
# included.
SuggestExtensions:
rubocop-rails: [rails]
rubocop-rspec: [rspec, rspec-rails]
rubocop-minitest: [minitest]
rubocop-sequel: [sequel]
rubocop-rake: [rake]

#################### Bundler ###############################

Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -649,6 +649,7 @@
require_relative 'rubocop/cli/command/execute_runner'
require_relative 'rubocop/cli/command/init_dotfile'
require_relative 'rubocop/cli/command/show_cops'
require_relative 'rubocop/cli/command/suggest_extensions'
require_relative 'rubocop/cli/command/version'
require_relative 'rubocop/config_regeneration'
require_relative 'rubocop/options'
Expand Down
6 changes: 5 additions & 1 deletion lib/rubocop/cli.rb
Expand Up @@ -69,10 +69,14 @@ def execute_runners
if @options[:auto_gen_config]
run_command(:auto_gen_config)
else
run_command(:execute_runner)
run_command(:execute_runner).tap { suggest_extensions }
end
end

def suggest_extensions
run_command(:suggest_extensions)
end

def validate_options_vs_config
if @options[:parallel] &&
!@config_store.for_pwd.for_all_cops['UseCache']
Expand Down
67 changes: 67 additions & 0 deletions lib/rubocop/cli/command/suggest_extensions.rb
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module RuboCop
class CLI
module Command
# Run all the selected cops and report the result.
# @api private
class SuggestExtensions < Base
# Combination of short and long formatter names.
INCLUDED_FORMATTERS = %w[p progress fu fuubar pa pacman].freeze

self.command_name = :suggest_extensions

def run
return if skip? || extensions.none?

puts
puts 'Tip: Based on detected gems, the following '\
'RuboCop extension libraries might be helpful:'

extensions.each do |extension|
puts " * #{extension} (http://github.com/rubocop-hq/#{extension})"
end

puts
puts 'You can opt out of this message by adding the following to your config:'
puts ' AllCops:'
puts ' SuggestExtensions: false'
puts if @options[:display_time]
end

private

def skip?
# Disable outputting the notification:
# 1. On CI
# 2. When given RuboCop options that it doesn't make sense for
# 3. For all formatters except specified in `INCLUDED_FORMATTERS'`
ENV['CI'] ||
@options[:only] || @options[:debug] || @options[:list_target_files] || @options[:out] ||
!INCLUDED_FORMATTERS.include?(current_formatter)
end

def current_formatter
@options[:format] || @config_store.for_pwd.for_all_cops['DefaultFormatter'] || 'p'
end

def extensions
extensions = @config_store.for_pwd.for_all_cops['SuggestExtensions']
return [] unless extensions

extensions.select { |_, v| (v & dependent_gems).any? }.keys - dependent_gems
end

def dependent_gems
# This only includes gems in Gemfile, not in lockfile
Bundler.load.dependencies.map(&:name)
end

def puts(*args)
output = (@options[:stderr] ? $stderr : $stdout)
output.puts(*args)
end
end
end
end
end
6 changes: 5 additions & 1 deletion lib/rubocop/config_loader_resolver.rb
Expand Up @@ -92,7 +92,7 @@ def merge(base_hash, derived_hash, **opts)
keys_appearing_in_both.each do |key|
if opts[:unset_nil] && derived_hash[key].nil?
result.delete(key)
elsif base_hash[key].is_a?(Hash)
elsif merge_hashes?(base_hash, derived_hash, key)
result[key] = merge(base_hash[key], derived_hash[key], **opts)
elsif should_union?(base_hash, key, opts[:inherit_mode])
result[key] = base_hash[key] | derived_hash[key]
Expand Down Expand Up @@ -164,6 +164,10 @@ def should_union?(base_hash, key, inherit_mode)
inherit_mode['merge'].include?(key)
end

def merge_hashes?(base_hash, derived_hash, key)
base_hash[key].is_a?(Hash) && derived_hash[key].is_a?(Hash)
end

def base_configs(path, inherit_from, file)
configs = Array(inherit_from).compact.map do |f|
ConfigLoader.load_file(inherited_file(path, f, file))
Expand Down
1 change: 1 addition & 0 deletions spec/rubocop/cli/cli_autocorrect_spec.rb
Expand Up @@ -7,6 +7,7 @@

before do
RuboCop::ConfigLoader.default_configuration = nil
RuboCop::ConfigLoader.default_configuration.for_all_cops['SuggestExtensions'] = false
end

it 'does not correct ExtraSpacing in a hash that would be changed back' do
Expand Down
4 changes: 4 additions & 0 deletions spec/rubocop/cli/cli_options_spec.rb
Expand Up @@ -766,6 +766,10 @@ class SomeCop < Cop
end

describe '-d/--debug' do
before do
RuboCop::ConfigLoader.default_configuration = nil
end

it 'shows config files' do
create_file('example1.rb', "\tputs 0")
expect(cli.run(['--debug', 'example1.rb'])).to eq(1)
Expand Down

0 comments on commit d29357c

Please sign in to comment.