# encoding: utf-8require'yaml'require'pathname'moduleRuboCop# 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.classConfigLoaderDOTFILE='.rubocop.yml'RUBOCOP_HOME=File.realpath(File.join(File.dirname(__FILE__),'..','..'))DEFAULT_FILE=File.join(RUBOCOP_HOME,'config','default.yml')AUTO_GENERATED_FILE='.rubocop_todo.yml'class<<selfattr_accessor:debug,:auto_gen_config,:exclude_limitattr_writer:root_level# The upwards search is stopped at this level.alias_method:debug?,:debugalias_method:auto_gen_config?,:auto_gen_configdefload_file(path)path=File.absolute_path(path)hash=load_yaml_configuration(path)resolve_inheritance(path,hash)Array(hash.delete('require')).each{|r|require(r)}hash.delete('inherit_from')config=Config.new(hash,path)config.deprecation_checkdo|deprecation_message|warn("#{path} - #{deprecation_message}")endconfig.add_missing_namespacesconfig.warn_unless_validconfig.make_excludes_absoluteconfigend# 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.defmerge(base_hash,derived_hash)result=base_hash.merge(derived_hash)keys_appearing_in_both=base_hash.keys&derived_hash.keyskeys_appearing_in_both.eachdo|key|nextunlessbase_hash[key].is_a?(Hash)result[key]=merge(base_hash[key],derived_hash[key])endresultenddefbase_configs(path,inherit_from)configs=Array(inherit_from).mapdo|f|f=File.expand_path(f,File.dirname(path))ifauto_gen_config?nextiff.include?(AUTO_GENERATED_FILE)old_auto_config_file_warningiff.include?('rubocop-todo.yml')endprint'Inheriting 'ifdebug?load_file(f)endconfigs.compactend# 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.defconfiguration_file_for(target_dir)config_files_in_path(target_dir).first||DEFAULT_FILEenddefconfiguration_from_file(config_file)config=load_file(config_file)returnconfigifconfig_file==DEFAULT_FILEfound_files=config_files_in_path(config_file)iffound_files.any?&&found_files.last!=config_fileprint'AllCops/Exclude 'ifdebug?config.add_excludes_from_higher_level(load_file(found_files.last))endmerge_with_default(config,config_file)enddefdefault_configuration@default_configuration||=beginprint'Default 'ifdebug?load_file(DEFAULT_FILE)endend# Merges the given configuration with the default one. If# AllCops:DisabledByDefault is true, it changes the Enabled params so# that only cops from user configuration are enabled.defmerge_with_default(config,config_file)configs=ifconfig.key?('AllCops')&&config['AllCops']['DisabledByDefault']disabled_default=transform(default_configuration)do|params|params.merge('Enabled'=>false)# Overwrite with false.endenabled_user_config=transform(config)do|params|{'Enabled'=>true}.merge(params)# Set true if not set.end[disabled_default,enabled_user_config]else[default_configuration,config]endConfig.new(merge(configs.first,configs.last),config_file)endprivate# Returns a new hash where the parameters of the given config hash have# been replaced by parmeters returned by the given block.deftransform(config)Hash[config.map{|cop,params|[cop,yield(params)]}]enddefload_yaml_configuration(absolute_path)yaml_code=IO.read(absolute_path)# At one time, there was a problem with the psych YAML engine under# Ruby 1.9.3. YAML.load_file would crash when reading empty .yml files# or files that only contained comments and blank lines. This problem# is not possible to reproduce now, but we want to avoid it in case# it's still there. So we only load the YAML code if we find some real# code in there.hash=yaml_code=~/^[A-Z]/i?yaml_safe_load(yaml_code):{}puts"configuration from #{absolute_path}"ifdebug?unlesshash.is_a?(Hash)fail(TypeError,"Malformed configuration in #{absolute_path}")endhashenddefyaml_safe_load(yaml_code)ifYAML.respond_to?(:safe_load)# Ruby 2.1+ifdefined?(SafeYAML)YAML.safe_load(yaml_code,nil,whitelisted_tags: %w(!ruby/regexp))elseYAML.safe_load(yaml_code,[Regexp])endelseYAML.load(yaml_code)endenddefresolve_inheritance(path,hash)base_configs(path,hash['inherit_from']).reverse_eachdo|base_config|base_config.eachdo|k,v|hash[k]=hash.key?(k)?merge(v,hash[k]):vifv.is_a?(Hash)endendenddefconfig_files_in_path(target)possible_config_files=dirs_to_search(target).mapdo|dir|File.join(dir,DOTFILE)endpossible_config_files.select{|config_file|File.exist?(config_file)}enddefdirs_to_search(target_dir)dirs_to_search=[]Pathname.new(File.expand_path(target_dir)).ascenddo|dir_pathname|breakifdir_pathname.to_s==@root_leveldirs_to_search<<dir_pathname.to_senddirs_to_search<<Dir.homeenddefold_auto_config_file_warningwarnRainbow('Attention: rubocop-todo.yml has been renamed to '\"#{AUTO_GENERATED_FILE}").redexit(1)endendendend