class RuboCop::ConfigLoader
This class represents the configuration of the RuboCop application and all its cops. A Config is associated with a YAML configuration file from which it was read. Several different Configs can be used during a run of the rubocop program, if files in several directories are inspected.
Constants
- DEFAULT_FILE
- DOTFILE
- PENDING_BANNER
- RUBOCOP_HOME
Attributes
Public Class Methods
# File lib/rubocop/config_loader.rb, line 138 def add_excludes_from_files(config, config_file) exclusion_file = find_last_file_upwards(DOTFILE, config_file, ConfigFinder.project_root) return unless exclusion_file return if PathUtil.relative_path(exclusion_file) == PathUtil.relative_path(config_file) print 'AllCops/Exclude ' if debug? config.add_excludes_from_higher_level(load_file(exclusion_file)) end
@api private Used to add features that were required inside a config or from the CLI using ‘–require`.
# File lib/rubocop/config_loader.rb, line 199 def add_loaded_features(loaded_features) @loaded_features.merge(Array(loaded_features)) end
# File lib/rubocop/config_loader.rb, line 81 def add_missing_namespaces(path, hash) # Using `hash.each_key` will cause the # `can't add a new key into hash during iteration` error hash_keys = hash.keys hash_keys.each do |key| q = Cop::Registry.qualified_cop_name(key, path) next if q == key hash[q] = hash.delete(key) end end
# File lib/rubocop/config_loader.rb, line 42 def clear_options @debug = nil @loaded_features = Set.new FileFinder.root_level = nil end
Returns the path of .rubocop.yml searching upwards in the directory structure starting at the given directory where the inspected file is. If no .rubocop.yml is found there, the user’s home directory is checked. If there’s no .rubocop.yml there either, the path to the default file is returned.
# File lib/rubocop/config_loader.rb, line 105 def configuration_file_for(target_dir) ConfigFinder.find_config_path(target_dir) end
# File lib/rubocop/config_loader.rb, line 109 def configuration_from_file(config_file, check: true) return default_configuration if config_file == DEFAULT_FILE config = load_file(config_file, check: check) config.validate_after_resolution if check if ignore_parent_exclusion? print 'Ignoring AllCops/Exclude from parent folders' if debug? else add_excludes_from_files(config, config_file) end merge_with_default(config, config_file).tap do |merged_config| unless possible_new_cops?(merged_config) pending_cops = pending_cops_only_qualified(merged_config.pending_cops) warn_on_pending_cops(pending_cops) unless pending_cops.empty? end end end
# File lib/rubocop/config_loader.rb, line 148 def default_configuration @default_configuration ||= begin print 'Default ' if debug? load_file(DEFAULT_FILE) end end
@api private
# File lib/rubocop/config_loader.rb, line 156 def inject_defaults!(project_root) path = File.join(project_root, 'config', 'default.yml') config = load_file(path) new_config = ConfigLoader.merge_with_default(config, path) puts "configuration from #{path}" if debug? @default_configuration = new_config end
# File lib/rubocop/config_loader.rb, line 48 def load_file(file, check: true) path = file_path(file) hash = load_yaml_configuration(path) loaded_features = resolver.resolve_requires(path, hash) add_loaded_features(loaded_features) resolver.override_department_setting_for_cops({}, hash) resolver.resolve_inheritance_from_gems(hash) resolver.resolve_inheritance(path, hash, file, debug?) hash.delete('inherit_from') # Adding missing namespaces only after resolving requires & inheritance, # since both can introduce new cops that need to be considered here. add_missing_namespaces(path, hash) Config.create(hash, path, check: check) end
# File lib/rubocop/config_loader.rb, line 68 def load_yaml_configuration(absolute_path) file_contents = read_file(absolute_path) yaml_code = Dir.chdir(File.dirname(absolute_path)) { ERB.new(file_contents).result } check_duplication(yaml_code, absolute_path) hash = yaml_safe_load(yaml_code, absolute_path) || {} puts "configuration from #{absolute_path}" if debug? raise(TypeError, "Malformed configuration in #{absolute_path}") unless hash.is_a?(Hash) hash end
Return a recursive merge of two hashes. That is, a normal hash merge, with the addition that any value that is a hash, and occurs in both arguments, will also be merged. And so on.
# File lib/rubocop/config_loader.rb, line 96 def merge(base_hash, derived_hash) resolver.merge(base_hash, derived_hash) end
Merges the given configuration with the default one.
# File lib/rubocop/config_loader.rb, line 192 def merge_with_default(config, config_file, unset_nil: true) resolver.merge_with_default(config, config_file, unset_nil: unset_nil) end
# File lib/rubocop/config_loader.rb, line 129 def pending_cops_only_qualified(pending_cops) pending_cops.select { |cop| Cop::Registry.qualified_cop?(cop.name) } end
# File lib/rubocop/config_loader.rb, line 133 def possible_new_cops?(config) disable_pending_cops || enable_pending_cops || config.disabled_new_cops? || config.enabled_new_cops? end
Returns the path RuboCop inferred as the root of the project. No file searches will go past this directory. @deprecated Use ‘RuboCop::ConfigFinder.project_root` instead.
# File lib/rubocop/config_loader.rb, line 167 def project_root warn Rainbow(<<~WARNING).yellow `RuboCop::ConfigLoader.project_root` is deprecated and will be removed in RuboCop 2.0. \ Use `RuboCop::ConfigFinder.project_root` instead. WARNING ConfigFinder.project_root end
# File lib/rubocop/config_loader.rb, line 176 def warn_on_pending_cops(pending_cops) warn Rainbow(PENDING_BANNER).yellow pending_cops.each { |cop| warn_pending_cop cop } warn Rainbow('For more information: https://docs.rubocop.org/rubocop/versioning.html').yellow end
# File lib/rubocop/config_loader.rb, line 184 def warn_pending_cop(cop) version = cop.metadata['VersionAdded'] || 'N/A' warn Rainbow("#{cop.name}: # new in #{version}").yellow warn Rainbow(' Enabled: true').yellow end
Private Class Methods
# File lib/rubocop/config_loader.rb, line 213 def check_duplication(yaml_code, absolute_path) smart_path = PathUtil.smart_path(absolute_path) YAMLDuplicationChecker.check(yaml_code, absolute_path) do |key1, key2| value = key1.value # .start_line is only available since ruby 2.5 / psych 3.0 message = if key1.respond_to? :start_line line1 = key1.start_line + 1 line2 = key2.start_line + 1 "#{smart_path}:#{line1}: " \ "`#{value}` is concealed by line #{line2}" else "#{smart_path}: `#{value}` is concealed by duplicate" end warn Rainbow(message).yellow end end
# File lib/rubocop/config_loader.rb, line 205 def file_path(file) File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file) end
Read the specified file, or exit with a friendly, concise message on stderr. Care is taken to use the standard OS exit code for a “file not found” error.
# File lib/rubocop/config_loader.rb, line 233 def read_file(absolute_path) File.read(absolute_path, encoding: Encoding::UTF_8) rescue Errno::ENOENT raise ConfigNotFoundError, "Configuration file not found: #{absolute_path}" end
# File lib/rubocop/config_loader.rb, line 209 def resolver @resolver ||= ConfigLoaderResolver.new end
# File lib/rubocop/config_loader.rb, line 239 def yaml_safe_load(yaml_code, filename) yaml_safe_load!(yaml_code, filename) rescue ::StandardError if defined?(::SafeYAML) raise 'SafeYAML is unmaintained, no longer needed and should be removed' end raise end
# File lib/rubocop/config_loader.rb, line 249 def yaml_safe_load!(yaml_code, filename) YAML.safe_load( yaml_code, permitted_classes: [Regexp, Symbol], aliases: true, filename: filename ) end