class RuboCop::ConfigLoader
directories are inspected.
during a run of the rubocop program, if files in several
file from which it was read. Several different Configs can be used
and all its cops. A Config is associated with a YAML configuration
This class represents the configuration of the RuboCop application
def add_excludes_from_files(config, config_file)
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
def add_loaded_features(loaded_features)
- Api: - private
def add_loaded_features(loaded_features) @loaded_features.merge(Array(loaded_features)) end
def add_missing_namespaces(path, hash)
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
def check_duplication(yaml_code, absolute_path)
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
def clear_options
def clear_options @debug = nil @loaded_features = Set.new FileFinder.root_level = nil end
def configuration_file_for(target_dir)
user's home directory is checked. If there's no .rubocop.yml
inspected file is. If no .rubocop.yml is found there, the
directory structure starting at the given directory where the
Returns the path of .rubocop.yml searching upwards in the
def configuration_file_for(target_dir) ConfigFinder.find_config_path(target_dir) end
def configuration_from_file(config_file, check: true)
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
def default_configuration
def default_configuration @default_configuration ||= begin print 'Default ' if debug? load_file(DEFAULT_FILE) end end
def file_path(file)
def file_path(file) File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file) end
def inject_defaults!(project_root)
- Api: - private
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
def load_file(file, check: true)
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
def load_yaml_configuration(absolute_path)
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
def merge(base_hash, derived_hash)
with the addition that any value that is a hash, and occurs in both
Return a recursive merge of two hashes. That is, a normal hash merge,
def merge(base_hash, derived_hash) resolver.merge(base_hash, derived_hash) end
def merge_with_default(config, config_file, unset_nil: true)
def merge_with_default(config, config_file, unset_nil: true) resolver.merge_with_default(config, config_file, unset_nil: unset_nil) end
def pending_cops_only_qualified(pending_cops)
def pending_cops_only_qualified(pending_cops) pending_cops.select { |cop| Cop::Registry.qualified_cop?(cop.name) } end
def possible_new_cops?(config)
def possible_new_cops?(config) disable_pending_cops || enable_pending_cops || config.disabled_new_cops? || config.enabled_new_cops? end
def project_root
- Use `RuboCop::ConfigFinder.project_root` instead.
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
def read_file(absolute_path)
stderr. Care is taken to use the standard OS exit code for a "file not
Read the specified file, or exit with a friendly, concise message on
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
def resolver
def resolver @resolver ||= ConfigLoaderResolver.new end
def warn_on_pending_cops(pending_cops)
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
def warn_pending_cop(cop)
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
def yaml_safe_load(yaml_code, filename)
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
def yaml_safe_load!(yaml_code, filename)
def yaml_safe_load!(yaml_code, filename) YAML.safe_load( yaml_code, permitted_classes: [Regexp, Symbol], aliases: true, filename: filename ) end